mirror of
https://github.com/daffyyyy/CS2-SimpleAdmin.git
synced 2026-11-07 12:07:05 +01:00
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:
parent
03a454b1b5
commit
460d8b2b85
112 changed files with 5559 additions and 36371 deletions
BIN
CS2-SimpleAdmin/3rd_party/MenuManagerApi.dll
vendored
BIN
CS2-SimpleAdmin/3rd_party/MenuManagerApi.dll
vendored
Binary file not shown.
BIN
CS2-SimpleAdmin/3rd_party/PanoramaUiKit.Shared.dll
vendored
Normal file
BIN
CS2-SimpleAdmin/3rd_party/PanoramaUiKit.Shared.dll
vendored
Normal file
Binary file not shown.
|
|
@ -18,9 +18,12 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
|
||||
public PlayerInfo GetPlayerInfo(CCSPlayerController player)
|
||||
{
|
||||
return !player.UserId.HasValue
|
||||
? throw new KeyNotFoundException("Player with specific UserId not found")
|
||||
: CS2_SimpleAdmin.PlayersInfo[player.SteamID];
|
||||
if (!player.UserId.HasValue)
|
||||
{
|
||||
throw new KeyNotFoundException("Player with specific UserId not found");
|
||||
}
|
||||
|
||||
return CS2_SimpleAdmin.PlayersInfo[player.SteamID];
|
||||
}
|
||||
|
||||
public string GetConnectionString() => CS2_SimpleAdmin.Instance.DbConnectionString;
|
||||
|
|
@ -57,35 +60,35 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
switch (penaltyType)
|
||||
{
|
||||
case PenaltyType.Ban:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Ban(admin, player, duration, reason);
|
||||
break;
|
||||
}
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Ban(admin, player, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Kick:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Kick(admin, player, reason);
|
||||
break;
|
||||
}
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Kick(admin, player, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Gag:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Gag(admin, player, duration, reason);
|
||||
break;
|
||||
}
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Gag(admin, player, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Mute:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Mute(admin, player, duration, reason);
|
||||
break;
|
||||
}
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Mute(admin, player, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Silence:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Silence(admin, player, duration, reason);
|
||||
break;
|
||||
}
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Silence(admin, player, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Warn:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Warn(admin, player, duration, reason);
|
||||
break;
|
||||
}
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Warn(admin, player, duration, reason);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(penaltyType), penaltyType, null);
|
||||
}
|
||||
|
|
@ -97,30 +100,30 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
switch (penaltyType)
|
||||
{
|
||||
case PenaltyType.Ban:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddBan(admin, steamid, duration, reason);
|
||||
break;
|
||||
}
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddBan(admin, steamid, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Gag:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddGag(admin, steamid, duration, reason);
|
||||
break;
|
||||
}
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddGag(admin, steamid, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Mute:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddMute(admin, steamid, duration, reason);
|
||||
break;
|
||||
}
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddMute(admin, steamid, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Silence:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddSilence(admin, steamid, duration, reason);
|
||||
break;
|
||||
}
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddSilence(admin, steamid, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Warn:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddWarn(admin, steamid, duration, reason);
|
||||
break;
|
||||
}
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddWarn(admin, steamid, duration, reason);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(penaltyType), penaltyType, null);
|
||||
}
|
||||
|
|
@ -149,27 +152,31 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
public void RegisterCommand(string name, string? description, CommandInfo.CommandCallback callback)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new ArgumentException("Command name cannot be null or empty.", nameof(name));
|
||||
}
|
||||
|
||||
ArgumentNullException.ThrowIfNull(callback);
|
||||
|
||||
var definition = new CommandDefinition(name, description ?? "No description", callback);
|
||||
if (!RegisterCommands._commandDefinitions.TryGetValue(name, out var list))
|
||||
CommandDefinition definition = new CommandDefinition(name, description ?? "No description", callback);
|
||||
if (!RegisterCommands._commandDefinitions.TryGetValue(name, out IList<CommandDefinition>? list))
|
||||
{
|
||||
list = new List<CommandDefinition>();
|
||||
RegisterCommands._commandDefinitions[name] = list;
|
||||
}
|
||||
|
||||
|
||||
list.Add(definition);
|
||||
}
|
||||
|
||||
public void UnRegisterCommand(string commandName)
|
||||
{
|
||||
var definitions = RegisterCommands._commandDefinitions[commandName];
|
||||
IList<CommandDefinition> definitions = RegisterCommands._commandDefinitions[commandName];
|
||||
if (definitions.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var definition in definitions)
|
||||
foreach (CommandDefinition definition in definitions)
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.RemoveCommand(commandName, definition.Callback);
|
||||
}
|
||||
|
|
@ -196,7 +203,9 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
bool dontPublish = false, params object[] messageArgs)
|
||||
{
|
||||
if (moduleLocalizer is not IStringLocalizer localizer)
|
||||
{
|
||||
throw new InvalidOperationException("moduleLocalizer must be an IStringLocalizer instance");
|
||||
}
|
||||
|
||||
Helper.ShowAdminActivityLocalized(localizer, messageKey, callerName, dontPublish, messageArgs);
|
||||
}
|
||||
|
|
@ -209,7 +218,9 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
public void RegisterMenuCategory(string categoryId, string categoryNameKey, string permission, object moduleLocalizer)
|
||||
{
|
||||
if (moduleLocalizer is not IStringLocalizer localizer)
|
||||
{
|
||||
throw new InvalidOperationException("moduleLocalizer must be an IStringLocalizer instance");
|
||||
}
|
||||
|
||||
Menus.MenuManager.Instance.RegisterCategory(categoryId, categoryNameKey, permission, localizer);
|
||||
}
|
||||
|
|
@ -223,22 +234,11 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
MenuBuilder BuilderFactory(CCSPlayerController player)
|
||||
{
|
||||
if (menuFactory(player) is not MenuBuilder menuBuilder)
|
||||
throw new InvalidOperationException("Menu factory must return MenuBuilder");
|
||||
|
||||
// Dodaj automatyczną obsługę przycisku 'Wróć'
|
||||
menuBuilder.WithBackAction(p =>
|
||||
{
|
||||
if (Menus.MenuManager.Instance.GetMenuCategories().TryGetValue(categoryId, out var category))
|
||||
{
|
||||
Menus.MenuManager.Instance.CreateCategoryMenuPublic(category, p).OpenMenu(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
Menus.MenuManager.Instance.OpenMainMenu(p);
|
||||
}
|
||||
});
|
||||
throw new InvalidOperationException("Menu factory must return MenuBuilder");
|
||||
}
|
||||
|
||||
return menuBuilder;
|
||||
return menuBuilder.WithBackButton();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -250,25 +250,14 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
|
||||
MenuBuilder BuilderFactory(CCSPlayerController player)
|
||||
{
|
||||
var context = new MenuContext(categoryId, menuId, menuName, permission, commandName);
|
||||
MenuContext context = new MenuContext(categoryId, menuId, menuName, permission, commandName);
|
||||
|
||||
if (menuFactory(player, context) is not MenuBuilder menuBuilder)
|
||||
throw new InvalidOperationException("Menu factory must return MenuBuilder");
|
||||
|
||||
// Dodaj automatyczną obsługę przycisku 'Wróć'
|
||||
menuBuilder.WithBackAction(p =>
|
||||
{
|
||||
if (Menus.MenuManager.Instance.GetMenuCategories().TryGetValue(categoryId, out var category))
|
||||
{
|
||||
Menus.MenuManager.Instance.CreateCategoryMenuPublic(category, p).OpenMenu(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
Menus.MenuManager.Instance.OpenMainMenu(p);
|
||||
}
|
||||
});
|
||||
throw new InvalidOperationException("Menu factory must return MenuBuilder");
|
||||
}
|
||||
|
||||
return menuBuilder;
|
||||
return menuBuilder.WithBackButton();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -276,32 +265,23 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
Func<CCSPlayerController, MenuContext, object> menuFactory, string? permission, string? commandName, object moduleLocalizer)
|
||||
{
|
||||
if (moduleLocalizer is not IStringLocalizer localizer)
|
||||
{
|
||||
throw new InvalidOperationException("moduleLocalizer must be an IStringLocalizer instance");
|
||||
}
|
||||
|
||||
Menus.MenuManager.Instance.RegisterMenu(categoryId, menuId, menuNameKey, BuilderFactory, permission, commandName, localizer);
|
||||
return;
|
||||
|
||||
MenuBuilder BuilderFactory(CCSPlayerController player)
|
||||
{
|
||||
var context = new MenuContext(categoryId, menuId, menuNameKey, permission, commandName);
|
||||
MenuContext context = new MenuContext(categoryId, menuId, menuNameKey, permission, commandName);
|
||||
|
||||
if (menuFactory(player, context) is not MenuBuilder menuBuilder)
|
||||
throw new InvalidOperationException("Menu factory must return MenuBuilder");
|
||||
|
||||
// Dodaj automatyczną obsługę przycisku 'Wróć'
|
||||
menuBuilder.WithBackAction(p =>
|
||||
{
|
||||
if (Menus.MenuManager.Instance.GetMenuCategories().TryGetValue(categoryId, out var category))
|
||||
{
|
||||
Menus.MenuManager.Instance.CreateCategoryMenuPublic(category, p).OpenMenu(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
Menus.MenuManager.Instance.OpenMainMenu(p);
|
||||
}
|
||||
});
|
||||
throw new InvalidOperationException("Menu factory must return MenuBuilder");
|
||||
}
|
||||
|
||||
return menuBuilder;
|
||||
return menuBuilder.WithBackButton();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -313,20 +293,7 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
|
||||
public object CreateMenuWithBack(string title, string categoryId, CCSPlayerController player)
|
||||
{
|
||||
var builder = new MenuBuilder(title);
|
||||
builder.WithBackAction(p =>
|
||||
{
|
||||
if (Menus.MenuManager.Instance.GetMenuCategories().TryGetValue(categoryId, out var category))
|
||||
{
|
||||
Menus.MenuManager.Instance.CreateCategoryMenuPublic(category, p).OpenMenu(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
Menus.MenuManager.Instance.OpenMainMenu(p);
|
||||
}
|
||||
});
|
||||
|
||||
return builder;
|
||||
return new MenuBuilder(title).WithBackButton();
|
||||
}
|
||||
|
||||
public object CreateMenuWithBack(MenuContext context, CCSPlayerController player)
|
||||
|
|
@ -334,10 +301,10 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
// Get translated title if module has localizer
|
||||
string title = context.MenuTitle;
|
||||
|
||||
if (Menus.MenuManager.Instance.GetMenuCategories().TryGetValue(context.CategoryId, out var category))
|
||||
if (Menus.MenuManager.Instance.GetMenuCategories().TryGetValue(context.CategoryId, out MenuCategory? category))
|
||||
{
|
||||
// Check if this specific menu has a localizer
|
||||
if (category.MenuLocalizers.TryGetValue(context.MenuId, out var menuLocalizer))
|
||||
if (category.MenuLocalizers.TryGetValue(context.MenuId, out IStringLocalizer? menuLocalizer))
|
||||
{
|
||||
using (new WithTemporaryCulture(player.GetLanguage()))
|
||||
{
|
||||
|
|
@ -365,12 +332,12 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
public object CreateMenuWithPlayers(string title, string categoryId, CCSPlayerController admin,
|
||||
Func<CCSPlayerController, bool> filter, Action<CCSPlayerController, CCSPlayerController> onSelect)
|
||||
{
|
||||
var menu = (MenuBuilder)CreateMenuWithBack(title, categoryId, admin);
|
||||
var players = Helper.GetValidPlayers().Where(filter);
|
||||
MenuBuilder menu = (MenuBuilder)CreateMenuWithBack(title, categoryId, admin);
|
||||
IEnumerable<CCSPlayerController> players = Helper.GetValidPlayers().Where(filter);
|
||||
|
||||
foreach (var player in players)
|
||||
foreach (CCSPlayerController? player in players)
|
||||
{
|
||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
||||
string playerName = Helper.Truncate(player.PlayerName, 26);
|
||||
menu.AddOption(playerName, _ =>
|
||||
{
|
||||
if (player.IsValid)
|
||||
|
|
@ -389,10 +356,10 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
// Get translated title if module has localizer
|
||||
string title = context.MenuTitle;
|
||||
|
||||
if (Menus.MenuManager.Instance.GetMenuCategories().TryGetValue(context.CategoryId, out var category))
|
||||
if (Menus.MenuManager.Instance.GetMenuCategories().TryGetValue(context.CategoryId, out MenuCategory? category))
|
||||
{
|
||||
// Check if this specific menu has a localizer
|
||||
if (category.MenuLocalizers.TryGetValue(context.MenuId, out var menuLocalizer))
|
||||
if (category.MenuLocalizers.TryGetValue(context.MenuId, out IStringLocalizer? menuLocalizer))
|
||||
{
|
||||
using (new WithTemporaryCulture(admin.GetLanguage()))
|
||||
{
|
||||
|
|
@ -416,7 +383,9 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
string? permission = null)
|
||||
{
|
||||
if (menu is not MenuBuilder menuBuilder)
|
||||
{
|
||||
throw new InvalidOperationException("Menu must be a MenuBuilder instance");
|
||||
}
|
||||
|
||||
menuBuilder.AddOption(name, action, disabled, permission);
|
||||
}
|
||||
|
|
@ -425,13 +394,18 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
bool disabled = false, string? permission = null)
|
||||
{
|
||||
if (menu is not MenuBuilder menuBuilder)
|
||||
{
|
||||
throw new InvalidOperationException("Menu must be a MenuBuilder instance");
|
||||
}
|
||||
|
||||
menuBuilder.AddSubMenu(name, player =>
|
||||
{
|
||||
var subMenu = subMenuFactory(player);
|
||||
object subMenu = subMenuFactory(player);
|
||||
if (subMenu is not MenuBuilder builder)
|
||||
{
|
||||
throw new InvalidOperationException("SubMenu factory must return MenuBuilder");
|
||||
}
|
||||
|
||||
return builder;
|
||||
}, disabled, permission);
|
||||
}
|
||||
|
|
@ -439,8 +413,10 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||
public void OpenMenu(object menu, CCSPlayerController player)
|
||||
{
|
||||
if (menu is not MenuBuilder menuBuilder)
|
||||
{
|
||||
throw new InvalidOperationException("Menu must be a MenuBuilder instance");
|
||||
}
|
||||
|
||||
menuBuilder.OpenMenu(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,22 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||
{
|
||||
internal static CS2_SimpleAdmin Instance { get; private set; } = new();
|
||||
|
||||
public override string ModuleName => "CS2-SimpleAdmin" + (Helper.IsDebugBuild ? " (DEBUG)" : " (RELEASE)");
|
||||
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;
|
||||
|
|
@ -31,30 +42,38 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||
{
|
||||
ServerLoaded = false;
|
||||
_serverLoading = false;
|
||||
|
||||
|
||||
CacheManager?.Dispose();
|
||||
CacheManager = new CacheManager();
|
||||
|
||||
|
||||
// OnGameServerSteamAPIActivated();
|
||||
OnMapStart(string.Empty);
|
||||
|
||||
AddTimer(6.0f, () =>
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
|
||||
if (DatabaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PlayersInfo.Clear();
|
||||
CachedPlayers.Clear();
|
||||
BotPlayers.Clear();
|
||||
|
||||
foreach (var player in Utilities.GetPlayers().Where(p => p.IsValid && p is { Connected: PlayerConnectedState.Connected, IsHLTV: false }).ToArray())
|
||||
|
||||
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"));
|
||||
|
||||
|
|
@ -64,22 +83,14 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||
PlayersTimer?.Kill();
|
||||
PlayersTimer = null;
|
||||
PlayerManager.CheckPlayersTimer();
|
||||
|
||||
|
||||
Menus.MenuManager.Instance.InitializeDefaultCategories();
|
||||
BasicMenu.Initialize();
|
||||
}
|
||||
|
||||
public override void OnAllPluginsLoaded(bool hotReload)
|
||||
{
|
||||
try
|
||||
{
|
||||
MenuApi = MenuCapability.Get();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Unable to load required plugins ... \n{exception}", ex.Message);
|
||||
Unload(false);
|
||||
}
|
||||
UiKit.Preload(Logger);
|
||||
|
||||
RegisterEvents();
|
||||
AddTimer(0.5f, RegisterCommands.InitializeCommands);
|
||||
|
|
@ -87,7 +98,10 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||
// 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 (!ServerLoaded)
|
||||
{
|
||||
OnGameServerSteamAPIActivated();
|
||||
}
|
||||
});
|
||||
|
||||
if (!CoreConfig.UnlockConCommands)
|
||||
|
|
@ -107,16 +121,17 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||
|
||||
_logger = Logger;
|
||||
Config = config;
|
||||
UiKit.Configure(config.MenuConfigs);
|
||||
|
||||
bool missing = false;
|
||||
var cssPath = Path.Combine(Server.GameDirectory, "csgo", "addons", "counterstrikesharp");
|
||||
var pluginsPath = Path.Combine(cssPath, "plugins");
|
||||
var sharedPath = Path.Combine(cssPath, "shared");
|
||||
string cssPath = Path.Combine(Server.GameDirectory, "csgo", "addons", "counterstrikesharp");
|
||||
string pluginsPath = Path.Combine(cssPath, "plugins");
|
||||
string sharedPath = Path.Combine(cssPath, "shared");
|
||||
|
||||
foreach (var plugin in _requiredPlugins)
|
||||
foreach (string plugin in _requiredPlugins)
|
||||
{
|
||||
var pluginDirPath = Path.Combine(pluginsPath, plugin);
|
||||
var pluginDllPath = Path.Combine(pluginDirPath, $"{plugin}.dll");
|
||||
string pluginDirPath = Path.Combine(pluginsPath, plugin);
|
||||
string pluginDllPath = Path.Combine(pluginDirPath, $"{plugin}.dll");
|
||||
|
||||
if (!Directory.Exists(pluginDirPath))
|
||||
{
|
||||
|
|
@ -135,10 +150,10 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||
}
|
||||
}
|
||||
|
||||
foreach (var shared in _requiredShared)
|
||||
foreach (string shared in _requiredShared)
|
||||
{
|
||||
var sharedDirPath = Path.Combine(sharedPath, shared);
|
||||
var sharedDllPath = Path.Combine(sharedDirPath, $"{shared}.dll");
|
||||
string sharedDirPath = Path.Combine(sharedPath, shared);
|
||||
string sharedDllPath = Path.Combine(sharedDirPath, $"{shared}.dll");
|
||||
|
||||
if (!Directory.Exists(sharedDirPath))
|
||||
{
|
||||
|
|
@ -156,7 +171,7 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||
missing = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (missing)
|
||||
{
|
||||
Server.ExecuteCommand($"css_plugins unload {ModuleDirectory}");
|
||||
|
|
@ -164,54 +179,59 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||
}
|
||||
|
||||
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))
|
||||
|
||||
if (Config.DatabaseConfig.DatabaseType.Contains("mysql", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
throw new Exception("[CS2-SimpleAdmin] You need to setup MySQL credentials in config!");
|
||||
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);
|
||||
}
|
||||
|
||||
var builder = new MySqlConnectionStringBuilder()
|
||||
(bool success, string? exception) = Task.Run(() => DatabaseProvider.CheckConnectionAsync()).GetAwaiter().GetResult();
|
||||
if (!success)
|
||||
{
|
||||
Server = config.DatabaseConfig.DatabaseHost,
|
||||
Database = config.DatabaseConfig.DatabaseName,
|
||||
UserID = config.DatabaseConfig.DatabaseUser,
|
||||
Password = config.DatabaseConfig.DatabasePassword,
|
||||
Port = (uint)config.DatabaseConfig.DatabasePort,
|
||||
SslMode = Enum.TryParse(config.DatabaseConfig.DatabaseSSlMode, true, out MySqlSslMode sslMode)
|
||||
? sslMode
|
||||
: MySqlSslMode.Preferred,
|
||||
Pooling = true,
|
||||
};
|
||||
if (exception != null)
|
||||
{
|
||||
Logger.LogError("Problem with database connection! \n{exception}", exception);
|
||||
}
|
||||
|
||||
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!");
|
||||
Unload(false);
|
||||
return;
|
||||
}
|
||||
|
||||
DatabaseProvider = new SqliteDatabaseProvider(ModuleDirectory + "/" + config.DatabaseConfig.SqliteFilePath);
|
||||
}
|
||||
|
||||
var (success, 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");
|
||||
|
|
@ -220,11 +240,15 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||
_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);
|
||||
|
|
@ -233,7 +257,7 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||
|
||||
internal static TargetResult? GetTarget(CommandInfo command, int argument = 1)
|
||||
{
|
||||
var matches = command.GetArgTargetResult(argument);
|
||||
TargetResult matches = command.GetArgTargetResult(argument);
|
||||
|
||||
if (!matches.Any())
|
||||
{
|
||||
|
|
@ -242,10 +266,14 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||
}
|
||||
|
||||
if (command.GetArg(argument).StartsWith('@'))
|
||||
{
|
||||
return matches;
|
||||
}
|
||||
|
||||
if (matches.Count() == 1)
|
||||
{
|
||||
return matches;
|
||||
}
|
||||
|
||||
command.ReplyToCommand($"Multiple targets found for \"{command.GetArg(argument)}\".");
|
||||
return null;
|
||||
|
|
@ -257,12 +285,16 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||
CacheManager = null;
|
||||
PlayersTimer?.Kill();
|
||||
PlayersTimer = null;
|
||||
|
||||
|
||||
UnregisterEvents();
|
||||
|
||||
|
||||
if (hotReload)
|
||||
{
|
||||
PlayersInfo.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
Server.ExecuteCommand($"css_plugins unload {ModuleDirectory}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.369">
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.376">
|
||||
<PrivateAssets>none</PrivateAssets>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
|
@ -47,8 +47,8 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="MenuManagerApi">
|
||||
<HintPath>3rd_party\MenuManagerApi.dll</HintPath>
|
||||
<Reference Include="PanoramaUiKit.Shared">
|
||||
<HintPath>3rd_party\PanoramaUiKit.Shared.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ public static class RegisterCommands
|
|||
{
|
||||
internal static readonly Dictionary<string, IList<CommandDefinition>> _commandDefinitions =
|
||||
new(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
|
||||
private delegate void CommandCallback(CCSPlayerController? caller, CommandInfo.CommandCallback callback);
|
||||
|
||||
|
||||
private static readonly string CommandsPath = Path.Combine(CS2_SimpleAdmin.ConfigDirectory, "Commands.json");
|
||||
private static readonly List<CommandMapping> CommandMappings =
|
||||
[
|
||||
|
|
@ -101,7 +101,7 @@ public static class RegisterCommands
|
|||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||
private static void CreateConfig()
|
||||
{
|
||||
var commands = new CommandsConfig
|
||||
CommandsConfig commands = new CommandsConfig
|
||||
{
|
||||
Commands = new Dictionary<string, Command>
|
||||
{
|
||||
|
|
@ -161,14 +161,14 @@ public static class RegisterCommands
|
|||
{ "css_adminvoice", new Command { Aliases = ["css_adminvoice", "css_listenall"] } }
|
||||
}
|
||||
};
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
|
||||
JsonSerializerOptions options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(commands, options);
|
||||
string json = JsonSerializer.Serialize(commands, options);
|
||||
File.WriteAllText(CommandsPath, json);
|
||||
}
|
||||
|
||||
|
|
@ -179,46 +179,52 @@ public static class RegisterCommands
|
|||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||
private static void Register()
|
||||
{
|
||||
var json = File.ReadAllText(CommandsPath);
|
||||
var commandsConfig = JsonSerializer.Deserialize<CommandsConfig>(json,
|
||||
string json = File.ReadAllText(CommandsPath);
|
||||
CommandsConfig? commandsConfig = JsonSerializer.Deserialize<CommandsConfig>(json,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
|
||||
if (commandsConfig?.Commands != null)
|
||||
{
|
||||
foreach (var command in commandsConfig.Commands)
|
||||
foreach (KeyValuePair<string, Command> command in commandsConfig.Commands)
|
||||
{
|
||||
if (command.Value.Aliases == null) continue;
|
||||
|
||||
if (command.Value.Aliases == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CS2_SimpleAdmin._logger?.LogInformation(
|
||||
$"Registering command: `{command.Key}` with aliases: `{string.Join(", ", command.Value.Aliases)}`");
|
||||
|
||||
var mapping = CommandMappings.FirstOrDefault(m => m.CommandKey == command.Key);
|
||||
if (mapping == null || command.Value.Aliases.Length == 0) continue;
|
||||
|
||||
foreach (var alias in command.Value.Aliases)
|
||||
|
||||
CommandMapping? mapping = CommandMappings.FirstOrDefault(m => m.CommandKey == command.Key);
|
||||
if (mapping == null || command.Value.Aliases.Length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (string alias in command.Value.Aliases)
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddCommand(alias, "", mapping.Callback);
|
||||
}
|
||||
}
|
||||
|
||||
// Commands added after Commands.json was generated are registered under their default name
|
||||
foreach (var mapping in CommandMappings.Where(m => !commandsConfig.Commands.ContainsKey(m.CommandKey)))
|
||||
foreach (CommandMapping? mapping in CommandMappings.Where(m => !commandsConfig.Commands.ContainsKey(m.CommandKey)))
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogInformation($"Registering command: `{mapping.CommandKey}` (not in Commands.json, using default alias)");
|
||||
CS2_SimpleAdmin.Instance.AddCommand(mapping.CommandKey, "", mapping.Callback);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (name, definitions) in _commandDefinitions)
|
||||
|
||||
foreach ((string? name, IList<CommandDefinition>? definitions) in _commandDefinitions)
|
||||
{
|
||||
foreach (var definition in definitions)
|
||||
foreach (CommandDefinition definition in definitions)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogInformation($"Registering custom command: `{name}`");
|
||||
CS2_SimpleAdmin.Instance.AddCommand(name, definition.Description, definition.Callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents the JSON configuration structure for commands.
|
||||
/// </summary>
|
||||
|
|
@ -226,7 +232,7 @@ public static class RegisterCommands
|
|||
{
|
||||
public Dictionary<string, Command>? Commands { get; init; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a command definition containing a list of aliases.
|
||||
/// </summary>
|
||||
|
|
@ -234,7 +240,7 @@ public static class RegisterCommands
|
|||
{
|
||||
public string[]? Aliases { get; init; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Maps a command key to its respective command callback handler.
|
||||
/// </summary>
|
||||
|
|
@ -243,4 +249,4 @@ public static class RegisterCommands
|
|||
public string CommandKey { get; } = commandKey;
|
||||
public CommandInfo.CommandCallback Callback { get; } = callback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using CounterStrikeSharp.API;
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Commands.Targeting;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.ValveConstants.Protobuf;
|
||||
using CS2_SimpleAdmin.Managers;
|
||||
|
|
@ -21,34 +22,38 @@ public partial class CS2_SimpleAdmin
|
|||
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnBanCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
string callerName = Helper.GetCallerName(caller);
|
||||
if (command.ArgCount < 2)
|
||||
{
|
||||
return;
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, Connected: PlayerConnectedState.Connected, IsHLTV: false }).ToList();
|
||||
}
|
||||
|
||||
TargetResult? targets = GetTarget(command);
|
||||
if (targets == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<CCSPlayerController> playersToTarget = targets.Players.Where(player => player is { IsValid: true, Connected: PlayerConnectedState.Connected, IsHLTV: false }).ToList();
|
||||
|
||||
if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var reason = command.ArgCount >= 3
|
||||
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
var time = Helper.ParsePenaltyTime(command.GetArg(2));
|
||||
string reason = Helper.GetArgsReason(command, 3);
|
||||
int time = Helper.ParsePenaltyTime(command.GetArg(2));
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
if (!caller.CanTarget(player)) return;
|
||||
|
||||
if (!caller.CanTarget(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (time < 0 && caller != null && caller.IsValid && Config.OtherSettings.ShowBanMenuIfNoTime)
|
||||
{
|
||||
DurationMenu.OpenMenu(caller, $"{_localizer?["sa_ban"] ?? "Ban"}: {player.PlayerName}", player,
|
||||
ManagePlayersMenu.BanMenu);
|
||||
BasicMenu.CreatePenaltyMenu(caller, player, PenaltyType.Ban).OpenMenu(caller);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -69,18 +74,27 @@ public partial class CS2_SimpleAdmin
|
|||
/// <param name="silent">If true, suppresses command logging.</param>
|
||||
internal void Ban(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, BanManager? banManager = null, CommandInfo? command = null, bool silent = false)
|
||||
{
|
||||
if (DatabaseProvider == null || !player.IsValid || !player.UserId.HasValue) return;
|
||||
if (!caller.CanTarget(player)) return;
|
||||
if (!CheckValidBan(caller, time)) return;
|
||||
if (DatabaseProvider == null || !player.IsValid || !player.UserId.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!caller.CanTarget(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckValidBan(caller, time))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Set default caller name if not provided
|
||||
callerName = !string.IsNullOrEmpty(caller?.PlayerName)
|
||||
? caller.PlayerName
|
||||
: (_localizer?["sa_console"] ?? "Console");
|
||||
|
||||
callerName = Helper.GetCallerName(caller);
|
||||
|
||||
// Get player and admin information
|
||||
var playerInfo = PlayersInfo[player.SteamID];
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
PlayerInfo playerInfo = PlayersInfo[player.SteamID];
|
||||
PlayerInfo? adminInfo = Helper.GetAdminInfo(caller);
|
||||
|
||||
// Asynchronously handle banning logic
|
||||
Task.Run(async () =>
|
||||
|
|
@ -93,13 +107,25 @@ public partial class CS2_SimpleAdmin
|
|||
});
|
||||
|
||||
// Determine message keys and arguments based on ban time
|
||||
var (messageKey, activityMessageKey, centerArgs, adminActivityArgs) = time == 0
|
||||
? ("sa_player_ban_message_perm", "sa_admin_ban_message_perm",
|
||||
[reason, "CALLER"],
|
||||
["CALLER", player.PlayerName, reason])
|
||||
: ("sa_player_ban_message_time", "sa_admin_ban_message_time",
|
||||
new object[] { reason, time, "CALLER" },
|
||||
new object[] { "CALLER", player.PlayerName, reason, time });
|
||||
string? messageKey;
|
||||
string? activityMessageKey;
|
||||
object[]? centerArgs;
|
||||
object[]? adminActivityArgs;
|
||||
|
||||
if (time == 0)
|
||||
{
|
||||
messageKey = "sa_player_ban_message_perm";
|
||||
activityMessageKey = "sa_admin_ban_message_perm";
|
||||
centerArgs = [reason, "CALLER"];
|
||||
adminActivityArgs = ["CALLER", player.PlayerName, reason];
|
||||
}
|
||||
else
|
||||
{
|
||||
messageKey = "sa_player_ban_message_time";
|
||||
activityMessageKey = "sa_admin_ban_message_time";
|
||||
centerArgs = new object[] { reason, time, "CALLER" };
|
||||
adminActivityArgs = new object[] { "CALLER", player.PlayerName, reason, time };
|
||||
}
|
||||
|
||||
// Display center message to the player
|
||||
Helper.DisplayCenterMessage(player, messageKey, callerName, centerArgs);
|
||||
|
|
@ -125,11 +151,15 @@ public partial class CS2_SimpleAdmin
|
|||
if (!silent)
|
||||
{
|
||||
if (command == null)
|
||||
Helper.LogCommand(caller, $"css_ban {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time} {reason}");
|
||||
{
|
||||
Helper.LogCommand(caller, $"css_ban {Helper.GetLogName(player)} {time} {reason}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Ban, _localizer);
|
||||
}
|
||||
|
||||
|
|
@ -144,23 +174,26 @@ public partial class CS2_SimpleAdmin
|
|||
internal void AddBan(CCSPlayerController? caller, SteamID steamid, int time, string reason, BanManager? banManager = null)
|
||||
{
|
||||
// Set default caller name if not provided
|
||||
var callerName = !string.IsNullOrEmpty(caller?.PlayerName)
|
||||
? caller.PlayerName
|
||||
: (_localizer?["sa_console"] ?? "Console");
|
||||
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
var player = Helper.GetPlayerFromSteamid64(steamid.SteamId64);
|
||||
string callerName = Helper.GetCallerName(caller);
|
||||
|
||||
PlayerInfo? adminInfo = Helper.GetAdminInfo(caller);
|
||||
CCSPlayerController? player = Helper.GetPlayerFromSteamid64(steamid.SteamId64);
|
||||
if (player != null && player.IsValid)
|
||||
{
|
||||
if (!caller.CanTarget(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Ban(caller, player, time, reason, callerName, silent: true);
|
||||
//command.ReplyToCommand($"Banned player {player.PlayerName}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!caller.CanTarget(steamid))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Asynchronous ban operation if player is not online or not found
|
||||
Task.Run(async () =>
|
||||
{
|
||||
|
|
@ -171,7 +204,7 @@ public partial class CS2_SimpleAdmin
|
|||
penaltyId);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, steamid.SteamId64.ToString(), reason, time, PenaltyType.Ban, _localizer);
|
||||
}
|
||||
}
|
||||
|
|
@ -185,34 +218,41 @@ public partial class CS2_SimpleAdmin
|
|||
[CommandHelper(minArgs: 1, usage: "<steamid> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnAddBanCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
var callerName = caller?.PlayerName ?? _localizer?["sa_console"] ?? "Console";
|
||||
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
|
||||
if (!Helper.ValidateSteamId(command.GetArg(1), out var steamId) || steamId == null)
|
||||
if (DatabaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string callerName = caller?.PlayerName ?? _localizer?["sa_console"] ?? "Console";
|
||||
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Helper.ValidateSteamId(command.GetArg(1), out SteamID? steamId) || steamId == null)
|
||||
{
|
||||
command.ReplyToCommand("Invalid SteamID64.");
|
||||
return;
|
||||
}
|
||||
|
||||
var steamid = steamId.SteamId64;
|
||||
var reason = command.ArgCount >= 3
|
||||
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
ulong steamid = steamId.SteamId64;
|
||||
string reason = Helper.GetArgsReason(command, 3);
|
||||
|
||||
var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||
if (!CheckValidBan(caller, time)) return;
|
||||
int time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||
if (!CheckValidBan(caller, time))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var adminInfo = caller != null && caller.UserId.HasValue
|
||||
? PlayersInfo[caller.SteamID]
|
||||
: null;
|
||||
PlayerInfo? adminInfo = Helper.GetAdminInfo(caller);
|
||||
|
||||
var player = Helper.GetPlayerFromSteamid64(steamid);
|
||||
CCSPlayerController? player = Helper.GetPlayerFromSteamid64(steamid);
|
||||
if (player != null && player.IsValid)
|
||||
{
|
||||
if (!caller.CanTarget(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Ban(caller, player, time, reason, callerName, silent: true);
|
||||
//command.ReplyToCommand($"Banned player {player.PlayerName}.");
|
||||
|
|
@ -220,8 +260,10 @@ public partial class CS2_SimpleAdmin
|
|||
else
|
||||
{
|
||||
if (!caller.CanTarget(new SteamID(steamId.SteamId64)))
|
||||
{
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// Asynchronous ban operation if player is not online or not found
|
||||
Task.Run(async () =>
|
||||
{
|
||||
|
|
@ -232,7 +274,7 @@ public partial class CS2_SimpleAdmin
|
|||
penaltyId);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, steamid.ToString(), reason, time, PenaltyType.Ban, _localizer);
|
||||
|
||||
command.ReplyToCommand($"Player with steamid {steamid} is not online. Ban has been added offline.");
|
||||
|
|
@ -241,7 +283,9 @@ public partial class CS2_SimpleAdmin
|
|||
Helper.LogCommand(caller, command);
|
||||
|
||||
if (UnlockedCommands)
|
||||
{
|
||||
Server.ExecuteCommand($"banid 1 {steamId.SteamId3}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -253,10 +297,18 @@ public partial class CS2_SimpleAdmin
|
|||
[CommandHelper(minArgs: 1, usage: "<ip> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnBanIpCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
var callerName = caller?.PlayerName ?? _localizer?["sa_console"] ?? "Console";
|
||||
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
|
||||
var ipAddress = command.GetArg(1);
|
||||
if (DatabaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string callerName = caller?.PlayerName ?? _localizer?["sa_console"] ?? "Console";
|
||||
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string ipAddress = command.GetArg(1);
|
||||
|
||||
if (!Helper.IsValidIp(ipAddress))
|
||||
{
|
||||
|
|
@ -264,28 +316,31 @@ public partial class CS2_SimpleAdmin
|
|||
return;
|
||||
}
|
||||
|
||||
var reason = command.ArgCount >= 3
|
||||
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
string reason = Helper.GetArgsReason(command, 3);
|
||||
|
||||
var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||
int time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||
|
||||
if (!CheckValidBan(caller, time)) return;
|
||||
if (!CheckValidBan(caller, time))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var adminInfo = caller != null && caller.UserId.HasValue
|
||||
? PlayersInfo[caller.SteamID]
|
||||
: null;
|
||||
PlayerInfo? adminInfo = Helper.GetAdminInfo(caller);
|
||||
|
||||
var players = Helper.GetPlayerFromIp(ipAddress);
|
||||
List<CCSPlayerController> players = Helper.GetPlayerFromIp(ipAddress);
|
||||
if (players.Count >= 1)
|
||||
{
|
||||
foreach (var player in players)
|
||||
foreach (CCSPlayerController player in players)
|
||||
{
|
||||
if (player == null || !player.IsValid) continue;
|
||||
if (player == null || !player.IsValid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!caller.CanTarget(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Ban(caller, player, time, reason, callerName, silent: true);
|
||||
}
|
||||
|
|
@ -312,9 +367,12 @@ public partial class CS2_SimpleAdmin
|
|||
/// <returns>True if ban duration is valid; otherwise, false.</returns>
|
||||
private bool CheckValidBan(CCSPlayerController? caller, int duration)
|
||||
{
|
||||
if (caller == null) return true;
|
||||
if (caller == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var canPermBan = AdminManager.PlayerHasPermissions(new SteamID(caller.SteamID), "@css/permban");
|
||||
bool canPermBan = AdminManager.PlayerHasPermissions(new SteamID(caller.SteamID), "@css/permban");
|
||||
|
||||
if (duration <= 0 && !canPermBan)
|
||||
{
|
||||
|
|
@ -322,7 +380,10 @@ public partial class CS2_SimpleAdmin
|
|||
return false;
|
||||
}
|
||||
|
||||
if (duration <= Config.OtherSettings.MaxBanDuration || canPermBan) return true;
|
||||
if (duration <= Config.OtherSettings.MaxBanDuration || canPermBan)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_max_duration_exceeded", Config.OtherSettings.MaxBanDuration]}");
|
||||
return false;
|
||||
|
|
@ -337,20 +398,20 @@ public partial class CS2_SimpleAdmin
|
|||
[CommandHelper(minArgs: 1, usage: "<steamid or name or ip> [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnUnbanCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
var callerSteamId = caller?.SteamID.ToString() ?? _localizer?["sa_console"] ?? "Console";
|
||||
if (DatabaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string callerSteamId = caller?.SteamID.ToString() ?? _localizer?["sa_console"] ?? "Console";
|
||||
if (command.GetArg(1).Length <= 1)
|
||||
{
|
||||
command.ReplyToCommand($"Too short pattern to search.");
|
||||
return;
|
||||
}
|
||||
|
||||
var pattern = command.GetArg(1);
|
||||
var reason = command.ArgCount >= 2
|
||||
? string.Join(" ", Enumerable.Range(2, command.ArgCount - 2).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
string pattern = command.GetArg(1);
|
||||
string reason = Helper.GetArgsReason(command, 2);
|
||||
Task.Run(async () => await BanManager.UnbanPlayer(pattern, callerSteamId, reason));
|
||||
Helper.LogCommand(caller, command);
|
||||
command.ReplyToCommand($"Unbanned player with pattern {pattern}.");
|
||||
|
|
@ -366,26 +427,31 @@ public partial class CS2_SimpleAdmin
|
|||
public void OnWarnCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null)
|
||||
{
|
||||
return;
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
}
|
||||
|
||||
string callerName = Helper.GetCallerName(caller);
|
||||
if (command.ArgCount < 2)
|
||||
{
|
||||
return;
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
var playersToTarget = targets.Players.Where(player => player.IsValid && player.Connected == PlayerConnectedState.Connected && !player.IsHLTV).ToList();
|
||||
}
|
||||
|
||||
TargetResult? targets = GetTarget(command);
|
||||
if (targets == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<CCSPlayerController> playersToTarget = targets.Players.Where(player => player.IsValid && player.Connected == PlayerConnectedState.Connected && !player.IsHLTV).ToList();
|
||||
|
||||
if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||
var reason = command.ArgCount >= 3
|
||||
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
int time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||
string reason = Helper.GetArgsReason(command, 3);
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
|
|
@ -407,14 +473,23 @@ public partial class CS2_SimpleAdmin
|
|||
/// <param name="command">Optional command info for logging.</param>
|
||||
internal void Warn(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, CommandInfo? command = null)
|
||||
{
|
||||
if (DatabaseProvider == null || !player.IsValid || !player.UserId.HasValue) return;
|
||||
if (!caller.CanTarget(player)) return;
|
||||
if (!CheckValidBan(caller, time)) return;
|
||||
if (DatabaseProvider == null || !player.IsValid || !player.UserId.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!caller.CanTarget(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckValidBan(caller, time))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Set default caller name if not provided
|
||||
callerName = !string.IsNullOrEmpty(caller?.PlayerName)
|
||||
? caller.PlayerName
|
||||
: (_localizer?["sa_console"] ?? "Console");
|
||||
callerName = Helper.GetCallerName(caller);
|
||||
|
||||
// Freeze player pawn if alive
|
||||
if (player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE)
|
||||
|
|
@ -424,8 +499,8 @@ public partial class CS2_SimpleAdmin
|
|||
}
|
||||
|
||||
// Get player and admin information
|
||||
var playerInfo = PlayersInfo[player.SteamID];
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
PlayerInfo playerInfo = PlayersInfo[player.SteamID];
|
||||
PlayerInfo? adminInfo = Helper.GetAdminInfo(caller);
|
||||
|
||||
// Asynchronously handle warning logic
|
||||
Task.Run(async () =>
|
||||
|
|
@ -438,16 +513,20 @@ public partial class CS2_SimpleAdmin
|
|||
});
|
||||
|
||||
// Check for warn thresholds and execute punish command if applicable
|
||||
var totalWarns = await WarnManager.GetPlayerWarnsCount(player.SteamID);
|
||||
int totalWarns = await WarnManager.GetPlayerWarnsCount(player.SteamID);
|
||||
if (Config.WarnThreshold.Count > 0)
|
||||
{
|
||||
string? punishCommand = null;
|
||||
var lastKey = Config.WarnThreshold.Keys.Max();
|
||||
int lastKey = Config.WarnThreshold.Keys.Max();
|
||||
|
||||
if (totalWarns >= lastKey)
|
||||
{
|
||||
punishCommand = Config.WarnThreshold[lastKey];
|
||||
else if (Config.WarnThreshold.TryGetValue(totalWarns, out var value))
|
||||
}
|
||||
else if (Config.WarnThreshold.TryGetValue(totalWarns, out string? value))
|
||||
{
|
||||
punishCommand = value;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(punishCommand))
|
||||
{
|
||||
|
|
@ -460,13 +539,25 @@ public partial class CS2_SimpleAdmin
|
|||
});
|
||||
|
||||
// Determine message keys and arguments based on warning time
|
||||
var (messageKey, activityMessageKey, centerArgs, adminActivityArgs) = time == 0
|
||||
? ("sa_player_warn_message_perm", "sa_admin_warn_message_perm",
|
||||
new object[] { reason, "CALLER" },
|
||||
new object[] { "CALLER", player.PlayerName, reason })
|
||||
: ("sa_player_warn_message_time", "sa_admin_warn_message_time",
|
||||
[reason, time, "CALLER"],
|
||||
["CALLER", player.PlayerName, reason, time]);
|
||||
string? messageKey;
|
||||
string? activityMessageKey;
|
||||
object[]? centerArgs;
|
||||
object[]? adminActivityArgs;
|
||||
|
||||
if (time == 0)
|
||||
{
|
||||
messageKey = "sa_player_warn_message_perm";
|
||||
activityMessageKey = "sa_admin_warn_message_perm";
|
||||
centerArgs = new object[] { reason, "CALLER" };
|
||||
adminActivityArgs = new object[] { "CALLER", player.PlayerName, reason };
|
||||
}
|
||||
else
|
||||
{
|
||||
messageKey = "sa_player_warn_message_time";
|
||||
activityMessageKey = "sa_admin_warn_message_time";
|
||||
centerArgs = [reason, time, "CALLER"];
|
||||
adminActivityArgs = ["CALLER", player.PlayerName, reason, time];
|
||||
}
|
||||
|
||||
// Display center message to the playser
|
||||
Helper.DisplayCenterMessage(player, messageKey, callerName, centerArgs);
|
||||
|
|
@ -479,14 +570,18 @@ public partial class CS2_SimpleAdmin
|
|||
|
||||
// Log the warning command
|
||||
if (command == null)
|
||||
Helper.LogCommand(caller, $"css_warn {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time} {reason}");
|
||||
{
|
||||
Helper.LogCommand(caller, $"css_warn {Helper.GetLogName(player)} {time} {reason}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
// Send Discord notification for the warning
|
||||
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Warn, _localizer);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a warning to a player by their SteamID, including support for offline players.
|
||||
/// </summary>
|
||||
|
|
@ -498,18 +593,18 @@ public partial class CS2_SimpleAdmin
|
|||
internal void AddWarn(CCSPlayerController? caller, SteamID steamid, int time, string reason, WarnManager? warnManager = null)
|
||||
{
|
||||
// Set default caller name if not provided
|
||||
var callerName = !string.IsNullOrEmpty(caller?.PlayerName)
|
||||
? caller.PlayerName
|
||||
: (_localizer?["sa_console"] ?? "Console");
|
||||
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
string callerName = Helper.GetCallerName(caller);
|
||||
|
||||
var player = Helper.GetPlayerFromSteamid64(steamid.SteamId64);
|
||||
PlayerInfo? adminInfo = Helper.GetAdminInfo(caller);
|
||||
|
||||
CCSPlayerController? player = Helper.GetPlayerFromSteamid64(steamid.SteamId64);
|
||||
|
||||
if (player != null && player.IsValid)
|
||||
{
|
||||
if (!caller.CanTarget(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Warn(caller, player, time, reason, callerName);
|
||||
//command.ReplyToCommand($"Banned player {player.PlayerName}.");
|
||||
|
|
@ -517,8 +612,10 @@ public partial class CS2_SimpleAdmin
|
|||
else
|
||||
{
|
||||
if (!caller.CanTarget(steamid))
|
||||
{
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// Asynchronous ban operation if player is not online or not found
|
||||
Task.Run(async () =>
|
||||
{
|
||||
|
|
@ -530,16 +627,20 @@ public partial class CS2_SimpleAdmin
|
|||
});
|
||||
|
||||
// Check for warn thresholds and execute punish command if applicable
|
||||
var totalWarns = await WarnManager.GetPlayerWarnsCount(steamid.SteamId64);
|
||||
int totalWarns = await WarnManager.GetPlayerWarnsCount(steamid.SteamId64);
|
||||
if (Config.WarnThreshold.Count > 0)
|
||||
{
|
||||
string? punishCommand = null;
|
||||
var lastKey = Config.WarnThreshold.Keys.Max();
|
||||
int lastKey = Config.WarnThreshold.Keys.Max();
|
||||
|
||||
if (totalWarns >= lastKey)
|
||||
{
|
||||
punishCommand = Config.WarnThreshold[lastKey];
|
||||
else if (Config.WarnThreshold.TryGetValue(totalWarns, out var value))
|
||||
}
|
||||
else if (Config.WarnThreshold.TryGetValue(totalWarns, out string? value))
|
||||
{
|
||||
punishCommand = value;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(punishCommand))
|
||||
{
|
||||
|
|
@ -550,11 +651,11 @@ public partial class CS2_SimpleAdmin
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, steamid.SteamId64.ToString(), reason, time, PenaltyType.Warn, _localizer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Handles removing a warning (unwarn) by a pattern string.
|
||||
/// </summary>
|
||||
|
|
@ -564,7 +665,10 @@ public partial class CS2_SimpleAdmin
|
|||
[CommandHelper(minArgs: 1, usage: "<steamid or name or ip>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnUnwarnCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
if (DatabaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (command.GetArg(1).Length <= 1)
|
||||
{
|
||||
|
|
@ -572,9 +676,9 @@ public partial class CS2_SimpleAdmin
|
|||
return;
|
||||
}
|
||||
|
||||
var pattern = command.GetArg(1);
|
||||
string pattern = command.GetArg(1);
|
||||
Task.Run(async () => await WarnManager.UnwarnPlayer(pattern));
|
||||
Helper.LogCommand(caller, command);
|
||||
command.ReplyToCommand($"Unwarned player with pattern {pattern}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,21 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Translations;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using System.Text;
|
||||
using CounterStrikeSharp.API.Modules.Commands.Targeting;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CS2_SimpleAdmin.Menus;
|
||||
using PanoramaUiKit.Shared.Api;
|
||||
using PanoramaUiKit.Shared.Common;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
public partial class CS2_SimpleAdmin
|
||||
{
|
||||
private const float AnnouncementSeconds = 7f;
|
||||
|
||||
/// <summary>
|
||||
/// Sends a chat message only to admins that have chat permission.
|
||||
/// The message is encoded properly to handle UTF-8 characters.
|
||||
|
|
@ -23,16 +28,18 @@ public partial class CS2_SimpleAdmin
|
|||
{
|
||||
Helper.LogCommand(caller, command);
|
||||
|
||||
var utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
|
||||
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
byte[] utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
|
||||
string utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
|
||||
foreach (var player in Helper.GetValidPlayers()
|
||||
foreach (CCSPlayerController? player in Helper.GetValidPlayers()
|
||||
.Where(p => AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat")))
|
||||
{
|
||||
if (_localizer != null)
|
||||
{
|
||||
player.PrintToChat(_localizer["sa_adminchat_template_admin",
|
||||
caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName,
|
||||
Helper.GetCallerName(caller),
|
||||
utf8String]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -45,14 +52,17 @@ public partial class CS2_SimpleAdmin
|
|||
[RequiresPermissions("@css/chat")]
|
||||
public void OnAdminCustomSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (command.GetCommandString[command.GetCommandString.IndexOf(' ')..].Length == 0) return;
|
||||
if (command.GetCommandString[command.GetCommandString.IndexOf(' ')..].Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
|
||||
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
byte[] utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
|
||||
string utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
|
||||
foreach (var player in Helper.GetValidPlayers())
|
||||
foreach (CCSPlayerController player in Helper.GetValidPlayers())
|
||||
{
|
||||
player.PrintToChat(utf8String.ReplaceColorTags());
|
||||
}
|
||||
|
|
@ -67,14 +77,17 @@ public partial class CS2_SimpleAdmin
|
|||
[RequiresPermissions("@css/chat")]
|
||||
public void OnAdminSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (command.GetCommandString[command.GetCommandString.IndexOf(' ')..].Length == 0) return;
|
||||
if (command.GetCommandString[command.GetCommandString.IndexOf(' ')..].Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
|
||||
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
var callerName = caller?.PlayerName;
|
||||
byte[] utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
|
||||
string utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
string? callerName = caller?.PlayerName;
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
foreach (var player in Helper.GetValidPlayers())
|
||||
foreach (CCSPlayerController player in Helper.GetValidPlayers())
|
||||
{
|
||||
// The message always goes out (it is a broadcast, not an activity notice),
|
||||
// but the admin's name follows ShowActivityType like every other activity message.
|
||||
|
|
@ -93,19 +106,23 @@ public partial class CS2_SimpleAdmin
|
|||
[RequiresPermissions("@css/chat")]
|
||||
public void OnAdminPrivateSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
string callerName = Helper.GetCallerName(caller);
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||
TargetResult? targets = GetTarget(command);
|
||||
if (targets == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<CCSPlayerController> playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||
|
||||
//Helper.LogCommand(caller, command);
|
||||
|
||||
var range = command.GetArg(0).Length + command.GetArg(1).Length + 2;
|
||||
var message = command.GetCommandString[range..];
|
||||
int range = command.GetArg(0).Length + command.GetArg(1).Length + 2;
|
||||
string message = command.GetCommandString[range..];
|
||||
|
||||
var utf8BytesString = Encoding.UTF8.GetBytes(message);
|
||||
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
byte[] utf8BytesString = Encoding.UTF8.GetBytes(message);
|
||||
string utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
|
|
@ -116,7 +133,7 @@ public partial class CS2_SimpleAdmin
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts a center-screen message to all players.
|
||||
/// Broadcasts a message to all players in a Panorama UI Kit banner.
|
||||
/// </summary>
|
||||
/// <param name="caller">The admin or console sending the message.</param>
|
||||
/// <param name="command">The command input containing the message.</param>
|
||||
|
|
@ -124,15 +141,15 @@ public partial class CS2_SimpleAdmin
|
|||
[RequiresPermissions("@css/chat")]
|
||||
public void OnAdminCenterSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
|
||||
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
byte[] utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
|
||||
string utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
Helper.PrintToCenterAll(utf8String.ReplaceColorTags());
|
||||
UiKit.Instance?.Banner(UiKit.BannerLayout, StripColorTags(utf8String), UiStyle.Info, AnnouncementSeconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a HUD alert message to all players.
|
||||
/// Sends a message to all players in a Panorama UI Kit toast, titled with the admin's name.
|
||||
/// </summary>
|
||||
/// <param name="caller">The admin or console sending the message.</param>
|
||||
/// <param name="command">The command input containing the message.</param>
|
||||
|
|
@ -140,14 +157,31 @@ public partial class CS2_SimpleAdmin
|
|||
[RequiresPermissions("@css/chat")]
|
||||
public void OnAdminHudSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
|
||||
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
byte[] utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
|
||||
string utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
|
||||
VirtualFunctions.ClientPrintAll(
|
||||
HudDestination.Alert,
|
||||
utf8String.ReplaceColorTags(),
|
||||
0, 0, 0, 0, 0);
|
||||
IPanoramaUiKit? kit = UiKit.Instance;
|
||||
if (kit == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string message = StripColorTags(utf8String);
|
||||
string? callerName = caller?.PlayerName;
|
||||
|
||||
foreach (CCSPlayerController player in Helper.GetValidPlayers())
|
||||
{
|
||||
kit.Toast(player, UiKit.ToastLayout, Helper.ResolveActivityName(player, callerName), message, UiStyle.Info, AnnouncementSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Panorama does not render chat color tags such as {red}: drop them.
|
||||
/// </summary>
|
||||
private static string StripColorTags(string message)
|
||||
{
|
||||
return Regex.Replace(message, @"\{[A-Za-z]+\}", string.Empty).Trim();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,16 +1,21 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Translations;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Menu;
|
||||
using CounterStrikeSharp.API.Modules.Timers;
|
||||
using CS2_SimpleAdmin.Menus;
|
||||
using PanoramaUiKit.Shared.Api;
|
||||
using PanoramaUiKit.Shared.Votes;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
public partial class CS2_SimpleAdmin
|
||||
{
|
||||
private const float VoteDurationSeconds = 30f;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the vote command, creates voting menu for players, and collects answers.
|
||||
/// Displays results after timeout and resets voting state.
|
||||
/// Handles the vote command: shows the vote screen to every player, collects one answer each
|
||||
/// and prints the results when the countdown ends.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player/admin who initiated the vote, or null for console.</param>
|
||||
/// <param name="command">Command object containing question and options.</param>
|
||||
|
|
@ -19,80 +24,123 @@ public partial class CS2_SimpleAdmin
|
|||
public void OnVoteCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (command.ArgCount < 2 || _localizer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
|
||||
VoteAnswers.Clear();
|
||||
|
||||
var question = command.GetArg(1);
|
||||
var answersCount = command.ArgCount;
|
||||
|
||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||
if (caller != null && SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
for (var i = 2; i <= answersCount - 1; i++)
|
||||
{
|
||||
VoteAnswers.Add(command.GetArg(i), 0);
|
||||
}
|
||||
|
||||
foreach (var player in Helper.GetValidPlayers())
|
||||
{
|
||||
using (new WithTemporaryCulture(player.GetLanguage()))
|
||||
{
|
||||
IMenu? voteMenu = Helper.CreateMenu(_localizer["sa_admin_vote_menu_title", question]);
|
||||
if (voteMenu == null)
|
||||
return;
|
||||
//ChatMenu voteMenu = new(_localizer!["sa_admin_vote_menu_title", question]);
|
||||
|
||||
for (var i = 2; i <= answersCount - 1; i++)
|
||||
{
|
||||
voteMenu.AddMenuOption(command.GetArg(i), Helper.HandleVotes);
|
||||
}
|
||||
|
||||
voteMenu.PostSelectAction = PostSelectAction.Close;
|
||||
|
||||
Helper.PrintToCenterAll(_localizer["sa_admin_vote_message", caller == null ? _localizer["sa_console"] : caller.PlayerName, question]);
|
||||
|
||||
player.SendLocalizedMessage(_localizer,
|
||||
"sa_admin_vote_message",
|
||||
caller == null ? _localizer["sa_console"] : caller.PlayerName,
|
||||
question);
|
||||
|
||||
voteMenu.Open(player);
|
||||
|
||||
//MenuManager.OpenChatMenu(player, voteMenu);
|
||||
}
|
||||
}
|
||||
|
||||
VoteInProgress = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (VoteInProgress)
|
||||
IPanoramaUiKit? kit = UiKit.Instance;
|
||||
if (kit == null)
|
||||
{
|
||||
AddTimer(30, () =>
|
||||
return;
|
||||
}
|
||||
|
||||
string question = command.GetArg(1);
|
||||
string callerName = Helper.GetCallerName(caller);
|
||||
List<string> answers = [];
|
||||
for (int i = 2; i < command.ArgCount; i++)
|
||||
{
|
||||
answers.Add(command.GetArg(i));
|
||||
}
|
||||
|
||||
int[] counts = new int[answers.Count];
|
||||
Dictionary<ulong, int> ballots = [];
|
||||
|
||||
VoteInProgress = true;
|
||||
|
||||
foreach (CCSPlayerController player in Helper.GetValidPlayers())
|
||||
{
|
||||
kit.Votes.Show(player, BuildVote(player, question, answers, counts, null, OnVote));
|
||||
player.SendLocalizedMessage(_localizer, "sa_admin_vote_message", callerName, question);
|
||||
}
|
||||
|
||||
Helper.PrintToCenterAll(_localizer["sa_admin_vote_message", callerName, question]);
|
||||
|
||||
AddTimer(VoteDurationSeconds, () =>
|
||||
{
|
||||
UiKit.Instance?.Votes.HideAll(UiKit.VoteLayout);
|
||||
VoteInProgress = false;
|
||||
|
||||
if (_localizer == null)
|
||||
{
|
||||
foreach (var player in Helper.GetValidPlayers())
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (CCSPlayerController player in Helper.GetValidPlayers())
|
||||
{
|
||||
player.SendLocalizedMessage(_localizer, "sa_admin_vote_message_results", question);
|
||||
|
||||
for (int i = 0; i < answers.Count; i++)
|
||||
{
|
||||
if (_localizer != null)
|
||||
player.SendLocalizedMessage(_localizer,
|
||||
"sa_admin_vote_message_results",
|
||||
question);
|
||||
player.SendLocalizedMessage(_localizer, "sa_admin_vote_message_results_answer", answers[i], counts[i]);
|
||||
}
|
||||
}
|
||||
}, TimerFlags.STOP_ON_MAPCHANGE);
|
||||
|
||||
return;
|
||||
|
||||
void OnVote(VoteEvent vote)
|
||||
{
|
||||
if (!VoteInProgress || ballots.ContainsKey(vote.Player.SteamID))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ballots[vote.Player.SteamID] = vote.Index;
|
||||
counts[vote.Index]++;
|
||||
|
||||
IPanoramaUiKit? currentKit = UiKit.Instance;
|
||||
if (currentKit == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (CCSPlayerController player in Helper.GetValidPlayers())
|
||||
{
|
||||
int? selected = null;
|
||||
if (ballots.TryGetValue(player.SteamID, out int index))
|
||||
{
|
||||
selected = index;
|
||||
}
|
||||
|
||||
foreach (var (key, value) in VoteAnswers)
|
||||
{
|
||||
foreach (var player in Helper.GetValidPlayers())
|
||||
{
|
||||
if (_localizer != null)
|
||||
player.SendLocalizedMessage(_localizer,
|
||||
"sa_admin_vote_message_results_answer",
|
||||
key,
|
||||
value);
|
||||
}
|
||||
}
|
||||
VoteAnswers.Clear();
|
||||
VoteInProgress = false;
|
||||
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||
currentKit.Votes.Update(player, BuildVote(player, question, answers, counts, selected, OnVote));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The vote screen for one player, locked once that player has voted.
|
||||
/// </summary>
|
||||
private static VoteOptions BuildVote(CCSPlayerController player, string question, List<string> answers, int[] counts,
|
||||
int? selected, Action<VoteEvent> onVote)
|
||||
{
|
||||
string title;
|
||||
using (new WithTemporaryCulture(player.GetLanguage()))
|
||||
{
|
||||
title = _localizer?["sa_admin_vote_menu_title", question] ?? question;
|
||||
}
|
||||
|
||||
List<VoteChoice> choices = [];
|
||||
for (int i = 0; i < answers.Count; i++)
|
||||
{
|
||||
choices.Add(new VoteChoice(answers[i], counts[i]));
|
||||
}
|
||||
|
||||
return new VoteOptions
|
||||
{
|
||||
Layout = UiKit.VoteLayout,
|
||||
Title = title,
|
||||
Choices = choices,
|
||||
Selected = selected,
|
||||
Locked = selected.HasValue,
|
||||
Seconds = VoteDurationSeconds,
|
||||
OnVote = onVote
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
// if (!caller.CanTarget(player)) return;
|
||||
//
|
||||
// // Set default caller name if not provided
|
||||
// callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||
// callerName ??= Helper.GetCallerName(caller);
|
||||
//
|
||||
// // Toggle no-clip mode for the player
|
||||
// player.Pawn.Value?.ToggleNoclip();
|
||||
|
|
@ -67,7 +67,7 @@
|
|||
//
|
||||
// // Log the command
|
||||
// if (command == null)
|
||||
// Helper.LogCommand(caller, $"css_noclip {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||
// Helper.LogCommand(caller, $"css_noclip {Helper.GetLogName(player)}");
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
// [CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
// public void OnGodCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
// {
|
||||
// var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
// var callerName = Helper.GetCallerName(caller);
|
||||
// var targets = GetTarget(command);
|
||||
// if (targets == null) return;
|
||||
//
|
||||
|
|
@ -111,7 +111,7 @@
|
|||
// if (!caller.CanTarget(player)) return;
|
||||
//
|
||||
// // Set default caller name if not provided
|
||||
// var callerName = caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||
// var callerName = Helper.GetCallerName(caller);
|
||||
//
|
||||
// // Toggle god mode for the player
|
||||
// if (!GodPlayers.Add(player.Slot))
|
||||
|
|
@ -121,7 +121,7 @@
|
|||
//
|
||||
// // Log the command
|
||||
// if (command == null)
|
||||
// Helper.LogCommand(caller, $"css_god {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||
// Helper.LogCommand(caller, $"css_god {Helper.GetLogName(player)}");
|
||||
//
|
||||
// // Determine message key and arguments for the god mode notification
|
||||
// var (activityMessageKey, adminActivityArgs) =
|
||||
|
|
@ -144,7 +144,7 @@
|
|||
// [RequiresPermissions("@css/slay")]
|
||||
// public void OnFreezeCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
// {
|
||||
// var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
// var callerName = Helper.GetCallerName(caller);
|
||||
// int.TryParse(command.GetArg(2), out var time);
|
||||
//
|
||||
// var targets = GetTarget(command);
|
||||
|
|
@ -171,7 +171,7 @@
|
|||
// [RequiresPermissions("@css/slay")]
|
||||
// public void OnResizeCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
// {
|
||||
// var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
// var callerName = Helper.GetCallerName(caller);
|
||||
// float.TryParse(command.GetArg(2), NumberStyles.Float, CultureInfo.InvariantCulture, out var size);
|
||||
//
|
||||
// var targets = GetTarget(command);
|
||||
|
|
@ -221,7 +221,7 @@
|
|||
// if (!caller.CanTarget(player)) return;
|
||||
//
|
||||
// // Set default caller name if not provided
|
||||
// callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||
// callerName ??= Helper.GetCallerName(caller);
|
||||
//
|
||||
// // Freeze player pawn
|
||||
// player.Pawn.Value?.Freeze();
|
||||
|
|
@ -245,7 +245,7 @@
|
|||
//
|
||||
// // Log the command and send Discord notification
|
||||
// if (command == null)
|
||||
// Helper.LogCommand(caller, $"css_freeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time}");
|
||||
// Helper.LogCommand(caller, $"css_freeze {Helper.GetLogName(player)} {time}");
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
|
|
@ -257,7 +257,7 @@
|
|||
// [RequiresPermissions("@css/slay")]
|
||||
// public void OnUnfreezeCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
// {
|
||||
// var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
// var callerName = Helper.GetCallerName(caller);
|
||||
//
|
||||
// var targets = GetTarget(command);
|
||||
// if (targets == null) return;
|
||||
|
|
@ -284,7 +284,7 @@
|
|||
// if (!caller.CanTarget(player)) return;
|
||||
//
|
||||
// // Set default caller name if not provided
|
||||
// callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||
// callerName ??= Helper.GetCallerName(caller);
|
||||
//
|
||||
// // Unfreeze player pawn
|
||||
// player.Pawn.Value?.Unfreeze();
|
||||
|
|
@ -302,6 +302,6 @@
|
|||
//
|
||||
// // Log the command and send Discord notification
|
||||
// if (command == null)
|
||||
// Helper.LogCommand(caller, $"css_unfreeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||
// Helper.LogCommand(caller, $"css_unfreeze {Helper.GetLogName(player)}");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
using System.Globalization;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Commands.Targeting;
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
|
@ -21,17 +20,20 @@ public partial class CS2_SimpleAdmin
|
|||
[CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnSlayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
string callerName = Helper.GetCallerName(caller);
|
||||
TargetResult? targets = GetTarget(command);
|
||||
if (targets == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var playersToTarget = targets.Players.Where(player => player.IsValid && player is {IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||
List<CCSPlayerController> playersToTarget = targets.Players.Where(player => player.IsValid && player is { IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
Slay(caller, player, callerName, command);
|
||||
});
|
||||
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
|
|
@ -44,18 +46,25 @@ public partial class CS2_SimpleAdmin
|
|||
/// <param name="command">Optional command info for logging.</param>
|
||||
internal static void Slay(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
||||
{
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.Connected) return;
|
||||
if (!caller.CanTarget(player)) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.Connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!caller.CanTarget(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Set default caller name if not provided
|
||||
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||
callerName ??= Helper.GetCallerName(caller);
|
||||
|
||||
// Make the player commit suicide
|
||||
player.CommitSuicide(false, true);
|
||||
player.EmitSound("Player.Death");
|
||||
|
||||
// Determine message keys and arguments for the slay notification
|
||||
var (activityMessageKey, adminActivityArgs) =
|
||||
(string? activityMessageKey, object[]? adminActivityArgs) =
|
||||
("sa_admin_slay_message",
|
||||
new object[] { "CALLER", player.PlayerName });
|
||||
|
||||
|
|
@ -67,7 +76,9 @@ public partial class CS2_SimpleAdmin
|
|||
|
||||
// Log the command and send Discord notification
|
||||
if (command == null)
|
||||
Helper.LogCommand(caller, $"css_slay {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||
{
|
||||
Helper.LogCommand(caller, $"css_slay {Helper.GetLogName(player)}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -79,12 +90,15 @@ public partial class CS2_SimpleAdmin
|
|||
[CommandHelper(minArgs: 1, usage: "<#userid or name> [damage]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnSlapCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var damage = 0;
|
||||
int damage = 0;
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
TargetResult? targets = GetTarget(command);
|
||||
if (targets == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||
List<CCSPlayerController> playersToTarget = targets.Players.Where(player => player.IsValid && player is { IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||
|
||||
if (command.ArgCount >= 2)
|
||||
{
|
||||
|
|
@ -94,14 +108,16 @@ public partial class CS2_SimpleAdmin
|
|||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
if (player.Connected != PlayerConnectedState.Connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (caller!.CanTarget(player))
|
||||
{
|
||||
Slap(caller, player, damage, command);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
|
|
@ -114,26 +130,34 @@ public partial class CS2_SimpleAdmin
|
|||
/// <param name="command">Optional command info for logging.</param>
|
||||
internal static void Slap(CCSPlayerController? caller, CCSPlayerController player, int damage, CommandInfo? command = null)
|
||||
{
|
||||
if (!caller.CanTarget(player)) return;
|
||||
if (!caller.CanTarget(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Set default caller name if not provided
|
||||
var callerName = caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||
|
||||
string callerName = Helper.GetCallerName(caller);
|
||||
|
||||
// Apply slap damage to the player
|
||||
player.Pawn.Value?.Slap(damage);
|
||||
player.EmitSound("Player.DamageFall");
|
||||
|
||||
// Log the command
|
||||
if (command == null)
|
||||
Helper.LogCommand(caller, $"css_slap {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {damage}");
|
||||
|
||||
{
|
||||
Helper.LogCommand(caller, $"css_slap {Helper.GetLogName(player)} {damage}");
|
||||
}
|
||||
|
||||
// Determine message key and arguments for the slap notification
|
||||
var (activityMessageKey, adminActivityArgs) =
|
||||
(string? activityMessageKey, object[]? adminActivityArgs) =
|
||||
("sa_admin_slap_message",
|
||||
new object[] { "CALLER", player.PlayerName });
|
||||
|
||||
// Display admin activity message to other players
|
||||
if (caller != null && SilentPlayers.Contains(caller.Slot)) return;
|
||||
if (caller != null && SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_localizer != null)
|
||||
{
|
||||
|
|
@ -150,15 +174,18 @@ public partial class CS2_SimpleAdmin
|
|||
[CommandHelper(minArgs: 2, usage: "<#userid or name> [<ct/tt/spec>] [-k]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnTeamCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
var teamName = command.GetArg(2).ToLower();
|
||||
string callerName = Helper.GetCallerName(caller);
|
||||
string teamName = command.GetArg(2).ToLower();
|
||||
string _teamName;
|
||||
var teamNum = CsTeam.Spectator;
|
||||
CsTeam teamNum = CsTeam.Spectator;
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
TargetResult? targets = GetTarget(command);
|
||||
if (targets == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||
List<CCSPlayerController> playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||
|
||||
switch (teamName)
|
||||
{
|
||||
|
|
@ -185,13 +212,13 @@ public partial class CS2_SimpleAdmin
|
|||
break;
|
||||
}
|
||||
|
||||
var kill = command.GetArg(3).ToLower().Equals("-k");
|
||||
bool kill = command.GetArg(3).ToLower().Equals("-k");
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
ChangeTeam(caller, player, _teamName, teamNum, kill, command);
|
||||
});
|
||||
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
|
|
@ -208,45 +235,79 @@ public partial class CS2_SimpleAdmin
|
|||
{
|
||||
// Check if the player is valid and connected
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.Connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure the caller can target the player
|
||||
if (!caller.CanTarget(player)) return;
|
||||
if (!caller.CanTarget(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Set default caller name if not provided
|
||||
var callerName = caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||
string callerName = Helper.GetCallerName(caller);
|
||||
|
||||
// Change team based on the provided teamName and conditions
|
||||
if (!teamName.Equals("swap", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && teamNum != CsTeam.Spectator && !kill && Instance.Config.OtherSettings.TeamSwitchType == 1)
|
||||
{
|
||||
player.SwitchTeam(teamNum);
|
||||
}
|
||||
else
|
||||
{
|
||||
player.ChangeTeam(teamNum);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (player.TeamNum != (byte)CsTeam.Spectator)
|
||||
{
|
||||
var _teamNum = (CsTeam)player.TeamNum == CsTeam.Terrorist ? CsTeam.CounterTerrorist : CsTeam.Terrorist;
|
||||
teamName = _teamNum == CsTeam.Terrorist ? "TT" : "CT";
|
||||
if (player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && !kill && Instance.Config.OtherSettings.TeamSwitchType == 1)
|
||||
player.SwitchTeam(_teamNum);
|
||||
CsTeam _teamNum;
|
||||
|
||||
if ((CsTeam)player.TeamNum == CsTeam.Terrorist)
|
||||
{
|
||||
_teamNum = CsTeam.CounterTerrorist;
|
||||
}
|
||||
else
|
||||
{
|
||||
_teamNum = CsTeam.Terrorist;
|
||||
}
|
||||
if (_teamNum == CsTeam.Terrorist)
|
||||
{
|
||||
teamName = "TT";
|
||||
}
|
||||
else
|
||||
{
|
||||
teamName = "CT";
|
||||
}
|
||||
if (player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && !kill && Instance.Config.OtherSettings.TeamSwitchType == 1)
|
||||
{
|
||||
player.SwitchTeam(_teamNum);
|
||||
}
|
||||
else
|
||||
{
|
||||
player.ChangeTeam(_teamNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log the command
|
||||
if (command == null)
|
||||
{
|
||||
Helper.LogCommand(caller, $"css_team {player.PlayerName} {teamName}");
|
||||
}
|
||||
|
||||
// Determine message key and arguments for the team change notification
|
||||
var activityMessageKey = "sa_admin_team_message";
|
||||
var adminActivityArgs = new object[] { "CALLER", player.PlayerName, teamName };
|
||||
string activityMessageKey = "sa_admin_team_message";
|
||||
object[] adminActivityArgs = new object[] { "CALLER", player.PlayerName, teamName };
|
||||
|
||||
// Display admin activity message to other players
|
||||
if (caller != null && SilentPlayers.Contains(caller.Slot)) return;
|
||||
if (caller != null && SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||
}
|
||||
|
|
@ -261,21 +322,26 @@ public partial class CS2_SimpleAdmin
|
|||
public void OnRenameCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
// Set default caller name if not provided
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
string callerName = Helper.GetCallerName(caller);
|
||||
|
||||
// Get the new name from the command arguments
|
||||
var newName = command.GetArg(2);
|
||||
string newName = command.GetArg(2);
|
||||
|
||||
// Check if the new name is valid
|
||||
if (string.IsNullOrEmpty(newName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the targets based on the command
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
TargetResult? targets = GetTarget(command);
|
||||
if (targets == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter out valid players from the targets
|
||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||
List<CCSPlayerController> playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||
|
||||
// Log the command
|
||||
Helper.LogCommand(caller, command);
|
||||
|
|
@ -285,15 +351,19 @@ public partial class CS2_SimpleAdmin
|
|||
{
|
||||
// Check if the player is connected and can be targeted
|
||||
if (player.Connected != PlayerConnectedState.Connected || !caller!.CanTarget(player))
|
||||
{
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// Determine message key and arguments for the rename notification
|
||||
var activityMessageKey = "sa_admin_rename_message";
|
||||
var adminActivityArgs = new object[] { "CALLER", player.PlayerName, newName };
|
||||
string activityMessageKey = "sa_admin_rename_message";
|
||||
object[] adminActivityArgs = new object[] { "CALLER", player.PlayerName, newName };
|
||||
|
||||
// Display admin activity message to other players
|
||||
if (caller != null && !SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||
}
|
||||
|
||||
// Rename the player
|
||||
player.Rename(newName);
|
||||
|
|
@ -310,17 +380,20 @@ public partial class CS2_SimpleAdmin
|
|||
public void OnPrenameCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
// Set default caller name if not provided
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
string callerName = Helper.GetCallerName(caller);
|
||||
|
||||
// Get the new name from the command arguments
|
||||
var newName = command.GetArg(2);
|
||||
string newName = command.GetArg(2);
|
||||
|
||||
// Retrieve the targets based on the command
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
TargetResult? targets = GetTarget(command);
|
||||
if (targets == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter out valid players from the targets
|
||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||
List<CCSPlayerController> playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||
|
||||
// Log the command
|
||||
Helper.LogCommand(caller, command);
|
||||
|
|
@ -330,20 +403,22 @@ public partial class CS2_SimpleAdmin
|
|||
{
|
||||
// Check if the player is connected and can be targeted
|
||||
if (player.Connected != PlayerConnectedState.Connected || !caller!.CanTarget(player))
|
||||
{
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// Determine message key and arguments for the rename notification
|
||||
var activityMessageKey = "sa_admin_rename_message";
|
||||
var adminActivityArgs = new object[] { "CALLER", player.PlayerName, newName };
|
||||
string activityMessageKey = "sa_admin_rename_message";
|
||||
object[] adminActivityArgs = new object[] { "CALLER", player.PlayerName, newName };
|
||||
|
||||
// Display admin activity message to other players
|
||||
if (caller != null && !SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||
}
|
||||
|
||||
|
||||
// Determine if the new name is valid and update the renamed players list
|
||||
var steamId = player.SteamID;
|
||||
ulong steamId = player.SteamID;
|
||||
if (!string.IsNullOrEmpty(newName))
|
||||
{
|
||||
RenamedPlayers[steamId] = newName;
|
||||
|
|
@ -370,51 +445,65 @@ public partial class CS2_SimpleAdmin
|
|||
IEnumerable<CCSPlayerController> playersToTeleport;
|
||||
CCSPlayerController? destinationPlayer;
|
||||
|
||||
var targets = GetTarget(command);
|
||||
TargetResult? targets = GetTarget(command);
|
||||
|
||||
if (command.ArgCount < 3)
|
||||
{
|
||||
if (caller == null || caller.PlayerPawn?.Value?.LifeState != (int)LifeState_t.LIFE_ALIVE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (targets == null || targets.Count() != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
destinationPlayer = targets.Players.FirstOrDefault(p =>
|
||||
p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.Connected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE });
|
||||
|
||||
if (destinationPlayer == null || !caller.CanTarget(destinationPlayer) || caller.PlayerPawn.Value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
playersToTeleport = [caller];
|
||||
}
|
||||
else
|
||||
{
|
||||
var destination = GetTarget(command, 2);
|
||||
TargetResult? destination = GetTarget(command, 2);
|
||||
if (targets == null || destination == null || destination.Count() != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
destinationPlayer = destination.Players.FirstOrDefault(p =>
|
||||
p is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE });
|
||||
|
||||
if (destinationPlayer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
playersToTeleport = targets.Players
|
||||
.Where(p => p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.Connected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE } && caller.CanTarget(p))
|
||||
.ToList();
|
||||
|
||||
if (!playersToTeleport.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Log command
|
||||
Helper.LogCommand(caller, command);
|
||||
|
||||
foreach (var player in playersToTeleport)
|
||||
foreach (CCSPlayerController player in playersToTeleport)
|
||||
{
|
||||
if (player.PlayerPawn?.Value == null || destinationPlayer?.PlayerPawn?.Value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
player.TeleportPlayer(destinationPlayer);
|
||||
|
||||
|
|
@ -453,7 +542,7 @@ public partial class CS2_SimpleAdmin
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Brings targeted player(s) to the caller or specified destination player's location.
|
||||
/// </summary>
|
||||
|
|
@ -469,11 +558,15 @@ public partial class CS2_SimpleAdmin
|
|||
if (command.ArgCount < 3)
|
||||
{
|
||||
if (caller == null || caller.PlayerPawn?.Value?.LifeState != (int)LifeState_t.LIFE_ALIVE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var targets = GetTarget(command);
|
||||
TargetResult? targets = GetTarget(command);
|
||||
if (targets == null || !targets.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
destinationPlayer = caller;
|
||||
|
||||
|
|
@ -483,20 +576,26 @@ public partial class CS2_SimpleAdmin
|
|||
}
|
||||
else
|
||||
{
|
||||
var destination = GetTarget(command);
|
||||
TargetResult? destination = GetTarget(command);
|
||||
if (destination == null || destination.Count() != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
destinationPlayer = destination.Players.FirstOrDefault(p =>
|
||||
p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.Connected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE });
|
||||
|
||||
if (destinationPlayer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Rest args = targets to teleport
|
||||
var targets = GetTarget(command, 2);
|
||||
TargetResult? targets = GetTarget(command, 2);
|
||||
if (targets == null || !targets.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
playersToTeleport = targets.Players
|
||||
.Where(p => p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.Connected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE } && caller!.CanTarget(p))
|
||||
|
|
@ -504,15 +603,19 @@ public partial class CS2_SimpleAdmin
|
|||
}
|
||||
|
||||
if (destinationPlayer == null || !playersToTeleport.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Log command
|
||||
Helper.LogCommand(caller, command);
|
||||
|
||||
foreach (var player in playersToTeleport)
|
||||
foreach (CCSPlayerController player in playersToTeleport)
|
||||
{
|
||||
if (player.PlayerPawn?.Value == null || destinationPlayer.PlayerPawn?.Value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Teleport
|
||||
player.TeleportPlayer(destinationPlayer);
|
||||
|
|
@ -624,4 +727,4 @@ public partial class CS2_SimpleAdmin
|
|||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ public class Discord
|
|||
new() { Name = "Footer", Value = "" },
|
||||
new() { Name = "Time", Value = "{relative}" },
|
||||
];
|
||||
|
||||
|
||||
[JsonPropertyName("DiscordAssociatedAccountsSettings")]
|
||||
public DiscordPenaltySetting[] DiscordAssociatedAccountsSettings { get; set; } =
|
||||
[
|
||||
|
|
@ -119,7 +119,13 @@ public class CustomServerCommandData
|
|||
|
||||
public class MenuConfig
|
||||
{
|
||||
[JsonPropertyName("MenuType")] public string MenuType { get; set; } = "selectable";
|
||||
[JsonPropertyName("MenuLayout")] public string MenuLayout { get; set; } = "uikit_menu";
|
||||
|
||||
[JsonPropertyName("VoteLayout")] public string VoteLayout { get; set; } = "uikit_vote";
|
||||
|
||||
[JsonPropertyName("BannerLayout")] public string BannerLayout { get; set; } = "uikit_banner";
|
||||
|
||||
[JsonPropertyName("ToastLayout")] public string ToastLayout { get; set; } = "uikit_toast";
|
||||
|
||||
[JsonPropertyName("Durations")]
|
||||
public DurationItem[] Durations { get; set; } =
|
||||
|
|
@ -214,7 +220,7 @@ public class OtherSettings
|
|||
|
||||
[JsonPropertyName("MaxBanDuration")]
|
||||
public int MaxBanDuration { get; set; } = 60 * 24 * 7;
|
||||
|
||||
|
||||
[JsonPropertyName("MaxMuteDuration")]
|
||||
public int MaxMuteDuration { get; set; } = 60 * 24 * 7;
|
||||
|
||||
|
|
@ -226,16 +232,16 @@ public class OtherSettings
|
|||
|
||||
[JsonPropertyName("DisconnectedPlayersHistoryCount")]
|
||||
public int DisconnectedPlayersHistoryCount { get; set; } = 10;
|
||||
|
||||
|
||||
[JsonPropertyName("NotifyPenaltiesToAdminOnConnect")]
|
||||
public bool NotifyPenaltiesToAdminOnConnect { get; set; } = true;
|
||||
|
||||
|
||||
[JsonPropertyName("ShowBanMenuIfNoTime")]
|
||||
public bool ShowBanMenuIfNoTime { get; set; } = true;
|
||||
|
||||
|
||||
[JsonPropertyName("UserMessageGagChatType")]
|
||||
public bool UserMessageGagChatType { get; set; } = false;
|
||||
|
||||
|
||||
[JsonPropertyName("CheckMultiAccountsByIp")]
|
||||
public bool CheckMultiAccountsByIp { get; set; } = true;
|
||||
|
||||
|
|
@ -247,11 +253,11 @@ public class OtherSettings
|
|||
|
||||
public class CS2_SimpleAdminConfig : BasePluginConfig
|
||||
{
|
||||
[JsonPropertyName("ConfigVersion")] public override int Version { get; set; } = 25;
|
||||
[JsonPropertyName("ConfigVersion")] public override int Version { get; set; } = 26;
|
||||
|
||||
[JsonPropertyName("DatabaseConfig")]
|
||||
[JsonPropertyName("DatabaseConfig")]
|
||||
public DatabaseConfig DatabaseConfig { get; set; } = new();
|
||||
|
||||
|
||||
[JsonPropertyName("OtherSettings")]
|
||||
public OtherSettings OtherSettings { get; set; } = new();
|
||||
|
||||
|
|
@ -295,7 +301,7 @@ public class DatabaseConfig
|
|||
{
|
||||
[JsonPropertyName("DatabaseType")]
|
||||
public string DatabaseType { get; set; } = "SQLite";
|
||||
|
||||
|
||||
[JsonPropertyName("SqliteFilePath")]
|
||||
public string SqliteFilePath { get; set; } = "cs2-simpleadmin.sqlite";
|
||||
|
||||
|
|
@ -313,7 +319,7 @@ public class DatabaseConfig
|
|||
|
||||
[JsonPropertyName("DatabaseName")]
|
||||
public string DatabaseName { get; set; } = "";
|
||||
|
||||
|
||||
[JsonPropertyName("DatabaseSSlMode")]
|
||||
public string DatabaseSSlMode { get; set; } = "preferred";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@ public class Database(string dbConnectionString)
|
|||
{
|
||||
try
|
||||
{
|
||||
var connection = new MySqlConnection(dbConnectionString);
|
||||
MySqlConnection connection = new MySqlConnection(dbConnectionString);
|
||||
connection.Open();
|
||||
|
||||
|
||||
// using var cmd = connection.CreateCommand();
|
||||
// cmd.CommandText = "SET NAMES 'utf8mb4' COLLATE 'utf8mb4_general_ci';";
|
||||
// cmd.ExecuteNonQueryAsync();
|
||||
|
||||
|
||||
return connection;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -29,13 +29,13 @@ public class Database(string dbConnectionString)
|
|||
{
|
||||
try
|
||||
{
|
||||
var connection = new MySqlConnection(dbConnectionString);
|
||||
MySqlConnection connection = new MySqlConnection(dbConnectionString);
|
||||
await connection.OpenAsync();
|
||||
|
||||
// await using var cmd = connection.CreateCommand();
|
||||
// cmd.CommandText = "SET NAMES 'utf8mb4' COLLATE 'utf8mb4_general_ci';";
|
||||
// await cmd.ExecuteNonQueryAsync();
|
||||
|
||||
|
||||
return connection;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -53,9 +53,9 @@ public class Database(string dbConnectionString)
|
|||
|
||||
public bool CheckDatabaseConnection(out string? exception)
|
||||
{
|
||||
using var connection = GetConnection();
|
||||
using MySqlConnection connection = GetConnection();
|
||||
exception = null;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
return connection.Ping();
|
||||
|
|
@ -66,4 +66,4 @@ public class Database(string dbConnectionString)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ public interface IDatabaseProvider
|
|||
Task<DbConnection> CreateConnectionAsync();
|
||||
Task<(bool Success, string? Exception)> CheckConnectionAsync();
|
||||
Task DatabaseMigrationAsync();
|
||||
|
||||
|
||||
// CacheManager
|
||||
string GetBanSelectQuery(bool multiServer);
|
||||
string GetIpHistoryQuery();
|
||||
|
|
@ -15,7 +15,7 @@ public interface IDatabaseProvider
|
|||
|
||||
// PlayerManager
|
||||
string GetUpsertPlayerIpQuery();
|
||||
|
||||
|
||||
// PermissionManager
|
||||
string GetAdminsQuery();
|
||||
string GetDeleteAdminQuery(bool globalDelete);
|
||||
|
|
@ -29,7 +29,7 @@ public interface IDatabaseProvider
|
|||
string GetAddGroupServerQuery();
|
||||
string GetDeleteGroupQuery();
|
||||
string GetDeleteOldAdminsQuery();
|
||||
|
||||
|
||||
// BanManager
|
||||
string GetAddBanQuery();
|
||||
string GetAddBanBySteamIdQuery();
|
||||
|
|
@ -58,7 +58,7 @@ public interface IDatabaseProvider
|
|||
string GetInsertUnmuteQuery(bool includeReason);
|
||||
string GetUpdateMuteStatusQuery();
|
||||
string GetExpireMutesQuery(bool multiServer, int timeMode);
|
||||
|
||||
|
||||
// WarnManager
|
||||
string GetAddWarnQuery(bool includePlayerName);
|
||||
string GetPlayerWarnsQuery(bool multiServer, bool active);
|
||||
|
|
@ -69,4 +69,4 @@ public interface IDatabaseProvider
|
|||
|
||||
// Penalty history (css_history)
|
||||
string GetPenaltyHistoryQuery(bool multiServer);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,19 @@ public class Migration(string migrationsPath)
|
|||
/// </summary>
|
||||
public async Task ExecuteMigrationsAsync()
|
||||
{
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null) return;
|
||||
var files = Directory.GetFiles(migrationsPath, "*.sql").OrderBy(f => f).ToList();
|
||||
if (files.Count == 0) return;
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
await using (var cmd = connection.CreateCommand())
|
||||
List<string> files = Directory.GetFiles(migrationsPath, "*.sql").OrderBy(f => f).ToList();
|
||||
if (files.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await using DbConnection connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
await using (DbCommand cmd = connection.CreateCommand())
|
||||
{
|
||||
if (migrationsPath.Contains("sqlite", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
|
|
@ -42,19 +49,21 @@ public class Migration(string migrationsPath)
|
|||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
var lastAppliedVersion = await GetLastAppliedVersionAsync(connection);
|
||||
string lastAppliedVersion = await GetLastAppliedVersionAsync(connection);
|
||||
|
||||
foreach (var file in files)
|
||||
foreach (string file in files)
|
||||
{
|
||||
var version = Path.GetFileNameWithoutExtension(file);
|
||||
string version = Path.GetFileNameWithoutExtension(file);
|
||||
if (string.Compare(version, lastAppliedVersion, StringComparison.OrdinalIgnoreCase) <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var sqlScript = await File.ReadAllTextAsync(file);
|
||||
string sqlScript = await File.ReadAllTextAsync(file);
|
||||
|
||||
await using (var cmdMigration = connection.CreateCommand())
|
||||
await using (DbCommand cmdMigration = connection.CreateCommand())
|
||||
{
|
||||
cmdMigration.CommandText = sqlScript;
|
||||
await cmdMigration.ExecuteNonQueryAsync();
|
||||
|
|
@ -79,9 +88,9 @@ public class Migration(string migrationsPath)
|
|||
/// <returns>The version string of the last applied migration, or empty string if none.</returns>
|
||||
private static async Task<string> GetLastAppliedVersionAsync(DbConnection connection)
|
||||
{
|
||||
await using var cmd = connection.CreateCommand();
|
||||
await using DbCommand cmd = connection.CreateCommand();
|
||||
cmd.CommandText = "SELECT version FROM sa_migrations ORDER BY id DESC LIMIT 1;";
|
||||
var result = await cmd.ExecuteScalarAsync();
|
||||
object? result = await cmd.ExecuteScalarAsync();
|
||||
return result?.ToString() ?? string.Empty;
|
||||
}
|
||||
|
||||
|
|
@ -92,10 +101,10 @@ public class Migration(string migrationsPath)
|
|||
/// <param name="version">The version string of the migration applied.</param>
|
||||
private static async Task UpdateLastAppliedVersionAsync(DbConnection connection, string version)
|
||||
{
|
||||
await using var cmd = connection.CreateCommand();
|
||||
await using DbCommand cmd = connection.CreateCommand();
|
||||
cmd.CommandText = "INSERT INTO sa_migrations (version) VALUES (@Version);";
|
||||
|
||||
var param = cmd.CreateParameter();
|
||||
DbParameter param = cmd.CreateParameter();
|
||||
param.ParameterName = "@Version";
|
||||
param.Value = version;
|
||||
cmd.Parameters.Add(param);
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@ public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider
|
|||
{
|
||||
public async Task<DbConnection> CreateConnectionAsync()
|
||||
{
|
||||
var connection = new MySqlConnection(connectionString);
|
||||
MySqlConnection connection = new MySqlConnection(connectionString);
|
||||
await connection.OpenAsync();
|
||||
|
||||
await using var cmd = connection.CreateCommand();
|
||||
|
||||
|
||||
await using MySqlCommand cmd = connection.CreateCommand();
|
||||
|
||||
cmd.CommandText = "SET NAMES 'utf8mb4' COLLATE 'utf8mb4_general_ci';";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
|
||||
|
||||
// cmd.CommandText = "SET time_zone = '+00:00';";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider
|
|||
{
|
||||
try
|
||||
{
|
||||
await using var conn = await CreateConnectionAsync();
|
||||
await using DbConnection conn = await CreateConnectionAsync();
|
||||
return (true, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -33,52 +33,62 @@ public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider
|
|||
return (false, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Task DatabaseMigrationAsync()
|
||||
{
|
||||
var migration = new Migration(CS2_SimpleAdmin.Instance.ModuleDirectory + "/Database/Migrations/Mysql");
|
||||
Migration migration = new Migration(CS2_SimpleAdmin.Instance.ModuleDirectory + "/Database/Migrations/Mysql");
|
||||
return migration.ExecuteMigrationsAsync();
|
||||
}
|
||||
|
||||
public string GetBanSelectQuery(bool multiServer)
|
||||
{
|
||||
return multiServer ? """
|
||||
SELECT
|
||||
id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
""" : """
|
||||
SELECT
|
||||
id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
WHERE server_id = @serverId
|
||||
""";
|
||||
}
|
||||
|
||||
if (multiServer)
|
||||
{
|
||||
return """
|
||||
SELECT
|
||||
id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
""";
|
||||
}
|
||||
|
||||
return """
|
||||
SELECT
|
||||
id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
WHERE server_id = @serverId
|
||||
""";
|
||||
}
|
||||
|
||||
public static string GetBanUpdatedSelectQuery(bool multiServer)
|
||||
{
|
||||
return multiServer ? """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM `sa_bans` WHERE updated_at > @lastUpdate OR created > @lastUpdate ORDER BY updated_at DESC
|
||||
""" : """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM `sa_bans` WHERE (updated_at > @lastUpdate OR created > @lastUpdate) AND server_id = @serverId ORDER BY updated_at DESC
|
||||
""";
|
||||
if (multiServer)
|
||||
{
|
||||
return """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM `sa_bans` WHERE updated_at > @lastUpdate OR created > @lastUpdate ORDER BY updated_at DESC
|
||||
""";
|
||||
}
|
||||
|
||||
return """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM `sa_bans` WHERE (updated_at > @lastUpdate OR created > @lastUpdate) AND server_id = @serverId ORDER BY updated_at DESC
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetIpHistoryQuery()
|
||||
|
|
@ -99,25 +109,30 @@ public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider
|
|||
|
||||
public string GetBanUpdateQuery(bool multiServer)
|
||||
{
|
||||
return multiServer ? """
|
||||
UPDATE sa_bans
|
||||
SET
|
||||
player_ip = COALESCE(player_ip, @PlayerIP),
|
||||
player_name = COALESCE(player_name, @PlayerName)
|
||||
WHERE
|
||||
(player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
AND status = 'ACTIVE'
|
||||
AND (duration = 0 OR ends > @CurrentTime)
|
||||
""" : """
|
||||
UPDATE sa_bans
|
||||
SET
|
||||
player_ip = COALESCE(player_ip, @PlayerIP),
|
||||
player_name = COALESCE(player_name, @PlayerName)
|
||||
WHERE
|
||||
(player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
AND status = 'ACTIVE'
|
||||
AND (duration = 0 OR ends > @CurrentTime) AND server_id = @ServerId
|
||||
""";
|
||||
if (multiServer)
|
||||
{
|
||||
return """
|
||||
UPDATE sa_bans
|
||||
SET
|
||||
player_ip = COALESCE(player_ip, @PlayerIP),
|
||||
player_name = COALESCE(player_name, @PlayerName)
|
||||
WHERE
|
||||
(player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
AND status = 'ACTIVE'
|
||||
AND (duration = 0 OR ends > @CurrentTime)
|
||||
""";
|
||||
}
|
||||
|
||||
return """
|
||||
UPDATE sa_bans
|
||||
SET
|
||||
player_ip = COALESCE(player_ip, @PlayerIP),
|
||||
player_name = COALESCE(player_name, @PlayerName)
|
||||
WHERE
|
||||
(player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
AND status = 'ACTIVE'
|
||||
AND (duration = 0 OR ends > @CurrentTime) AND server_id = @ServerId
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetAdminsQuery()
|
||||
|
|
@ -131,12 +146,17 @@ public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider
|
|||
ORDER BY sa_admins.player_steamid
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetDeleteAdminQuery(bool globalDelete) =>
|
||||
globalDelete
|
||||
? "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID"
|
||||
: "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId";
|
||||
|
||||
|
||||
public string GetDeleteAdminQuery(bool globalDelete)
|
||||
{
|
||||
if (globalDelete)
|
||||
{
|
||||
return "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID";
|
||||
}
|
||||
|
||||
return "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId";
|
||||
}
|
||||
|
||||
public string GetAddAdminQuery() =>
|
||||
"INSERT INTO sa_admins (player_steamid, player_name, immunity, ends, created, server_id) " +
|
||||
"VALUES (@playerSteamId, @playerName, @immunity, @ends, @created, @serverid); SELECT LAST_INSERT_ID();";
|
||||
|
|
@ -160,7 +180,7 @@ public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider
|
|||
|
||||
public string GetAddGroupQuery() =>
|
||||
"INSERT INTO sa_groups (name, immunity) VALUES (@groupName, @immunity); SELECT LAST_INSERT_ID();";
|
||||
|
||||
|
||||
public string GetGroupIdByNameQuery() =>
|
||||
"""
|
||||
SELECT sgs.group_id
|
||||
|
|
@ -181,7 +201,7 @@ public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider
|
|||
|
||||
public string GetDeleteOldAdminsQuery() =>
|
||||
"DELETE FROM sa_admins WHERE ends IS NOT NULL AND ends <= @CurrentTime;";
|
||||
|
||||
|
||||
public string GetAddBanQuery()
|
||||
{
|
||||
return """
|
||||
|
|
@ -192,7 +212,7 @@ public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider
|
|||
SELECT LAST_INSERT_ID();
|
||||
""";
|
||||
}
|
||||
|
||||
|
||||
public string GetAddBanBySteamIdQuery()
|
||||
{
|
||||
return """
|
||||
|
|
@ -213,12 +233,15 @@ public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider
|
|||
(@playerIp, @adminSteamid, @adminName, @banReason, @duration, @ends, @created, @serverid);
|
||||
""";
|
||||
}
|
||||
|
||||
|
||||
public string GetUnbanRetrieveBansQuery(bool multiServer)
|
||||
{
|
||||
return multiServer
|
||||
? "SELECT id, player_steamid FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE'"
|
||||
: "SELECT id, player_steamid FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
if (multiServer)
|
||||
{
|
||||
return "SELECT id, player_steamid FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE'";
|
||||
}
|
||||
|
||||
return "SELECT id, player_steamid FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetUnbanAdminIdQuery()
|
||||
|
|
@ -228,9 +251,12 @@ public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider
|
|||
|
||||
public string GetInsertUnbanQuery(bool includeReason)
|
||||
{
|
||||
return includeReason
|
||||
? "INSERT INTO sa_unbans (ban_id, admin_id, reason) VALUES (@banId, @adminId, @reason); SELECT LAST_INSERT_ID();"
|
||||
: "INSERT INTO sa_unbans (ban_id, admin_id) VALUES (@banId, @adminId); SELECT LAST_INSERT_ID();";
|
||||
if (includeReason)
|
||||
{
|
||||
return "INSERT INTO sa_unbans (ban_id, admin_id, reason) VALUES (@banId, @adminId, @reason); SELECT LAST_INSERT_ID();";
|
||||
}
|
||||
|
||||
return "INSERT INTO sa_unbans (ban_id, admin_id) VALUES (@banId, @adminId); SELECT LAST_INSERT_ID();";
|
||||
}
|
||||
|
||||
public string GetUpdateBanStatusQuery()
|
||||
|
|
@ -249,145 +275,245 @@ public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider
|
|||
|
||||
public string GetExpireBansQuery(bool multiServer)
|
||||
{
|
||||
return multiServer
|
||||
? "UPDATE sa_bans SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @currentTime"
|
||||
: "UPDATE sa_bans SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @currentTime AND server_id = @serverid";
|
||||
if (multiServer)
|
||||
{
|
||||
return "UPDATE sa_bans SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @currentTime";
|
||||
}
|
||||
|
||||
return "UPDATE sa_bans SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @currentTime AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetExpireIpBansQuery(bool multiServer)
|
||||
{
|
||||
return multiServer
|
||||
? "UPDATE sa_bans SET player_ip = NULL WHERE status = 'ACTIVE' AND ends <= @ipBansTime"
|
||||
: "UPDATE sa_bans SET player_ip = NULL WHERE status = 'ACTIVE' AND ends <= @ipBansTime AND server_id = @serverid";
|
||||
if (multiServer)
|
||||
{
|
||||
return "UPDATE sa_bans SET player_ip = NULL WHERE status = 'ACTIVE' AND ends <= @ipBansTime";
|
||||
}
|
||||
|
||||
return "UPDATE sa_bans SET player_ip = NULL WHERE status = 'ACTIVE' AND ends <= @ipBansTime AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetExpireOldPlayerIpsQuery()
|
||||
{
|
||||
return "DELETE FROM sa_players_ips WHERE used_at <= @ipBansTime";
|
||||
}
|
||||
|
||||
public string GetAddMuteQuery(bool includePlayerName) =>
|
||||
includePlayerName
|
||||
? """
|
||||
|
||||
public string GetAddMuteQuery(bool includePlayerName)
|
||||
{
|
||||
if (includePlayerName)
|
||||
{
|
||||
return """
|
||||
INSERT INTO `sa_mutes`
|
||||
(`player_steamid`, `player_name`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `type`, `server_id`)
|
||||
VALUES (@playerSteamid, @playerName, @adminSteamid, @adminName, @muteReason, @duration, @ends, @created, @type, @serverid);
|
||||
SELECT LAST_INSERT_ID();
|
||||
"""
|
||||
: """
|
||||
INSERT INTO `sa_mutes`
|
||||
(`player_steamid`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `type`, `server_id`)
|
||||
VALUES (@playerSteamid, @adminSteamid, @adminName, @muteReason, @duration, @ends, @created, @type, @serverid);
|
||||
SELECT LAST_INSERT_ID();
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetIsMutedQuery(bool multiServer, int timeMode) =>
|
||||
multiServer
|
||||
? (timeMode == 1
|
||||
? "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime)"
|
||||
: "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR duration > COALESCE(passed, 0))")
|
||||
: (timeMode == 1
|
||||
? "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime) AND server_id = @serverid"
|
||||
: "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR duration > COALESCE(passed, 0)) AND server_id = @serverid");
|
||||
return """
|
||||
INSERT INTO `sa_mutes`
|
||||
(`player_steamid`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `type`, `server_id`)
|
||||
VALUES (@playerSteamid, @adminSteamid, @adminName, @muteReason, @duration, @ends, @created, @type, @serverid);
|
||||
SELECT LAST_INSERT_ID();
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetMuteStatsQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? """
|
||||
public string GetIsMutedQuery(bool multiServer, int timeMode)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
if (timeMode == 1)
|
||||
{
|
||||
return "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime)";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR duration > COALESCE(passed, 0))";
|
||||
}
|
||||
}
|
||||
|
||||
if (timeMode == 1)
|
||||
{
|
||||
return "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime) AND server_id = @serverid";
|
||||
}
|
||||
|
||||
return "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR duration > COALESCE(passed, 0)) AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetMuteStatsQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return """
|
||||
SELECT
|
||||
COUNT(CASE WHEN type = 'MUTE' THEN 1 END) AS TotalMutes,
|
||||
COUNT(CASE WHEN type = 'GAG' THEN 1 END) AS TotalGags,
|
||||
COUNT(CASE WHEN type = 'SILENCE' THEN 1 END) AS TotalSilences
|
||||
FROM sa_mutes
|
||||
WHERE player_steamid = @PlayerSteamID;
|
||||
"""
|
||||
: """
|
||||
SELECT
|
||||
COUNT(CASE WHEN type = 'MUTE' THEN 1 END) AS TotalMutes,
|
||||
COUNT(CASE WHEN type = 'GAG' THEN 1 END) AS TotalGags,
|
||||
COUNT(CASE WHEN type = 'SILENCE' THEN 1 END) AS TotalSilences
|
||||
FROM sa_mutes
|
||||
WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId;
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetUpdateMutePassedQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE `sa_mutes` SET passed = COALESCE(passed, 0) + 1 WHERE (player_steamid = @PlayerSteamID) AND duration > 0 AND status = 'ACTIVE'"
|
||||
: "UPDATE `sa_mutes` SET passed = COALESCE(passed, 0) + 1 WHERE (player_steamid = @PlayerSteamID) AND duration > 0 AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
return """
|
||||
SELECT
|
||||
COUNT(CASE WHEN type = 'MUTE' THEN 1 END) AS TotalMutes,
|
||||
COUNT(CASE WHEN type = 'GAG' THEN 1 END) AS TotalGags,
|
||||
COUNT(CASE WHEN type = 'SILENCE' THEN 1 END) AS TotalSilences
|
||||
FROM sa_mutes
|
||||
WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId;
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetCheckExpiredMutesQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "SELECT * FROM `sa_mutes` WHERE player_steamid = @PlayerSteamID AND passed >= duration AND duration > 0 AND status = 'ACTIVE'"
|
||||
: "SELECT * FROM `sa_mutes` WHERE player_steamid = @PlayerSteamID AND passed >= duration AND duration > 0 AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
public string GetUpdateMutePassedQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return "UPDATE `sa_mutes` SET passed = COALESCE(passed, 0) + 1 WHERE (player_steamid = @PlayerSteamID) AND duration > 0 AND status = 'ACTIVE'";
|
||||
}
|
||||
|
||||
public string GetRetrieveMutesQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "SELECT id FROM sa_mutes WHERE (player_steamid = @pattern OR player_name = @pattern) AND type = @muteType AND status = 'ACTIVE'"
|
||||
: "SELECT id FROM sa_mutes WHERE (player_steamid = @pattern OR player_name = @pattern) AND type = @muteType AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
return "UPDATE `sa_mutes` SET passed = COALESCE(passed, 0) + 1 WHERE (player_steamid = @PlayerSteamID) AND duration > 0 AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetCheckExpiredMutesQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return "SELECT * FROM `sa_mutes` WHERE player_steamid = @PlayerSteamID AND passed >= duration AND duration > 0 AND status = 'ACTIVE'";
|
||||
}
|
||||
|
||||
return "SELECT * FROM `sa_mutes` WHERE player_steamid = @PlayerSteamID AND passed >= duration AND duration > 0 AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetRetrieveMutesQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return "SELECT id FROM sa_mutes WHERE (player_steamid = @pattern OR player_name = @pattern) AND type = @muteType AND status = 'ACTIVE'";
|
||||
}
|
||||
|
||||
return "SELECT id FROM sa_mutes WHERE (player_steamid = @pattern OR player_name = @pattern) AND type = @muteType AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetUnmuteAdminIdQuery() =>
|
||||
"SELECT id FROM sa_admins WHERE player_steamid = @adminSteamId";
|
||||
|
||||
public string GetInsertUnmuteQuery(bool includeReason) =>
|
||||
includeReason
|
||||
? "INSERT INTO sa_unmutes (mute_id, admin_id, reason) VALUES (@muteId, @adminId, @reason); SELECT LAST_INSERT_ID();"
|
||||
: "INSERT INTO sa_unmutes (mute_id, admin_id) VALUES (@muteId, @adminId); SELECT LAST_INSERT_ID();";
|
||||
public string GetInsertUnmuteQuery(bool includeReason)
|
||||
{
|
||||
if (includeReason)
|
||||
{
|
||||
return "INSERT INTO sa_unmutes (mute_id, admin_id, reason) VALUES (@muteId, @adminId, @reason); SELECT LAST_INSERT_ID();";
|
||||
}
|
||||
|
||||
return "INSERT INTO sa_unmutes (mute_id, admin_id) VALUES (@muteId, @adminId); SELECT LAST_INSERT_ID();";
|
||||
}
|
||||
|
||||
public string GetUpdateMuteStatusQuery() =>
|
||||
"UPDATE sa_mutes SET status = 'UNMUTED', unmute_id = @unmuteId WHERE id = @muteId";
|
||||
|
||||
public string GetExpireMutesQuery(bool multiServer, int timeMode) =>
|
||||
multiServer
|
||||
? (timeMode == 1
|
||||
? "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime"
|
||||
: "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND `passed` >= `duration`")
|
||||
: (timeMode == 1
|
||||
? "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime AND server_id = @serverid"
|
||||
: "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND `passed` >= `duration` AND server_id = @serverid");
|
||||
|
||||
|
||||
public string GetAddWarnQuery(bool includePlayerName) =>
|
||||
includePlayerName
|
||||
? """
|
||||
public string GetExpireMutesQuery(bool multiServer, int timeMode)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
if (timeMode == 1)
|
||||
{
|
||||
return "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND `passed` >= `duration`";
|
||||
}
|
||||
}
|
||||
|
||||
if (timeMode == 1)
|
||||
{
|
||||
return "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime AND server_id = @serverid";
|
||||
}
|
||||
|
||||
return "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND `passed` >= `duration` AND server_id = @serverid";
|
||||
}
|
||||
|
||||
|
||||
public string GetAddWarnQuery(bool includePlayerName)
|
||||
{
|
||||
if (includePlayerName)
|
||||
{
|
||||
return """
|
||||
INSERT INTO `sa_warns`
|
||||
(`player_steamid`, `player_name`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `server_id`)
|
||||
VALUES
|
||||
(@playerSteamid, @playerName, @adminSteamid, @adminName, @warnReason, @duration, @ends, @created, @serverid);
|
||||
SELECT LAST_INSERT_ID();
|
||||
"""
|
||||
: """
|
||||
INSERT INTO `sa_warns`
|
||||
(`player_steamid`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `server_id`)
|
||||
VALUES
|
||||
(@playerSteamid, @adminSteamid, @adminName, @warnReason, @duration, @ends, @created, @serverid);
|
||||
SELECT LAST_INSERT_ID();
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetPlayerWarnsQuery(bool multiServer, bool active) =>
|
||||
multiServer
|
||||
? active
|
||||
? "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' ORDER BY id DESC"
|
||||
: "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID ORDER BY id DESC"
|
||||
: active
|
||||
? "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid AND status = 'ACTIVE' ORDER BY id DESC"
|
||||
: "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid ORDER BY id DESC";
|
||||
return """
|
||||
INSERT INTO `sa_warns`
|
||||
(`player_steamid`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `server_id`)
|
||||
VALUES
|
||||
(@playerSteamid, @adminSteamid, @adminName, @warnReason, @duration, @ends, @created, @serverid);
|
||||
SELECT LAST_INSERT_ID();
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetPlayerWarnsCountQuery(bool multiServer, bool active) =>
|
||||
multiServer
|
||||
? active
|
||||
? "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE'"
|
||||
: "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID"
|
||||
: active
|
||||
? "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid AND status = 'ACTIVE'"
|
||||
: "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid";
|
||||
public string GetPlayerWarnsQuery(bool multiServer, bool active)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
return "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' ORDER BY id DESC";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID ORDER BY id DESC";
|
||||
}
|
||||
}
|
||||
|
||||
public string GetUnwarnByIdQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND player_steamid = @steamid AND id = @warnId"
|
||||
: "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND player_steamid = @steamid AND id = @warnId AND server_id = @serverid";
|
||||
if (active)
|
||||
{
|
||||
return "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid AND status = 'ACTIVE' ORDER BY id DESC";
|
||||
}
|
||||
|
||||
public string GetUnwarnLastQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? """
|
||||
return "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid ORDER BY id DESC";
|
||||
}
|
||||
|
||||
public string GetPlayerWarnsCountQuery(bool multiServer, bool active)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
return "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE'";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID";
|
||||
}
|
||||
}
|
||||
|
||||
if (active)
|
||||
{
|
||||
return "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid AND status = 'ACTIVE'";
|
||||
}
|
||||
|
||||
return "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetUnwarnByIdQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND player_steamid = @steamid AND id = @warnId";
|
||||
}
|
||||
|
||||
return "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND player_steamid = @steamid AND id = @warnId AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetUnwarnLastQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return """
|
||||
UPDATE sa_warns
|
||||
JOIN (
|
||||
SELECT MAX(id) AS max_id
|
||||
|
|
@ -396,45 +522,66 @@ public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider
|
|||
) AS subquery ON sa_warns.id = subquery.max_id
|
||||
SET sa_warns.status = 'EXPIRED'
|
||||
WHERE sa_warns.status = 'ACTIVE' AND sa_warns.player_steamid = @steamid;
|
||||
"""
|
||||
: """
|
||||
UPDATE sa_warns
|
||||
JOIN (
|
||||
SELECT MAX(id) AS max_id
|
||||
FROM sa_warns
|
||||
WHERE player_steamid = @steamid AND status = 'ACTIVE' AND server_id = @serverid
|
||||
) AS subquery ON sa_warns.id = subquery.max_id
|
||||
SET sa_warns.status = 'EXPIRED'
|
||||
WHERE sa_warns.status = 'ACTIVE' AND sa_warns.player_steamid = @steamid AND sa_warns.server_id = @serverid;
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetExpireWarnsQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime"
|
||||
: "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime AND server_id = @serverid";
|
||||
return """
|
||||
UPDATE sa_warns
|
||||
JOIN (
|
||||
SELECT MAX(id) AS max_id
|
||||
FROM sa_warns
|
||||
WHERE player_steamid = @steamid AND status = 'ACTIVE' AND server_id = @serverid
|
||||
) AS subquery ON sa_warns.id = subquery.max_id
|
||||
SET sa_warns.status = 'EXPIRED'
|
||||
WHERE sa_warns.status = 'ACTIVE' AND sa_warns.player_steamid = @steamid AND sa_warns.server_id = @serverid;
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetPenaltyHistoryQuery(bool multiServer) =>
|
||||
$"""
|
||||
SELECT b.id, 'BAN' AS type, b.player_name, b.admin_name, b.reason, b.duration, b.created, b.ends, b.status,
|
||||
ub.reason AS lift_reason, ub.date AS lift_date, ua.player_name AS lift_admin
|
||||
FROM sa_bans b
|
||||
LEFT JOIN sa_unbans ub ON ub.id = b.unban_id
|
||||
LEFT JOIN sa_admins ua ON ua.id = ub.admin_id
|
||||
WHERE b.player_steamid = @PlayerSteamID {(multiServer ? "" : "AND b.server_id = @serverid")}
|
||||
UNION ALL
|
||||
SELECT m.id, m.type, m.player_name, m.admin_name, m.reason, m.duration, m.created, m.ends, m.status,
|
||||
um.reason AS lift_reason, um.date AS lift_date, ua.player_name AS lift_admin
|
||||
FROM sa_mutes m
|
||||
LEFT JOIN sa_unmutes um ON um.id = m.unmute_id
|
||||
LEFT JOIN sa_admins ua ON ua.id = um.admin_id
|
||||
WHERE m.player_steamid = @PlayerSteamID {(multiServer ? "" : "AND m.server_id = @serverid")}
|
||||
UNION ALL
|
||||
SELECT w.id, 'WARN' AS type, w.player_name, w.admin_name, w.reason, w.duration, w.created, w.ends, w.status,
|
||||
NULL AS lift_reason, NULL AS lift_date, NULL AS lift_admin
|
||||
FROM sa_warns w
|
||||
WHERE w.player_steamid = @PlayerSteamID {(multiServer ? "" : "AND w.server_id = @serverid")}
|
||||
ORDER BY created DESC
|
||||
""";
|
||||
public string GetExpireWarnsQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime";
|
||||
}
|
||||
|
||||
return "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetPenaltyHistoryQuery(bool multiServer)
|
||||
{
|
||||
string banServerFilter = string.Empty;
|
||||
string muteServerFilter = string.Empty;
|
||||
string warnServerFilter = string.Empty;
|
||||
|
||||
if (!multiServer)
|
||||
{
|
||||
banServerFilter = "AND b.server_id = @serverid";
|
||||
muteServerFilter = "AND m.server_id = @serverid";
|
||||
warnServerFilter = "AND w.server_id = @serverid";
|
||||
}
|
||||
|
||||
return $"""
|
||||
SELECT b.id, 'BAN' AS type, b.player_name, b.admin_name, b.reason, b.duration, b.created, b.ends, b.status,
|
||||
ub.reason AS lift_reason, ub.date AS lift_date, ua.player_name AS lift_admin
|
||||
FROM sa_bans b
|
||||
LEFT JOIN sa_unbans ub ON ub.id = b.unban_id
|
||||
LEFT JOIN sa_admins ua ON ua.id = ub.admin_id
|
||||
WHERE b.player_steamid = @PlayerSteamID {banServerFilter}
|
||||
UNION ALL
|
||||
SELECT m.id, m.type, m.player_name, m.admin_name, m.reason, m.duration, m.created, m.ends, m.status,
|
||||
um.reason AS lift_reason, um.date AS lift_date, ua.player_name AS lift_admin
|
||||
FROM sa_mutes m
|
||||
LEFT JOIN sa_unmutes um ON um.id = m.unmute_id
|
||||
LEFT JOIN sa_admins ua ON ua.id = um.admin_id
|
||||
WHERE m.player_steamid = @PlayerSteamID {muteServerFilter}
|
||||
UNION ALL
|
||||
SELECT w.id, 'WARN' AS type, w.player_name, w.admin_name, w.reason, w.duration, w.created, w.ends, w.status,
|
||||
NULL AS lift_reason, NULL AS lift_date, NULL AS lift_admin
|
||||
FROM sa_warns w
|
||||
WHERE w.player_steamid = @PlayerSteamID {warnServerFilter}
|
||||
ORDER BY created DESC
|
||||
""";
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ public class SqliteDatabaseProvider(string filePath) : IDatabaseProvider
|
|||
|
||||
public async Task<DbConnection> CreateConnectionAsync()
|
||||
{
|
||||
var conn = new SQLiteConnection(_connectionString);
|
||||
SQLiteConnection conn = new SQLiteConnection(_connectionString);
|
||||
await conn.OpenAsync();
|
||||
return conn;
|
||||
}
|
||||
|
|
@ -18,8 +18,8 @@ public class SqliteDatabaseProvider(string filePath) : IDatabaseProvider
|
|||
{
|
||||
try
|
||||
{
|
||||
await using var conn = await CreateConnectionAsync();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
await using DbConnection conn = await CreateConnectionAsync();
|
||||
await using DbCommand cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "SELECT 1";
|
||||
await cmd.ExecuteScalarAsync();
|
||||
return (true, null);
|
||||
|
|
@ -32,33 +32,40 @@ public class SqliteDatabaseProvider(string filePath) : IDatabaseProvider
|
|||
|
||||
public Task DatabaseMigrationAsync()
|
||||
{
|
||||
var migration = new Migration(CS2_SimpleAdmin.Instance.ModuleDirectory + "/Database/Migrations/Sqlite");
|
||||
Migration migration = new Migration(CS2_SimpleAdmin.Instance.ModuleDirectory + "/Database/Migrations/Sqlite");
|
||||
return migration.ExecuteMigrationsAsync();
|
||||
}
|
||||
|
||||
public string GetBanSelectQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? """
|
||||
public string GetBanSelectQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
"""
|
||||
: """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
WHERE server_id = @serverId
|
||||
""";
|
||||
}
|
||||
|
||||
public static string GetBanUpdatedSelectQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? """
|
||||
return """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
WHERE server_id = @serverId
|
||||
""";
|
||||
}
|
||||
|
||||
public static string GetBanUpdatedSelectQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
|
|
@ -67,18 +74,21 @@ public class SqliteDatabaseProvider(string filePath) : IDatabaseProvider
|
|||
FROM sa_bans
|
||||
WHERE updated_at > @lastUpdate OR created > @lastUpdate
|
||||
ORDER BY updated_at DESC
|
||||
"""
|
||||
: """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
WHERE (updated_at > @lastUpdate OR created > @lastUpdate)
|
||||
AND server_id = @serverId
|
||||
ORDER BY updated_at DESC
|
||||
""";
|
||||
}
|
||||
|
||||
return """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
WHERE (updated_at > @lastUpdate OR created > @lastUpdate)
|
||||
AND server_id = @serverId
|
||||
ORDER BY updated_at DESC
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetIpHistoryQuery() =>
|
||||
"SELECT steamid, name, address, used_at FROM sa_players_ips ORDER BY used_at DESC";
|
||||
|
|
@ -94,25 +104,30 @@ public class SqliteDatabaseProvider(string filePath) : IDatabaseProvider
|
|||
""";
|
||||
}
|
||||
|
||||
public string GetBanUpdateQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? """
|
||||
public string GetBanUpdateQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return """
|
||||
UPDATE sa_bans
|
||||
SET player_ip = COALESCE(player_ip, @PlayerIP),
|
||||
player_name = COALESCE(player_name, @PlayerName)
|
||||
WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
AND status = 'ACTIVE'
|
||||
AND (duration = 0 OR ends > @CurrentTime)
|
||||
"""
|
||||
: """
|
||||
UPDATE sa_bans
|
||||
SET player_ip = COALESCE(player_ip, @PlayerIP),
|
||||
player_name = COALESCE(player_name, @PlayerName)
|
||||
WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
AND status = 'ACTIVE'
|
||||
AND (duration = 0 OR ends > @CurrentTime)
|
||||
AND server_id = @ServerId
|
||||
""";
|
||||
}
|
||||
|
||||
return """
|
||||
UPDATE sa_bans
|
||||
SET player_ip = COALESCE(player_ip, @PlayerIP),
|
||||
player_name = COALESCE(player_name, @PlayerName)
|
||||
WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
AND status = 'ACTIVE'
|
||||
AND (duration = 0 OR ends > @CurrentTime)
|
||||
AND server_id = @ServerId
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetAddBanQuery() =>
|
||||
"""
|
||||
|
|
@ -141,32 +156,52 @@ public class SqliteDatabaseProvider(string filePath) : IDatabaseProvider
|
|||
SELECT last_insert_rowid();
|
||||
""";
|
||||
|
||||
public string GetUnbanRetrieveBansQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "SELECT id, player_steamid FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE'"
|
||||
: "SELECT id, player_steamid FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
public string GetUnbanRetrieveBansQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return "SELECT id, player_steamid FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE'";
|
||||
}
|
||||
|
||||
return "SELECT id, player_steamid FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetUnbanAdminIdQuery() =>
|
||||
"SELECT id FROM sa_admins WHERE player_steamid = @adminSteamId";
|
||||
|
||||
public string GetInsertUnbanQuery(bool includeReason) =>
|
||||
includeReason
|
||||
? "INSERT INTO sa_unbans (ban_id, admin_id, reason) VALUES (@banId, @adminId, @reason); SELECT last_insert_rowid();"
|
||||
: "INSERT INTO sa_unbans (ban_id, admin_id) VALUES (@banId, @adminId); SELECT last_insert_rowid();";
|
||||
public string GetInsertUnbanQuery(bool includeReason)
|
||||
{
|
||||
if (includeReason)
|
||||
{
|
||||
return "INSERT INTO sa_unbans (ban_id, admin_id, reason) VALUES (@banId, @adminId, @reason); SELECT last_insert_rowid();";
|
||||
}
|
||||
|
||||
return "INSERT INTO sa_unbans (ban_id, admin_id) VALUES (@banId, @adminId); SELECT last_insert_rowid();";
|
||||
}
|
||||
|
||||
// SQLite has no ON UPDATE CURRENT_TIMESTAMP, so updated_at must be set explicitly or the cache refresh never sees the change
|
||||
public string GetUpdateBanStatusQuery() =>
|
||||
"UPDATE sa_bans SET status = 'UNBANNED', unban_id = @unbanId, updated_at = CURRENT_TIMESTAMP WHERE id = @banId";
|
||||
|
||||
public string GetExpireBansQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE sa_bans SET status = 'EXPIRED', updated_at = CURRENT_TIMESTAMP WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @currentTime"
|
||||
: "UPDATE sa_bans SET status = 'EXPIRED', updated_at = CURRENT_TIMESTAMP WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @currentTime AND server_id = @serverid";
|
||||
public string GetExpireBansQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return "UPDATE sa_bans SET status = 'EXPIRED', updated_at = CURRENT_TIMESTAMP WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @currentTime";
|
||||
}
|
||||
|
||||
public string GetExpireIpBansQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE sa_bans SET player_ip = NULL WHERE status = 'ACTIVE' AND ends <= @ipBansTime"
|
||||
: "UPDATE sa_bans SET player_ip = NULL WHERE status = 'ACTIVE' AND ends <= @ipBansTime AND server_id = @serverid";
|
||||
return "UPDATE sa_bans SET status = 'EXPIRED', updated_at = CURRENT_TIMESTAMP WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @currentTime AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetExpireIpBansQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return "UPDATE sa_bans SET player_ip = NULL WHERE status = 'ACTIVE' AND ends <= @ipBansTime";
|
||||
}
|
||||
|
||||
return "UPDATE sa_bans SET player_ip = NULL WHERE status = 'ACTIVE' AND ends <= @ipBansTime AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetExpireOldPlayerIpsQuery() =>
|
||||
"DELETE FROM sa_players_ips WHERE used_at <= @ipBansTime";
|
||||
|
|
@ -190,10 +225,15 @@ public class SqliteDatabaseProvider(string filePath) : IDatabaseProvider
|
|||
ORDER BY sa_admins.player_steamid
|
||||
""";
|
||||
|
||||
public string GetDeleteAdminQuery(bool globalDelete) =>
|
||||
globalDelete
|
||||
? "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID"
|
||||
: "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId";
|
||||
public string GetDeleteAdminQuery(bool globalDelete)
|
||||
{
|
||||
if (globalDelete)
|
||||
{
|
||||
return "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID";
|
||||
}
|
||||
|
||||
return "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId";
|
||||
}
|
||||
|
||||
public string GetAddAdminQuery() =>
|
||||
"""
|
||||
|
|
@ -244,170 +284,285 @@ public class SqliteDatabaseProvider(string filePath) : IDatabaseProvider
|
|||
|
||||
public string GetDeleteOldAdminsQuery() =>
|
||||
"DELETE FROM sa_admins WHERE ends IS NOT NULL AND ends <= @CurrentTime;";
|
||||
|
||||
public string GetAddMuteQuery(bool includePlayerName) =>
|
||||
includePlayerName
|
||||
? """
|
||||
|
||||
public string GetAddMuteQuery(bool includePlayerName)
|
||||
{
|
||||
if (includePlayerName)
|
||||
{
|
||||
return """
|
||||
INSERT INTO sa_mutes
|
||||
(player_steamid, player_name, admin_steamid, admin_name, reason, duration, ends, created, type, server_id)
|
||||
VALUES (@playerSteamid, @playerName, @adminSteamid, @adminName, @muteReason, @duration, @ends, @created, @type, @serverid);
|
||||
SELECT last_insert_rowid();
|
||||
"""
|
||||
: """
|
||||
INSERT INTO sa_mutes
|
||||
(player_steamid, admin_steamid, admin_name, reason, duration, ends, created, type, server_id)
|
||||
VALUES (@playerSteamid, @adminSteamid, @adminName, @muteReason, @duration, @ends, @created, @type, @serverid);
|
||||
SELECT last_insert_rowid();
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetIsMutedQuery(bool multiServer, int timeMode) =>
|
||||
multiServer
|
||||
? (timeMode == 1
|
||||
? "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime)"
|
||||
: "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR duration > COALESCE(passed, 0))")
|
||||
: (timeMode == 1
|
||||
? "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime) AND server_id = @serverid"
|
||||
: "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR duration > COALESCE(passed, 0)) AND server_id = @serverid");
|
||||
return """
|
||||
INSERT INTO sa_mutes
|
||||
(player_steamid, admin_steamid, admin_name, reason, duration, ends, created, type, server_id)
|
||||
VALUES (@playerSteamid, @adminSteamid, @adminName, @muteReason, @duration, @ends, @created, @type, @serverid);
|
||||
SELECT last_insert_rowid();
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetMuteStatsQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? """
|
||||
public string GetIsMutedQuery(bool multiServer, int timeMode)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
if (timeMode == 1)
|
||||
{
|
||||
return "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime)";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR duration > COALESCE(passed, 0))";
|
||||
}
|
||||
}
|
||||
|
||||
if (timeMode == 1)
|
||||
{
|
||||
return "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime) AND server_id = @serverid";
|
||||
}
|
||||
|
||||
return "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR duration > COALESCE(passed, 0)) AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetMuteStatsQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return """
|
||||
SELECT
|
||||
COUNT(CASE WHEN type = 'MUTE' THEN 1 END) AS TotalMutes,
|
||||
COUNT(CASE WHEN type = 'GAG' THEN 1 END) AS TotalGags,
|
||||
COUNT(CASE WHEN type = 'SILENCE' THEN 1 END) AS TotalSilences
|
||||
FROM sa_mutes
|
||||
WHERE player_steamid = @PlayerSteamID;
|
||||
"""
|
||||
: """
|
||||
SELECT
|
||||
COUNT(CASE WHEN type = 'MUTE' THEN 1 END) AS TotalMutes,
|
||||
COUNT(CASE WHEN type = 'GAG' THEN 1 END) AS TotalGags,
|
||||
COUNT(CASE WHEN type = 'SILENCE' THEN 1 END) AS TotalSilences
|
||||
FROM sa_mutes
|
||||
WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId;
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetUpdateMutePassedQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE sa_mutes SET passed = COALESCE(passed, 0) + 1 WHERE (player_steamid = @PlayerSteamID) AND duration > 0 AND status = 'ACTIVE'"
|
||||
: "UPDATE sa_mutes SET passed = COALESCE(passed, 0) + 1 WHERE (player_steamid = @PlayerSteamID) AND duration > 0 AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
return """
|
||||
SELECT
|
||||
COUNT(CASE WHEN type = 'MUTE' THEN 1 END) AS TotalMutes,
|
||||
COUNT(CASE WHEN type = 'GAG' THEN 1 END) AS TotalGags,
|
||||
COUNT(CASE WHEN type = 'SILENCE' THEN 1 END) AS TotalSilences
|
||||
FROM sa_mutes
|
||||
WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId;
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetCheckExpiredMutesQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND passed >= duration AND duration > 0 AND status = 'ACTIVE'"
|
||||
: "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND passed >= duration AND duration > 0 AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
public string GetUpdateMutePassedQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return "UPDATE sa_mutes SET passed = COALESCE(passed, 0) + 1 WHERE (player_steamid = @PlayerSteamID) AND duration > 0 AND status = 'ACTIVE'";
|
||||
}
|
||||
|
||||
public string GetRetrieveMutesQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "SELECT id FROM sa_mutes WHERE (player_steamid = @pattern OR player_name = @pattern) AND type = @muteType AND status = 'ACTIVE'"
|
||||
: "SELECT id FROM sa_mutes WHERE (player_steamid = @pattern OR player_name = @pattern) AND type = @muteType AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
return "UPDATE sa_mutes SET passed = COALESCE(passed, 0) + 1 WHERE (player_steamid = @PlayerSteamID) AND duration > 0 AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetCheckExpiredMutesQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND passed >= duration AND duration > 0 AND status = 'ACTIVE'";
|
||||
}
|
||||
|
||||
return "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND passed >= duration AND duration > 0 AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetRetrieveMutesQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return "SELECT id FROM sa_mutes WHERE (player_steamid = @pattern OR player_name = @pattern) AND type = @muteType AND status = 'ACTIVE'";
|
||||
}
|
||||
|
||||
return "SELECT id FROM sa_mutes WHERE (player_steamid = @pattern OR player_name = @pattern) AND type = @muteType AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetUnmuteAdminIdQuery() =>
|
||||
"SELECT id FROM sa_admins WHERE player_steamid = @adminSteamId";
|
||||
|
||||
public string GetInsertUnmuteQuery(bool includeReason) =>
|
||||
includeReason
|
||||
? "INSERT INTO sa_unmutes (mute_id, admin_id, reason) VALUES (@muteId, @adminId, @reason); SELECT last_insert_rowid();"
|
||||
: "INSERT INTO sa_unmutes (mute_id, admin_id) VALUES (@muteId, @adminId); SELECT last_insert_rowid();";
|
||||
public string GetInsertUnmuteQuery(bool includeReason)
|
||||
{
|
||||
if (includeReason)
|
||||
{
|
||||
return "INSERT INTO sa_unmutes (mute_id, admin_id, reason) VALUES (@muteId, @adminId, @reason); SELECT last_insert_rowid();";
|
||||
}
|
||||
|
||||
return "INSERT INTO sa_unmutes (mute_id, admin_id) VALUES (@muteId, @adminId); SELECT last_insert_rowid();";
|
||||
}
|
||||
|
||||
public string GetUpdateMuteStatusQuery() =>
|
||||
"UPDATE sa_mutes SET status = 'UNMUTED', unmute_id = @unmuteId WHERE id = @muteId";
|
||||
|
||||
public string GetExpireMutesQuery(bool multiServer, int timeMode) =>
|
||||
multiServer
|
||||
? (timeMode == 1
|
||||
? "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @CurrentTime"
|
||||
: "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND passed >= duration")
|
||||
: (timeMode == 1
|
||||
? "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @CurrentTime AND server_id = @serverid"
|
||||
: "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND passed >= duration AND server_id = @serverid");
|
||||
|
||||
public string GetAddWarnQuery(bool includePlayerName) =>
|
||||
includePlayerName
|
||||
? """
|
||||
public string GetExpireMutesQuery(bool multiServer, int timeMode)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
if (timeMode == 1)
|
||||
{
|
||||
return "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @CurrentTime";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND passed >= duration";
|
||||
}
|
||||
}
|
||||
|
||||
if (timeMode == 1)
|
||||
{
|
||||
return "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @CurrentTime AND server_id = @serverid";
|
||||
}
|
||||
|
||||
return "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND passed >= duration AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetAddWarnQuery(bool includePlayerName)
|
||||
{
|
||||
if (includePlayerName)
|
||||
{
|
||||
return """
|
||||
INSERT INTO sa_warns
|
||||
(player_steamid, player_name, admin_steamid, admin_name, reason, duration, ends, created, server_id)
|
||||
VALUES
|
||||
(@playerSteamid, @playerName, @adminSteamid, @adminName, @warnReason, @duration, @ends, @created, @serverid);
|
||||
SELECT last_insert_rowid();
|
||||
"""
|
||||
: """
|
||||
INSERT INTO sa_warns
|
||||
(player_steamid, admin_steamid, admin_name, reason, duration, ends, created, server_id)
|
||||
VALUES
|
||||
(@playerSteamid, @adminSteamid, @adminName, @warnReason, @duration, @ends, @created, @serverid);
|
||||
SELECT last_insert_rowid();
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetPlayerWarnsQuery(bool multiServer, bool active) =>
|
||||
multiServer
|
||||
? active
|
||||
? "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' ORDER BY id DESC"
|
||||
: "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID ORDER BY id DESC"
|
||||
: active
|
||||
? "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid AND status = 'ACTIVE' ORDER BY id DESC"
|
||||
: "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid ORDER BY id DESC";
|
||||
return """
|
||||
INSERT INTO sa_warns
|
||||
(player_steamid, admin_steamid, admin_name, reason, duration, ends, created, server_id)
|
||||
VALUES
|
||||
(@playerSteamid, @adminSteamid, @adminName, @warnReason, @duration, @ends, @created, @serverid);
|
||||
SELECT last_insert_rowid();
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetPlayerWarnsCountQuery(bool multiServer, bool active) =>
|
||||
multiServer
|
||||
? active
|
||||
? "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE'"
|
||||
: "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID"
|
||||
: active
|
||||
? "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid AND status = 'ACTIVE'"
|
||||
: "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid";
|
||||
public string GetPlayerWarnsQuery(bool multiServer, bool active)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
return "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' ORDER BY id DESC";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID ORDER BY id DESC";
|
||||
}
|
||||
}
|
||||
|
||||
public string GetUnwarnByIdQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND player_steamid = @steamid AND id = @warnId"
|
||||
: "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND player_steamid = @steamid AND id = @warnId AND server_id = @serverid";
|
||||
if (active)
|
||||
{
|
||||
return "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid AND status = 'ACTIVE' ORDER BY id DESC";
|
||||
}
|
||||
|
||||
public string GetUnwarnLastQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? """
|
||||
return "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid ORDER BY id DESC";
|
||||
}
|
||||
|
||||
public string GetPlayerWarnsCountQuery(bool multiServer, bool active)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
return "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE'";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID";
|
||||
}
|
||||
}
|
||||
|
||||
if (active)
|
||||
{
|
||||
return "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid AND status = 'ACTIVE'";
|
||||
}
|
||||
|
||||
return "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetUnwarnByIdQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND player_steamid = @steamid AND id = @warnId";
|
||||
}
|
||||
|
||||
return "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND player_steamid = @steamid AND id = @warnId AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetUnwarnLastQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return """
|
||||
UPDATE sa_warns
|
||||
SET status = 'EXPIRED'
|
||||
WHERE status = 'ACTIVE'
|
||||
AND player_steamid = @steamid
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
"""
|
||||
: """
|
||||
UPDATE sa_warns
|
||||
SET status = 'EXPIRED'
|
||||
WHERE status = 'ACTIVE'
|
||||
AND player_steamid = @steamid
|
||||
AND server_id = @serverid
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetExpireWarnsQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @CurrentTime"
|
||||
: "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @CurrentTime AND server_id = @serverid";
|
||||
return """
|
||||
UPDATE sa_warns
|
||||
SET status = 'EXPIRED'
|
||||
WHERE status = 'ACTIVE'
|
||||
AND player_steamid = @steamid
|
||||
AND server_id = @serverid
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetPenaltyHistoryQuery(bool multiServer) =>
|
||||
$"""
|
||||
SELECT b.id, 'BAN' AS type, b.player_name, b.admin_name, b.reason, b.duration, b.created, b.ends, b.status,
|
||||
ub.reason AS lift_reason, ub.date AS lift_date, ua.player_name AS lift_admin
|
||||
FROM sa_bans b
|
||||
LEFT JOIN sa_unbans ub ON ub.id = b.unban_id
|
||||
LEFT JOIN sa_admins ua ON ua.id = ub.admin_id
|
||||
WHERE b.player_steamid = @PlayerSteamID {(multiServer ? "" : "AND b.server_id = @serverid")}
|
||||
UNION ALL
|
||||
SELECT m.id, m.type, m.player_name, m.admin_name, m.reason, m.duration, m.created, m.ends, m.status,
|
||||
um.reason AS lift_reason, um.date AS lift_date, ua.player_name AS lift_admin
|
||||
FROM sa_mutes m
|
||||
LEFT JOIN sa_unmutes um ON um.id = m.unmute_id
|
||||
LEFT JOIN sa_admins ua ON ua.id = um.admin_id
|
||||
WHERE m.player_steamid = @PlayerSteamID {(multiServer ? "" : "AND m.server_id = @serverid")}
|
||||
UNION ALL
|
||||
SELECT w.id, 'WARN' AS type, w.player_name, w.admin_name, w.reason, w.duration, w.created, w.ends, w.status,
|
||||
NULL AS lift_reason, NULL AS lift_date, NULL AS lift_admin
|
||||
FROM sa_warns w
|
||||
WHERE w.player_steamid = @PlayerSteamID {(multiServer ? "" : "AND w.server_id = @serverid")}
|
||||
ORDER BY created DESC
|
||||
""";
|
||||
public string GetExpireWarnsQuery(bool multiServer)
|
||||
{
|
||||
if (multiServer)
|
||||
{
|
||||
return "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @CurrentTime";
|
||||
}
|
||||
|
||||
return "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @CurrentTime AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetPenaltyHistoryQuery(bool multiServer)
|
||||
{
|
||||
string banServerFilter = string.Empty;
|
||||
string muteServerFilter = string.Empty;
|
||||
string warnServerFilter = string.Empty;
|
||||
|
||||
if (!multiServer)
|
||||
{
|
||||
banServerFilter = "AND b.server_id = @serverid";
|
||||
muteServerFilter = "AND m.server_id = @serverid";
|
||||
warnServerFilter = "AND w.server_id = @serverid";
|
||||
}
|
||||
|
||||
return $"""
|
||||
SELECT b.id, 'BAN' AS type, b.player_name, b.admin_name, b.reason, b.duration, b.created, b.ends, b.status,
|
||||
ub.reason AS lift_reason, ub.date AS lift_date, ua.player_name AS lift_admin
|
||||
FROM sa_bans b
|
||||
LEFT JOIN sa_unbans ub ON ub.id = b.unban_id
|
||||
LEFT JOIN sa_admins ua ON ua.id = ub.admin_id
|
||||
WHERE b.player_steamid = @PlayerSteamID {banServerFilter}
|
||||
UNION ALL
|
||||
SELECT m.id, m.type, m.player_name, m.admin_name, m.reason, m.duration, m.created, m.ends, m.status,
|
||||
um.reason AS lift_reason, um.date AS lift_date, ua.player_name AS lift_admin
|
||||
FROM sa_mutes m
|
||||
LEFT JOIN sa_unmutes um ON um.id = m.unmute_id
|
||||
LEFT JOIN sa_admins ua ON ua.id = um.admin_id
|
||||
WHERE m.player_steamid = @PlayerSteamID {muteServerFilter}
|
||||
UNION ALL
|
||||
SELECT w.id, 'WARN' AS type, w.player_name, w.admin_name, w.reason, w.duration, w.created, w.ends, w.status,
|
||||
NULL AS lift_reason, NULL AS lift_date, NULL AS lift_admin
|
||||
FROM sa_warns w
|
||||
WHERE w.player_steamid = @PlayerSteamID {warnServerFilter}
|
||||
ORDER BY created DESC
|
||||
""";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,4 +9,4 @@ public static class EnumerableExtensions
|
|||
.GroupBy(x => x.Index / chunkSize)
|
||||
.Select(x => x.Select(v => v.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
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.Memory;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using System.Text;
|
||||
using CounterStrikeSharp.API.Modules.UserMessages;
|
||||
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
|
|
@ -45,12 +44,19 @@ public static class PlayerExtensions
|
|||
/// <returns>True if targeting is allowed, false otherwise.</returns>
|
||||
public static bool CanTarget(this CCSPlayerController? controller, CCSPlayerController? target)
|
||||
{
|
||||
if (controller is null || target is null) return true;
|
||||
if (target.IsBot) return true;
|
||||
if (controller is null || target is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target.IsBot)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return AdminManager.CanPlayerTarget(controller, target) ||
|
||||
AdminManager.CanPlayerTarget(new SteamID(controller.SteamID),
|
||||
new SteamID(target.SteamID)) ||
|
||||
new SteamID(target.SteamID)) ||
|
||||
AdminManager.GetPlayerImmunity(controller) >= AdminManager.GetPlayerImmunity(target);
|
||||
}
|
||||
|
||||
|
|
@ -62,9 +68,12 @@ public static class PlayerExtensions
|
|||
/// <returns>True if targeting is permitted, false otherwise.</returns>
|
||||
public static bool CanTarget(this CCSPlayerController? controller, SteamID steamId)
|
||||
{
|
||||
if (controller is null) return true;
|
||||
if (controller is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return AdminManager.CanPlayerTarget(new SteamID(controller.SteamID), steamId) ||
|
||||
return AdminManager.CanPlayerTarget(new SteamID(controller.SteamID), steamId) ||
|
||||
AdminManager.GetPlayerImmunity(controller) >= AdminManager.GetPlayerImmunity(steamId);
|
||||
}
|
||||
|
||||
|
|
@ -75,8 +84,11 @@ public static class PlayerExtensions
|
|||
/// <param name="speed">The speed modifier value.</param>
|
||||
public static void SetSpeed(this CCSPlayerController? controller, float speed)
|
||||
{
|
||||
var playerPawnValue = controller?.PlayerPawn.Value;
|
||||
if (playerPawnValue == null) return;
|
||||
CCSPlayerPawn? playerPawnValue = controller?.PlayerPawn.Value;
|
||||
if (playerPawnValue == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
playerPawnValue.VelocityModifier = speed;
|
||||
}
|
||||
|
|
@ -88,8 +100,11 @@ public static class PlayerExtensions
|
|||
/// <param name="gravity">The gravity scale.</param>
|
||||
public static void SetGravity(this CCSPlayerController? controller, float gravity)
|
||||
{
|
||||
var playerPawnValue = controller?.PlayerPawn.Value;
|
||||
if (playerPawnValue == null) return;
|
||||
CCSPlayerPawn? playerPawnValue = controller?.PlayerPawn.Value;
|
||||
if (playerPawnValue == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
playerPawnValue.ActualGravityScale = gravity;
|
||||
}
|
||||
|
|
@ -101,12 +116,18 @@ public static class PlayerExtensions
|
|||
/// <param name="money">The amount of money to set.</param>
|
||||
public static void SetMoney(this CCSPlayerController? controller, int money)
|
||||
{
|
||||
var moneyServices = controller?.InGameMoneyServices;
|
||||
if (moneyServices == null) return;
|
||||
CCSPlayerController_InGameMoneyServices? moneyServices = controller?.InGameMoneyServices;
|
||||
if (moneyServices == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
moneyServices.Account = money;
|
||||
|
||||
if (controller != null) Utilities.SetStateChanged(controller, "CCSPlayerController", "m_pInGameMoneyServices");
|
||||
if (controller != null)
|
||||
{
|
||||
Utilities.SetStateChanged(controller, "CCSPlayerController", "m_pInGameMoneyServices");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -116,8 +137,15 @@ public static class PlayerExtensions
|
|||
/// <param name="health">The health value, default is 100.</param>
|
||||
public static void SetHp(this CCSPlayerController? controller, int health = 100)
|
||||
{
|
||||
if (controller == null) return;
|
||||
if (health <= 0 || controller.PlayerPawn.Value == null || controller.PlayerPawn?.Value?.LifeState != (int)LifeState_t.LIFE_ALIVE) return;
|
||||
if (controller == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (health <= 0 || controller.PlayerPawn.Value == null || controller.PlayerPawn?.Value?.LifeState != (int)LifeState_t.LIFE_ALIVE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
controller.PlayerPawn.Value.Health = health;
|
||||
|
||||
|
|
@ -136,10 +164,10 @@ public static class PlayerExtensions
|
|||
/// <param name="depth">The depth offset (default 10 units).</param>
|
||||
public static void Bury(this CBasePlayerPawn pawn, float depth = 10f)
|
||||
{
|
||||
var newPos = new Vector3(pawn.AbsOrigin!.X, pawn.AbsOrigin.Y,
|
||||
Vector3 newPos = new Vector3(pawn.AbsOrigin!.X, pawn.AbsOrigin.Y,
|
||||
pawn.AbsOrigin!.Z - depth);
|
||||
var newRotation = new Vector3(pawn.AbsRotation!.X, pawn.AbsRotation.Y, pawn.AbsRotation.Z);
|
||||
var newVelocity = new Vector3(pawn.AbsVelocity.X, pawn.AbsVelocity.Y, pawn.AbsVelocity.Z);
|
||||
Vector3 newRotation = new Vector3(pawn.AbsRotation!.X, pawn.AbsRotation.Y, pawn.AbsRotation.Z);
|
||||
Vector3 newVelocity = new Vector3(pawn.AbsVelocity.X, pawn.AbsVelocity.Y, pawn.AbsVelocity.Z);
|
||||
|
||||
pawn.Teleport(newPos, newRotation, newVelocity);
|
||||
}
|
||||
|
|
@ -151,10 +179,10 @@ public static class PlayerExtensions
|
|||
/// <param name="depth">The depth offset (default 15 units).</param>
|
||||
public static void Unbury(this CBasePlayerPawn pawn, float depth = 15f)
|
||||
{
|
||||
var newPos = new Vector3(pawn.AbsOrigin!.X, pawn.AbsOrigin.Y,
|
||||
Vector3 newPos = new Vector3(pawn.AbsOrigin!.X, pawn.AbsOrigin.Y,
|
||||
pawn.AbsOrigin!.Z + depth);
|
||||
var newRotation = new Vector3(pawn.AbsRotation!.X, pawn.AbsRotation.Y, pawn.AbsRotation.Z);
|
||||
var newVelocity = new Vector3(pawn.AbsVelocity.X, pawn.AbsVelocity.Y, pawn.AbsVelocity.Z);
|
||||
Vector3 newRotation = new Vector3(pawn.AbsRotation!.X, pawn.AbsRotation.Y, pawn.AbsRotation.Z);
|
||||
Vector3 newVelocity = new Vector3(pawn.AbsVelocity.X, pawn.AbsVelocity.Y, pawn.AbsVelocity.Z);
|
||||
|
||||
pawn.Teleport(newPos, newRotation, newVelocity);
|
||||
}
|
||||
|
|
@ -169,7 +197,7 @@ public static class PlayerExtensions
|
|||
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 11); // invalid
|
||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Unfreezes the player pawn, enabling movement.
|
||||
/// </summary>
|
||||
|
|
@ -180,7 +208,7 @@ public static class PlayerExtensions
|
|||
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 2); // walk
|
||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Changes the player's color tint to specified RGBA values.
|
||||
/// </summary>
|
||||
|
|
@ -226,7 +254,7 @@ public static class PlayerExtensions
|
|||
|
||||
if (controller != null)
|
||||
{
|
||||
var playerName = new SchemaString<CBasePlayerController>(controller, "m_iszPlayerName");
|
||||
SchemaString<CBasePlayerController> playerName = new SchemaString<CBasePlayerController>(controller, "m_iszPlayerName");
|
||||
playerName.Set(newName + " ");
|
||||
|
||||
CS2_SimpleAdmin.Instance.AddTimer(0.25f, () =>
|
||||
|
|
@ -243,7 +271,10 @@ public static class PlayerExtensions
|
|||
|
||||
CS2_SimpleAdmin.Instance.AddTimer(0.4f, () =>
|
||||
{
|
||||
if (controller != null) Utilities.SetStateChanged(controller, "CBasePlayerController", "m_iszPlayerName");
|
||||
if (controller != null)
|
||||
{
|
||||
Utilities.SetStateChanged(controller, "CBasePlayerController", "m_iszPlayerName");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -255,7 +286,9 @@ public static class PlayerExtensions
|
|||
public static void TeleportPlayer(this CCSPlayerController? controller, CCSPlayerController? target)
|
||||
{
|
||||
if (controller?.PlayerPawn.Value == null && target?.PlayerPawn.Value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
controller?.PlayerPawn.Value is { AbsOrigin: not null, AbsRotation: not null } &&
|
||||
|
|
@ -278,17 +311,19 @@ public static class PlayerExtensions
|
|||
private static void PerformSlap(CBasePlayerPawn pawn, int damage = 0)
|
||||
{
|
||||
if (pawn.LifeState != (int)LifeState_t.LIFE_ALIVE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var controller = pawn.Controller.Value?.As<CCSPlayerController>();
|
||||
CCSPlayerController? controller = pawn.Controller.Value?.As<CCSPlayerController>();
|
||||
|
||||
/* Teleport in a random direction - thank you, Mani!*/
|
||||
/* Thank you AM & al!*/
|
||||
var random = new Random();
|
||||
var vel = new Vector3(pawn.AbsVelocity.X, pawn.AbsVelocity.Y, pawn.AbsVelocity.Z);
|
||||
Random random = new Random();
|
||||
Vector3 vel = new Vector3(pawn.AbsVelocity.X, pawn.AbsVelocity.Y, pawn.AbsVelocity.Z);
|
||||
|
||||
vel.X += (random.Next(180) + 50) * (random.Next(2) == 1 ? -1 : 1);
|
||||
vel.Y += (random.Next(180) + 50) * (random.Next(2) == 1 ? -1 : 1);
|
||||
vel.X += (random.Next(180) + 50) * RandomSign(random);
|
||||
vel.Y += (random.Next(180) + 50) * RandomSign(random);
|
||||
vel.Z += random.Next(200) + 100;
|
||||
|
||||
pawn.AbsVelocity.X = vel.X;
|
||||
|
|
@ -297,7 +332,7 @@ public static class PlayerExtensions
|
|||
|
||||
if (controller != null && controller.IsValid)
|
||||
{
|
||||
var shakeMessage = UserMessage.FromPartialName("Shake");
|
||||
UserMessage shakeMessage = UserMessage.FromPartialName("Shake");
|
||||
shakeMessage.SetFloat("duration", 1);
|
||||
shakeMessage.SetFloat("amplitude", 10);
|
||||
shakeMessage.SetFloat("frequency", 1f);
|
||||
|
|
@ -307,13 +342,17 @@ public static class PlayerExtensions
|
|||
}
|
||||
|
||||
if (damage <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pawn.Health -= damage;
|
||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth");
|
||||
|
||||
if (pawn.Health <= 0)
|
||||
{
|
||||
pawn.CommitSuicide(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -329,16 +368,19 @@ public static class PlayerExtensions
|
|||
public static void SendLocalizedMessage(this CCSPlayerController? controller, IStringLocalizer? localizer,
|
||||
string messageKey, params object[] messageArgs)
|
||||
{
|
||||
if (controller == null || localizer == null) return;
|
||||
if (controller == null || localizer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (new WithTemporaryCulture(controller.GetLanguage()))
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.Append(localizer[messageKey, messageArgs]);
|
||||
|
||||
foreach (var part in Helper.SeparateLines(sb.ToString()))
|
||||
|
||||
foreach (string part in Helper.SeparateLines(sb.ToString()))
|
||||
{
|
||||
var lineWithPrefix = (CS2_SimpleAdmin._localizer?["sa_prefix"] ?? "") + part.Trim();
|
||||
string lineWithPrefix = (CS2_SimpleAdmin._localizer?["sa_prefix"] ?? "") + part.Trim();
|
||||
controller.PrintToChat(lineWithPrefix);
|
||||
}
|
||||
}
|
||||
|
|
@ -357,20 +399,33 @@ public static class PlayerExtensions
|
|||
public static void SendLocalizedMessageCenter(this CCSPlayerController? controller, IStringLocalizer? localizer,
|
||||
string messageKey, params object[] messageArgs)
|
||||
{
|
||||
if (controller == null || localizer == null) return;
|
||||
if (controller == null || localizer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (new WithTemporaryCulture(controller.GetLanguage()))
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.Append(localizer[messageKey, messageArgs]);
|
||||
|
||||
foreach (var part in Helper.SeparateLines(sb.ToString()))
|
||||
foreach (string part in Helper.SeparateLines(sb.ToString()))
|
||||
{
|
||||
string _part;
|
||||
_part = Helper.CenterMessage(part);
|
||||
var lineWithPrefix = localizer["sa_prefix"] + _part;
|
||||
string lineWithPrefix = localizer["sa_prefix"] + _part;
|
||||
controller.PrintToChat(lineWithPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int RandomSign(Random random)
|
||||
{
|
||||
if (random.Next(2) == 1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,12 +1,10 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.ValveConstants.Protobuf;
|
||||
using System.Data.Common;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CS2_SimpleAdmin.Database;
|
||||
using CS2_SimpleAdminApi;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MySqlConnector;
|
||||
using System.Text;
|
||||
using CS2_SimpleAdmin.Database;
|
||||
|
||||
namespace CS2_SimpleAdmin.Managers;
|
||||
|
||||
|
|
@ -22,19 +20,29 @@ internal class BanManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>The newly created ban ID if successful, otherwise null.</returns>
|
||||
public async Task<int?> BanPlayer(PlayerInfo player, PlayerInfo? issuer, string reason, int time = 0)
|
||||
{
|
||||
if (databaseProvider == null) return null;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
DateTime now = Time.ActualDateTime();
|
||||
DateTime futureTime = now.AddMinutes(time);
|
||||
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
try
|
||||
{
|
||||
var sql = databaseProvider.GetAddBanQuery();
|
||||
var banId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
string? playerIp = null;
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 1)
|
||||
{
|
||||
playerIp = player.IpAddress;
|
||||
}
|
||||
|
||||
string sql = databaseProvider.GetAddBanQuery();
|
||||
int? banId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
{
|
||||
playerSteamid = player.SteamId.SteamId64,
|
||||
playerName = player.Name,
|
||||
playerIp = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 1 ? player.IpAddress : null,
|
||||
playerIp,
|
||||
adminSteamid = issuer?.SteamId.SteamId64 ?? 0,
|
||||
adminName = issuer?.Name ?? CS2_SimpleAdmin._localizer?["sa_console"] ?? "Console",
|
||||
banReason = reason,
|
||||
|
|
@ -46,7 +54,7 @@ internal class BanManager(IDatabaseProvider? databaseProvider)
|
|||
|
||||
return banId;
|
||||
}
|
||||
catch(Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError(ex, ex.Message);
|
||||
return null;
|
||||
|
|
@ -63,16 +71,19 @@ internal class BanManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>The ID of the newly created ban if successful, otherwise null.</returns>
|
||||
public async Task<int?> AddBanBySteamid(ulong playerSteamId, PlayerInfo? issuer, string reason, int time = 0)
|
||||
{
|
||||
if (databaseProvider == null) return null;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
DateTime now = Time.ActualDateTime();
|
||||
DateTime futureTime = now.AddMinutes(time);
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetAddBanBySteamIdQuery();
|
||||
var banId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
string sql = databaseProvider.GetAddBanBySteamIdQuery();
|
||||
int? banId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
{
|
||||
playerSteamid = playerSteamId,
|
||||
adminSteamid = issuer?.SteamId.SteamId64 ?? 0,
|
||||
|
|
@ -83,10 +94,10 @@ internal class BanManager(IDatabaseProvider? databaseProvider)
|
|||
created = now,
|
||||
serverid = CS2_SimpleAdmin.ServerId
|
||||
});
|
||||
|
||||
|
||||
return banId;
|
||||
}
|
||||
catch(Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError(ex, ex.Message);
|
||||
return null;
|
||||
|
|
@ -102,18 +113,24 @@ internal class BanManager(IDatabaseProvider? databaseProvider)
|
|||
/// <param name="time">Ban duration in minutes. If 0, the ban is permanent.</param>
|
||||
public async Task AddBanByIp(string playerIp, PlayerInfo? issuer, string reason, int time = 0)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(playerIp)) return;
|
||||
if (string.IsNullOrEmpty(playerIp))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DateTime now = Time.ActualDateTime();
|
||||
DateTime futureTime = now.AddMinutes(time);
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetAddBanByIpQuery();
|
||||
string sql = databaseProvider.GetAddBanByIpQuery();
|
||||
await connection.ExecuteAsync(sql, new
|
||||
{
|
||||
playerIp,
|
||||
|
|
@ -129,179 +146,179 @@ internal class BanManager(IDatabaseProvider? databaseProvider)
|
|||
catch { }
|
||||
}
|
||||
|
||||
// public async Task<bool> IsPlayerBanned(PlayerInfo player)
|
||||
// {
|
||||
// if (database == null) return false;
|
||||
//
|
||||
// if (player.IpAddress == null)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// #if DEBUG
|
||||
// if (CS2_SimpleAdmin._logger != null)
|
||||
// CS2_SimpleAdmin._logger.LogCritical($"IsPlayerBanned for {player.Name}");
|
||||
// #endif
|
||||
//
|
||||
// int banCount;
|
||||
//
|
||||
// DateTime currentTime = Time.ActualDateTime();
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// string sql;
|
||||
//
|
||||
// if (CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp && !CS2_SimpleAdmin.Instance.Config.OtherSettings.IgnoredIps.Contains(player.IpAddress))
|
||||
// {
|
||||
// sql = CS2_SimpleAdmin.Instance.Config.MultiServerMode ? """
|
||||
// SELECT COALESCE((
|
||||
// SELECT COUNT(*)
|
||||
// FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime)
|
||||
// ), 0)
|
||||
// +
|
||||
// COALESCE((
|
||||
// SELECT COUNT(*)
|
||||
// FROM sa_bans
|
||||
// JOIN sa_players_ips ON sa_bans.player_steamid = sa_players_ips.steamid
|
||||
// WHERE sa_bans.status = 'ACTIVE'
|
||||
// AND sa_players_ips.address = @PlayerIP
|
||||
// AND NOT EXISTS (
|
||||
// SELECT 1
|
||||
// FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime)
|
||||
// )
|
||||
// ), 0) AS TotalBanCount;
|
||||
// """ : """
|
||||
// SELECT COALESCE((
|
||||
// SELECT COUNT(*)
|
||||
// FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime)
|
||||
// AND server_id = @ServerId
|
||||
// ), 0)
|
||||
// +
|
||||
// COALESCE((
|
||||
// SELECT COUNT(*)
|
||||
// FROM sa_bans
|
||||
// JOIN sa_players_ips ON sa_bans.player_steamid = sa_players_ips.steamid
|
||||
// WHERE sa_bans.status = 'ACTIVE'
|
||||
// AND sa_players_ips.address = @PlayerIP
|
||||
// AND NOT EXISTS (
|
||||
// SELECT 1
|
||||
// FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime)
|
||||
// AND server_id = @ServerId
|
||||
// )
|
||||
// ), 0) AS TotalBanCount;
|
||||
// """;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// sql = CS2_SimpleAdmin.Instance.Config.MultiServerMode ? """
|
||||
// UPDATE sa_bans
|
||||
// SET player_ip = CASE WHEN player_ip IS NULL THEN @PlayerIP ELSE player_ip END,
|
||||
// player_name = CASE WHEN player_name IS NULL THEN @PlayerName ELSE player_name END
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime);
|
||||
//
|
||||
// SELECT COUNT(*) FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime);
|
||||
// """ : """
|
||||
// UPDATE sa_bans
|
||||
// SET player_ip = CASE WHEN player_ip IS NULL THEN @PlayerIP ELSE player_ip END,
|
||||
// player_name = CASE WHEN player_name IS NULL THEN @PlayerName ELSE player_name END
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime) AND server_id = @ServerId;
|
||||
//
|
||||
// SELECT COUNT(*) FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime) AND server_id = @ServerId;
|
||||
// """;
|
||||
// }
|
||||
//
|
||||
// await using var connection = await database.GetConnectionAsync();
|
||||
//
|
||||
// var parameters = new
|
||||
// {
|
||||
// PlayerSteamID = player.SteamId.SteamId64,
|
||||
// PlayerIP = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0 ||
|
||||
// string.IsNullOrEmpty(player.IpAddress) ||
|
||||
// CS2_SimpleAdmin.Instance.Config.OtherSettings.IgnoredIps.Contains(player.IpAddress)
|
||||
// ? null
|
||||
// : player.IpAddress,
|
||||
// PlayerName = !string.IsNullOrEmpty(player.Name) ? player.Name : string.Empty,
|
||||
// CurrentTime = currentTime,
|
||||
// CS2_SimpleAdmin.ServerId
|
||||
// };
|
||||
//
|
||||
// banCount = await connection.ExecuteScalarAsync<int>(sql, parameters);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// CS2_SimpleAdmin._logger?.LogError("Unable to check ban status for {PlayerName} ({ExceptionMessage})",
|
||||
// player.Name, ex.Message);
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// return banCount > 0;
|
||||
// }
|
||||
//
|
||||
// public async Task<int> GetPlayerBans(PlayerInfo player)
|
||||
// {
|
||||
// if (database == null) return 0;
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// string sql;
|
||||
//
|
||||
// sql = CS2_SimpleAdmin.Instance.Config.MultiServerMode
|
||||
// ? "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)"
|
||||
// : "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP) AND server_id = @serverid";
|
||||
//
|
||||
// int banCount;
|
||||
//
|
||||
// await using var connection = await database.GetConnectionAsync();
|
||||
//
|
||||
// if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType > 0 && !string.IsNullOrEmpty(player.IpAddress))
|
||||
// {
|
||||
// banCount = await connection.ExecuteScalarAsync<int>(sql,
|
||||
// new
|
||||
// {
|
||||
// PlayerSteamID = player.SteamId.SteamId64,
|
||||
// PlayerIP = player.IpAddress,
|
||||
// serverid = CS2_SimpleAdmin.ServerId
|
||||
// });
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// banCount = await connection.ExecuteScalarAsync<int>(sql,
|
||||
// new
|
||||
// {
|
||||
// PlayerSteamID = player.SteamId.SteamId64,
|
||||
// PlayerIP = DBNull.Value,
|
||||
// serverid = CS2_SimpleAdmin.ServerId
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// return banCount;
|
||||
// }
|
||||
// catch { }
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
// public async Task<bool> IsPlayerBanned(PlayerInfo player)
|
||||
// {
|
||||
// if (database == null) return false;
|
||||
//
|
||||
// if (player.IpAddress == null)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// #if DEBUG
|
||||
// if (CS2_SimpleAdmin._logger != null)
|
||||
// CS2_SimpleAdmin._logger.LogCritical($"IsPlayerBanned for {player.Name}");
|
||||
// #endif
|
||||
//
|
||||
// int banCount;
|
||||
//
|
||||
// DateTime currentTime = Time.ActualDateTime();
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// string sql;
|
||||
//
|
||||
// if (CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp && !CS2_SimpleAdmin.Instance.Config.OtherSettings.IgnoredIps.Contains(player.IpAddress))
|
||||
// {
|
||||
// sql = CS2_SimpleAdmin.Instance.Config.MultiServerMode ? """
|
||||
// SELECT COALESCE((
|
||||
// SELECT COUNT(*)
|
||||
// FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime)
|
||||
// ), 0)
|
||||
// +
|
||||
// COALESCE((
|
||||
// SELECT COUNT(*)
|
||||
// FROM sa_bans
|
||||
// JOIN sa_players_ips ON sa_bans.player_steamid = sa_players_ips.steamid
|
||||
// WHERE sa_bans.status = 'ACTIVE'
|
||||
// AND sa_players_ips.address = @PlayerIP
|
||||
// AND NOT EXISTS (
|
||||
// SELECT 1
|
||||
// FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime)
|
||||
// )
|
||||
// ), 0) AS TotalBanCount;
|
||||
// """ : """
|
||||
// SELECT COALESCE((
|
||||
// SELECT COUNT(*)
|
||||
// FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime)
|
||||
// AND server_id = @ServerId
|
||||
// ), 0)
|
||||
// +
|
||||
// COALESCE((
|
||||
// SELECT COUNT(*)
|
||||
// FROM sa_bans
|
||||
// JOIN sa_players_ips ON sa_bans.player_steamid = sa_players_ips.steamid
|
||||
// WHERE sa_bans.status = 'ACTIVE'
|
||||
// AND sa_players_ips.address = @PlayerIP
|
||||
// AND NOT EXISTS (
|
||||
// SELECT 1
|
||||
// FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime)
|
||||
// AND server_id = @ServerId
|
||||
// )
|
||||
// ), 0) AS TotalBanCount;
|
||||
// """;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// sql = CS2_SimpleAdmin.Instance.Config.MultiServerMode ? """
|
||||
// UPDATE sa_bans
|
||||
// SET player_ip = CASE WHEN player_ip IS NULL THEN @PlayerIP ELSE player_ip END,
|
||||
// player_name = CASE WHEN player_name IS NULL THEN @PlayerName ELSE player_name END
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime);
|
||||
//
|
||||
// SELECT COUNT(*) FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime);
|
||||
// """ : """
|
||||
// UPDATE sa_bans
|
||||
// SET player_ip = CASE WHEN player_ip IS NULL THEN @PlayerIP ELSE player_ip END,
|
||||
// player_name = CASE WHEN player_name IS NULL THEN @PlayerName ELSE player_name END
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime) AND server_id = @ServerId;
|
||||
//
|
||||
// SELECT COUNT(*) FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime) AND server_id = @ServerId;
|
||||
// """;
|
||||
// }
|
||||
//
|
||||
// await using var connection = await database.GetConnectionAsync();
|
||||
//
|
||||
// var parameters = new
|
||||
// {
|
||||
// PlayerSteamID = player.SteamId.SteamId64,
|
||||
// PlayerIP = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0 ||
|
||||
// string.IsNullOrEmpty(player.IpAddress) ||
|
||||
// CS2_SimpleAdmin.Instance.Config.OtherSettings.IgnoredIps.Contains(player.IpAddress)
|
||||
// ? null
|
||||
// : player.IpAddress,
|
||||
// PlayerName = !string.IsNullOrEmpty(player.Name) ? player.Name : string.Empty,
|
||||
// CurrentTime = currentTime,
|
||||
// CS2_SimpleAdmin.ServerId
|
||||
// };
|
||||
//
|
||||
// banCount = await connection.ExecuteScalarAsync<int>(sql, parameters);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// CS2_SimpleAdmin._logger?.LogError("Unable to check ban status for {PlayerName} ({ExceptionMessage})",
|
||||
// player.Name, ex.Message);
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// return banCount > 0;
|
||||
// }
|
||||
//
|
||||
// public async Task<int> GetPlayerBans(PlayerInfo player)
|
||||
// {
|
||||
// if (database == null) return 0;
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// string sql;
|
||||
//
|
||||
// sql = CS2_SimpleAdmin.Instance.Config.MultiServerMode
|
||||
// ? "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)"
|
||||
// : "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP) AND server_id = @serverid";
|
||||
//
|
||||
// int banCount;
|
||||
//
|
||||
// await using var connection = await database.GetConnectionAsync();
|
||||
//
|
||||
// if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType > 0 && !string.IsNullOrEmpty(player.IpAddress))
|
||||
// {
|
||||
// banCount = await connection.ExecuteScalarAsync<int>(sql,
|
||||
// new
|
||||
// {
|
||||
// PlayerSteamID = player.SteamId.SteamId64,
|
||||
// PlayerIP = player.IpAddress,
|
||||
// serverid = CS2_SimpleAdmin.ServerId
|
||||
// });
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// banCount = await connection.ExecuteScalarAsync<int>(sql,
|
||||
// new
|
||||
// {
|
||||
// PlayerSteamID = player.SteamId.SteamId64,
|
||||
// PlayerIP = DBNull.Value,
|
||||
// serverid = CS2_SimpleAdmin.ServerId
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// return banCount;
|
||||
// }
|
||||
// catch { }
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Unbans a player based on a pattern match of SteamID or IP address.
|
||||
|
|
@ -309,46 +326,51 @@ internal class BanManager(IDatabaseProvider? databaseProvider)
|
|||
/// <param name="playerPattern">Pattern to match against player identifiers (e.g., partial SteamID).</param>
|
||||
/// <param name="adminSteamId">SteamID64 of the admin performing the unban.</param>
|
||||
/// <param name="reason">Optional reason for the unban. If null or empty, the unban reason is not stored.</param>
|
||||
public async Task UnbanPlayer(string playerPattern, string adminSteamId, string reason)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
|
||||
if (playerPattern is not { Length: > 1 })
|
||||
public async Task UnbanPlayer(string playerPattern, string adminSteamId, string reason)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sqlRetrieveBans = databaseProvider.GetUnbanRetrieveBansQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
|
||||
var bans = await connection.QueryAsync(sqlRetrieveBans, new { pattern = playerPattern, serverid = CS2_SimpleAdmin.ServerId });
|
||||
var bansList = bans as dynamic[] ?? bans.ToArray();
|
||||
if (bansList.Length == 0)
|
||||
return;
|
||||
|
||||
var sqlAdminId = databaseProvider.GetUnbanAdminIdQuery();
|
||||
var adminId = await connection.ExecuteScalarAsync<int?>(sqlAdminId, new { adminSteamId }) ?? 0;
|
||||
|
||||
foreach (var ban in bansList)
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
int banId = ban.id;
|
||||
|
||||
var sqlInsertUnban = databaseProvider.GetInsertUnbanQuery(reason != null);
|
||||
var unbanId = await connection.ExecuteScalarAsync<int>(sqlInsertUnban, new { banId, adminId, reason });
|
||||
|
||||
var sqlUpdateBan = databaseProvider.GetUpdateBanStatusQuery();
|
||||
await connection.ExecuteAsync(sqlUpdateBan, new { unbanId, banId });
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply immediately; the periodic cache refresh would otherwise keep rejecting the player for up to a minute
|
||||
CS2_SimpleAdmin.Instance.CacheManager?.SetBanStatus(bansList.Select(b => (int)b.id), Models.BanStatus.UNBANNED);
|
||||
|
||||
// css_ban also issues a native banid when UnlockedCommands is on; clear it so the engine stops
|
||||
// rejecting the player. Done here, after the rows are updated, and for whichever pattern matched them.
|
||||
if (CS2_SimpleAdmin.UnlockedCommands)
|
||||
if (playerPattern is not { Length: > 1 })
|
||||
{
|
||||
var steamIds = bansList
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
string sqlRetrieveBans = databaseProvider.GetUnbanRetrieveBansQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
|
||||
IEnumerable<dynamic> bans = await connection.QueryAsync(sqlRetrieveBans, new { pattern = playerPattern, serverid = CS2_SimpleAdmin.ServerId });
|
||||
dynamic[] bansList = bans as dynamic[] ?? bans.ToArray();
|
||||
if (bansList.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string sqlAdminId = databaseProvider.GetUnbanAdminIdQuery();
|
||||
int adminId = await connection.ExecuteScalarAsync<int?>(sqlAdminId, new { adminSteamId }) ?? 0;
|
||||
|
||||
foreach (dynamic ban in bansList)
|
||||
{
|
||||
int banId = ban.id;
|
||||
|
||||
string sqlInsertUnban = databaseProvider.GetInsertUnbanQuery(reason != null);
|
||||
int unbanId = await connection.ExecuteScalarAsync<int>(sqlInsertUnban, new { banId, adminId, reason });
|
||||
|
||||
string sqlUpdateBan = databaseProvider.GetUpdateBanStatusQuery();
|
||||
await connection.ExecuteAsync(sqlUpdateBan, new { unbanId, banId });
|
||||
}
|
||||
|
||||
// Apply immediately; the periodic cache refresh would otherwise keep rejecting the player for up to a minute
|
||||
CS2_SimpleAdmin.Instance.CacheManager?.SetBanStatus(bansList.Select(b => (int)b.id), Models.BanStatus.UNBANNED);
|
||||
|
||||
// css_ban also issues a native banid when UnlockedCommands is on; clear it so the engine stops
|
||||
// rejecting the player. Done here, after the rows are updated, and for whichever pattern matched them.
|
||||
if (CS2_SimpleAdmin.UnlockedCommands)
|
||||
{
|
||||
List<ulong> steamIds = bansList
|
||||
.Select(b => (object?)b.player_steamid)
|
||||
.Where(id => id != null)
|
||||
.Select(id => Convert.ToUInt64(id))
|
||||
|
|
@ -356,19 +378,23 @@ public async Task UnbanPlayer(string playerPattern, string adminSteamId, string
|
|||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
if (steamIds.Count > 0)
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
if (steamIds.Count > 0)
|
||||
{
|
||||
foreach (var steamId in steamIds)
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
foreach (ulong steamId in steamIds)
|
||||
{
|
||||
Server.ExecuteCommand($"removeid {new SteamID(steamId).SteamId3}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError("Unable to unban player: {exception}", ex.Message);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError("Unable to unban player: {exception}", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// public async Task CheckOnlinePlayers(List<(string? IpAddress, ulong SteamID, int? UserId, int Slot)> players)
|
||||
// {
|
||||
|
|
@ -445,17 +471,21 @@ public async Task UnbanPlayer(string playerPattern, string adminSteamId, string
|
|||
/// </summary>
|
||||
public async Task ExpireOldBans()
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
var currentTime = Time.ActualDateTime();
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DateTime currentTime = Time.ActualDateTime();
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetExpireBansQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
string sql = databaseProvider.GetExpireBansQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
await connection.ExecuteAsync(sql, new { currentTime, serverid = CS2_SimpleAdmin.ServerId });
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.ExpireOldIpBans > 0)
|
||||
{
|
||||
var ipBansTime = currentTime.AddDays(-CS2_SimpleAdmin.Instance.Config.OtherSettings.ExpireOldIpBans);
|
||||
DateTime ipBansTime = currentTime.AddDays(-CS2_SimpleAdmin.Instance.Config.OtherSettings.ExpireOldIpBans);
|
||||
sql = databaseProvider.GetExpireIpBansQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
await connection.ExecuteAsync(sql, new { ipBansTime, CS2_SimpleAdmin.ServerId });
|
||||
|
||||
|
|
@ -468,4 +498,4 @@ public async Task UnbanPlayer(string playerPattern, string adminSteamId, string
|
|||
CS2_SimpleAdmin._logger?.LogCritical("Unable to remove expired bans");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
using System.Collections.Concurrent;
|
||||
using CS2_SimpleAdmin.Database;
|
||||
using System.Data.Common;
|
||||
using CS2_SimpleAdmin.Models;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ZLinq;
|
||||
|
||||
namespace CS2_SimpleAdmin.Managers;
|
||||
|
||||
internal class CacheManager: IDisposable
|
||||
internal class CacheManager : IDisposable
|
||||
{
|
||||
private readonly ConcurrentDictionary<int, BanRecord> _banCache = [];
|
||||
// Rebuilt into fresh instances and swapped, so unlocked readers on the game thread never see a half-built index
|
||||
|
|
@ -17,22 +16,33 @@ internal class CacheManager: IDisposable
|
|||
|
||||
private readonly ConcurrentDictionary<ulong, HashSet<IpRecord>> _playerIpsCache = [];
|
||||
private HashSet<uint> _cachedIgnoredIps = [];
|
||||
|
||||
|
||||
private DateTime _lastUpdateTime = DateTime.MinValue;
|
||||
private DateTime? _lastDatabaseTime = null; // Track actual time from database
|
||||
private bool _isInitialized;
|
||||
private bool _disposed;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes and builds the ban and IP cache from the database. Loads bans, player IP history, and config settings.
|
||||
/// </summary>
|
||||
/// <returns>Asynchronous task representing the initialization process.</returns>
|
||||
public async Task InitializeCacheAsync()
|
||||
{
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null) return;
|
||||
if (!CS2_SimpleAdmin.ServerLoaded) return;
|
||||
if (_isInitialized) return;
|
||||
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CS2_SimpleAdmin.ServerLoaded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Clear();
|
||||
|
|
@ -41,9 +51,9 @@ internal class CacheManager: IDisposable
|
|||
.Select(IpHelper.IpToUint)
|
||||
.ToHashSet();
|
||||
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
List<BanRecord> bans;
|
||||
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.MultiServerMode)
|
||||
{
|
||||
bans = (await connection.QueryAsync<BanRecord>(
|
||||
|
|
@ -69,21 +79,21 @@ internal class CacheManager: IDisposable
|
|||
status AS Status
|
||||
FROM sa_bans
|
||||
WHERE server_id = @serverId
|
||||
""", new {serverId = CS2_SimpleAdmin.ServerId})).ToList();
|
||||
""", new { serverId = CS2_SimpleAdmin.ServerId })).ToList();
|
||||
}
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp)
|
||||
{
|
||||
// Optimization: Load IP history and build cache in single pass
|
||||
var ipHistory = await connection.QueryAsync<IpHistoryRow>(
|
||||
IEnumerable<IpHistoryRow> ipHistory = await connection.QueryAsync<IpHistoryRow>(
|
||||
"SELECT steamid, name, address, used_at FROM sa_players_ips ORDER BY steamid, address, used_at DESC");
|
||||
|
||||
var unknownName = CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
||||
var currentSteamId = 0UL;
|
||||
var currentIpSet = new HashSet<IpRecord>(new IpRecordComparer());
|
||||
var latestIpTimestamps = new Dictionary<uint, DateTime>();
|
||||
string unknownName = CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
||||
ulong currentSteamId = 0UL;
|
||||
HashSet<IpRecord> currentIpSet = new HashSet<IpRecord>(new IpRecordComparer());
|
||||
Dictionary<uint, DateTime> latestIpTimestamps = new Dictionary<uint, DateTime>();
|
||||
|
||||
foreach (var record in ipHistory)
|
||||
foreach (IpHistoryRow record in ipHistory)
|
||||
{
|
||||
// When we encounter a new steamid, save the previous one
|
||||
if ((ulong)record.Steamid != currentSteamId && currentSteamId != 0)
|
||||
|
|
@ -96,14 +106,14 @@ internal class CacheManager: IDisposable
|
|||
currentSteamId = (ulong)record.Steamid;
|
||||
|
||||
// Only keep the latest timestamp for each IP
|
||||
if (!latestIpTimestamps.TryGetValue(record.Address, out var existingTimestamp) ||
|
||||
if (!latestIpTimestamps.TryGetValue(record.Address, out DateTime existingTimestamp) ||
|
||||
record.Used_at > existingTimestamp)
|
||||
{
|
||||
latestIpTimestamps[record.Address] = record.Used_at;
|
||||
currentIpSet.Add(new IpRecord(
|
||||
record.Address,
|
||||
record.Used_at,
|
||||
string.IsNullOrEmpty(record.Name) ? unknownName : record.Name
|
||||
NameOrDefault(record.Name, unknownName)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -115,11 +125,13 @@ internal class CacheManager: IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
foreach (var ban in bans.AsValueEnumerable())
|
||||
foreach (BanRecord? ban in bans.AsValueEnumerable())
|
||||
{
|
||||
_banCache.TryAdd(ban.Id, ban);
|
||||
|
||||
}
|
||||
|
||||
RebuildIndexes();
|
||||
|
||||
|
||||
_lastUpdateTime = Time.ActualDateTime().AddSeconds(-1);
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
|
@ -128,7 +140,7 @@ internal class CacheManager: IDisposable
|
|||
Console.WriteLine(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Clears all cached data and reinitializes the cache from the database.
|
||||
/// </summary>
|
||||
|
|
@ -136,15 +148,15 @@ internal class CacheManager: IDisposable
|
|||
public async Task ForceReInitializeCacheAsync()
|
||||
{
|
||||
_isInitialized = false;
|
||||
|
||||
|
||||
_banCache.Clear();
|
||||
_playerIpsCache.Clear();
|
||||
_cachedIgnoredIps = [];
|
||||
_lastUpdateTime = DateTime.MinValue;
|
||||
|
||||
|
||||
await InitializeCacheAsync();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the in-memory cache with updated or new data from the database since the last update time.
|
||||
/// Also updates multi-account IP history if enabled.
|
||||
|
|
@ -152,16 +164,23 @@ internal class CacheManager: IDisposable
|
|||
/// <returns>Asynchronous task representing the refresh operation.</returns>
|
||||
public async Task RefreshCacheAsync()
|
||||
{
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null) return;
|
||||
if (!_isInitialized) return;
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_isInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
IEnumerable<BanRecord> updatedBans;
|
||||
|
||||
// Get current time from database in local timezone (CURRENT_TIMESTAMP uses session timezone, not UTC)
|
||||
var currentDatabaseTime = await connection.QueryFirstAsync<DateTime>("SELECT CURRENT_TIMESTAMP");
|
||||
DateTime currentDatabaseTime = await connection.QueryFirstAsync<DateTime>("SELECT CURRENT_TIMESTAMP");
|
||||
|
||||
// Optimization: Only get IDs for comparison if we need to check for deletions
|
||||
// Most of the time bans are just added/updated, not deleted
|
||||
|
|
@ -170,10 +189,10 @@ internal class CacheManager: IDisposable
|
|||
if (CS2_SimpleAdmin.Instance.Config.MultiServerMode)
|
||||
{
|
||||
// Use previous database time or start from far past if first run
|
||||
var lastCheckTime = _lastDatabaseTime ?? DateTime.MinValue;
|
||||
DateTime lastCheckTime = _lastDatabaseTime ?? DateTime.MinValue;
|
||||
|
||||
// Get recently updated bans by timestamp (using database time to avoid timezone issues)
|
||||
var updatedBans_Query = (await connection.QueryAsync<BanRecord>(
|
||||
List<BanRecord> updatedBans_Query = (await connection.QueryAsync<BanRecord>(
|
||||
"""
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
|
|
@ -186,10 +205,10 @@ internal class CacheManager: IDisposable
|
|||
)).ToList();
|
||||
|
||||
// Detect changes: new bans or status changes
|
||||
var updatedList = new List<BanRecord>();
|
||||
foreach (var ban in updatedBans_Query)
|
||||
List<BanRecord> updatedList = new List<BanRecord>();
|
||||
foreach (BanRecord ban in updatedBans_Query)
|
||||
{
|
||||
if (!_banCache.TryGetValue(ban.Id, out var cachedBan))
|
||||
if (!_banCache.TryGetValue(ban.Id, out BanRecord? cachedBan))
|
||||
{
|
||||
// New ban
|
||||
updatedList.Add(ban);
|
||||
|
|
@ -213,10 +232,10 @@ internal class CacheManager: IDisposable
|
|||
else
|
||||
{
|
||||
// Use previous database time or start from far past if first run
|
||||
var lastCheckTime = _lastDatabaseTime ?? DateTime.MinValue;
|
||||
DateTime lastCheckTime = _lastDatabaseTime ?? DateTime.MinValue;
|
||||
|
||||
// Get recently updated bans for this server by timestamp (using database time to avoid timezone issues)
|
||||
var updatedBans_Query = (await connection.QueryAsync<BanRecord>(
|
||||
List<BanRecord> updatedBans_Query = (await connection.QueryAsync<BanRecord>(
|
||||
"""
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
|
|
@ -229,10 +248,10 @@ internal class CacheManager: IDisposable
|
|||
)).ToList();
|
||||
|
||||
// Detect changes: new bans or status changes
|
||||
var updatedList = new List<BanRecord>();
|
||||
foreach (var ban in updatedBans_Query)
|
||||
List<BanRecord> updatedList = new List<BanRecord>();
|
||||
foreach (BanRecord ban in updatedBans_Query)
|
||||
{
|
||||
if (!_banCache.TryGetValue(ban.Id, out var cachedBan))
|
||||
if (!_banCache.TryGetValue(ban.Id, out BanRecord? cachedBan))
|
||||
{
|
||||
// New ban
|
||||
updatedList.Add(ban);
|
||||
|
|
@ -260,49 +279,56 @@ internal class CacheManager: IDisposable
|
|||
// Optimization: Only process deletions if we have the full ID list
|
||||
if (allIds != null)
|
||||
{
|
||||
foreach (var id in _banCache.Keys)
|
||||
foreach (int id in _banCache.Keys)
|
||||
{
|
||||
if (allIds.Contains(id) || !_banCache.TryRemove(id, out var ban)) continue;
|
||||
if (allIds.Contains(id) || !_banCache.TryRemove(id, out BanRecord? ban))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ban.PlayerSteamId != null &&
|
||||
_steamIdIndex.TryGetValue(ban.PlayerSteamId.Value, out var steamBans))
|
||||
_steamIdIndex.TryGetValue(ban.PlayerSteamId.Value, out List<BanRecord>? steamBans))
|
||||
{
|
||||
steamBans.RemoveAll(b => b.Id == id);
|
||||
if (steamBans.Count == 0)
|
||||
{
|
||||
_steamIdIndex.TryRemove(ban.PlayerSteamId.Value, out _);
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ban.PlayerIp) ||
|
||||
!IpHelper.TryConvertIpToUint(ban.PlayerIp, out var ipUInt) ||
|
||||
!_ipIndex.TryGetValue(ipUInt, out var ipBans))
|
||||
!IpHelper.TryConvertIpToUint(ban.PlayerIp, out uint ipUInt) ||
|
||||
!_ipIndex.TryGetValue(ipUInt, out List<BanRecord>? ipBans))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ipBans.RemoveAll(b => b.Id == id);
|
||||
if (ipBans.Count == 0)
|
||||
{
|
||||
_ipIndex.TryRemove(ipUInt, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp)
|
||||
{
|
||||
var ipHistory = (await connection.QueryAsync<IpHistoryRow>(
|
||||
IEnumerable<IpHistoryRow> ipHistory = (await connection.QueryAsync<IpHistoryRow>(
|
||||
"SELECT steamid, name, address, used_at FROM sa_players_ips WHERE used_at >= @lastUpdate ORDER BY used_at DESC LIMIT 300",
|
||||
new { lastUpdate = _lastUpdateTime }));
|
||||
|
||||
foreach (var group in ipHistory.AsValueEnumerable().GroupBy(x => x.Steamid))
|
||||
foreach (IGrouping<long, IpHistoryRow>? group in ipHistory.AsValueEnumerable().GroupBy(x => x.Steamid))
|
||||
{
|
||||
var ipSet = new HashSet<IpRecord>(
|
||||
HashSet<IpRecord> ipSet = new HashSet<IpRecord>(
|
||||
group
|
||||
.GroupBy(x => x.Address)
|
||||
.Select(g =>
|
||||
{
|
||||
var latest = g.MaxBy(x => x.Used_at)!; // group is never empty
|
||||
IpHistoryRow latest = g.MaxBy(x => x.Used_at)!; // group is never empty
|
||||
return new IpRecord(
|
||||
g.Key,
|
||||
latest.Used_at,
|
||||
!string.IsNullOrEmpty(latest.Name)
|
||||
? latest.Name
|
||||
: CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown"
|
||||
NameOrDefault(latest.Name, CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown")
|
||||
);
|
||||
}),
|
||||
new IpRecordComparer()
|
||||
|
|
@ -313,7 +339,7 @@ internal class CacheManager: IDisposable
|
|||
_ => ipSet,
|
||||
(_, existingSet) =>
|
||||
{
|
||||
foreach (var newEntry in ipSet)
|
||||
foreach (IpRecord newEntry in ipSet)
|
||||
{
|
||||
existingSet.Remove(newEntry);
|
||||
existingSet.Add(newEntry);
|
||||
|
|
@ -325,10 +351,10 @@ internal class CacheManager: IDisposable
|
|||
}
|
||||
|
||||
// Update cache with new/modified bans
|
||||
var needsRebuild = false;
|
||||
foreach (var ban in updatedBans)
|
||||
bool needsRebuild = false;
|
||||
foreach (BanRecord ban in updatedBans)
|
||||
{
|
||||
if (_banCache.TryGetValue(ban.Id, out var oldBan) && oldBan.Status != ban.Status)
|
||||
if (_banCache.TryGetValue(ban.Id, out BanRecord? oldBan) && oldBan.Status != ban.Status)
|
||||
{
|
||||
// Ban status changed (e.g., ACTIVE -> EXPIRED/UNBANNED), need to rebuild indexes
|
||||
needsRebuild = true;
|
||||
|
|
@ -370,36 +396,43 @@ internal class CacheManager: IDisposable
|
|||
/// <param name="status">New status.</param>
|
||||
public void SetBanStatus(IEnumerable<int> banIds, BanStatus status)
|
||||
{
|
||||
var changed = false;
|
||||
foreach (var id in banIds)
|
||||
bool changed = false;
|
||||
foreach (int id in banIds)
|
||||
{
|
||||
if (!_banCache.TryGetValue(id, out var ban) || ban.StatusEnum == status) continue;
|
||||
if (!_banCache.TryGetValue(id, out BanRecord? ban) || ban.StatusEnum == status)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_banCache[id] = ban with { Status = status.ToString() };
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) RebuildIndexes();
|
||||
if (changed)
|
||||
{
|
||||
RebuildIndexes();
|
||||
}
|
||||
}
|
||||
|
||||
private void RebuildIndexesCore()
|
||||
{
|
||||
var steamIdIndex = new ConcurrentDictionary<ulong, List<BanRecord>>();
|
||||
var ipIndex = new ConcurrentDictionary<uint, List<BanRecord>>();
|
||||
ConcurrentDictionary<ulong, List<BanRecord>> steamIdIndex = new ConcurrentDictionary<ulong, List<BanRecord>>();
|
||||
ConcurrentDictionary<uint, List<BanRecord>> ipIndex = new ConcurrentDictionary<uint, List<BanRecord>>();
|
||||
|
||||
// Optimization: Cache config value to avoid repeated property access
|
||||
var banType = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType;
|
||||
var checkIpBans = banType != 0;
|
||||
int banType = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType;
|
||||
bool checkIpBans = banType != 0;
|
||||
|
||||
// Optimization: Pre-filter only ACTIVE bans to avoid checking status in loop
|
||||
var activeBans = _banCache.Values.Where(b => b.StatusEnum == BanStatus.ACTIVE);
|
||||
IEnumerable<BanRecord> activeBans = _banCache.Values.Where(b => b.StatusEnum == BanStatus.ACTIVE);
|
||||
|
||||
foreach (var ban in activeBans)
|
||||
foreach (BanRecord? ban in activeBans)
|
||||
{
|
||||
// Index by Steam ID
|
||||
if (ban.PlayerSteamId.HasValue)
|
||||
{
|
||||
var steamId = ban.PlayerSteamId.Value;
|
||||
if (!steamIdIndex.TryGetValue(steamId, out var steamList))
|
||||
ulong steamId = ban.PlayerSteamId.Value;
|
||||
if (!steamIdIndex.TryGetValue(steamId, out List<BanRecord>? steamList))
|
||||
{
|
||||
steamList = new List<BanRecord>();
|
||||
steamIdIndex[steamId] = steamList;
|
||||
|
|
@ -409,9 +442,9 @@ internal class CacheManager: IDisposable
|
|||
|
||||
// Index by IP (only if IP bans are enabled)
|
||||
if (checkIpBans && !string.IsNullOrEmpty(ban.PlayerIp) &&
|
||||
IpHelper.TryConvertIpToUint(ban.PlayerIp, out var ipUInt))
|
||||
IpHelper.TryConvertIpToUint(ban.PlayerIp, out uint ipUInt))
|
||||
{
|
||||
if (!ipIndex.TryGetValue(ipUInt, out var ipList))
|
||||
if (!ipIndex.TryGetValue(ipUInt, out List<BanRecord>? ipList))
|
||||
{
|
||||
ipList = new List<BanRecord>();
|
||||
ipIndex[ipUInt] = ipList;
|
||||
|
|
@ -424,19 +457,19 @@ internal class CacheManager: IDisposable
|
|||
Volatile.Write(ref _steamIdIndex, steamIdIndex);
|
||||
Volatile.Write(ref _ipIndex, ipIndex);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all ban records currently stored in the cache.
|
||||
/// </summary>
|
||||
/// <returns>List of all <see cref="BanRecord"/> objects.</returns>
|
||||
public List<BanRecord> GetAllBans() => _banCache.Values.ToList();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves only active ban records from the cache.
|
||||
/// </summary>
|
||||
/// <returns>List of active <see cref="BanRecord"/> objects.</returns>
|
||||
public List<BanRecord> GetActiveBans() => _banCache.Values.Where(b => b.StatusEnum == BanStatus.ACTIVE).ToList();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all ban records for a specific player by their Steam ID.
|
||||
/// </summary>
|
||||
|
|
@ -446,7 +479,7 @@ internal class CacheManager: IDisposable
|
|||
// ponytail: O(n) over all cached bans per call; add an all-status index if this gets hot.
|
||||
public List<BanRecord> GetPlayerBansBySteamId(ulong steamId) =>
|
||||
_banCache.Values.Where(b => b.PlayerSteamId == steamId).ToList();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets all known Steam accounts that have used the specified IP address.
|
||||
/// </summary>
|
||||
|
|
@ -456,16 +489,16 @@ internal class CacheManager: IDisposable
|
|||
/// </returns>
|
||||
public List<(ulong SteamId, DateTime UsedAt, string PlayerName)> GetAccountsByIp(string ipAddress)
|
||||
{
|
||||
var ipAsUint = IpHelper.IpToUint(ipAddress);
|
||||
var results = new List<(ulong, DateTime, string)>();
|
||||
uint ipAsUint = IpHelper.IpToUint(ipAddress);
|
||||
List<(ulong, DateTime, string)> results = new List<(ulong, DateTime, string)>();
|
||||
|
||||
// Optimization: Direct lookup using HashSet.Contains instead of TryGetValue
|
||||
var searchRecord = new IpRecord(ipAsUint, default, null!);
|
||||
IpRecord searchRecord = new IpRecord(ipAsUint, default, null!);
|
||||
|
||||
foreach (var (steamId, ipSet) in _playerIpsCache)
|
||||
foreach ((ulong steamId, HashSet<IpRecord>? ipSet) in _playerIpsCache)
|
||||
{
|
||||
// Optimization: Single pass through the set
|
||||
foreach (var entry in ipSet)
|
||||
foreach (IpRecord entry in ipSet)
|
||||
{
|
||||
if (entry.Ip == ipAsUint)
|
||||
{
|
||||
|
|
@ -488,11 +521,15 @@ internal class CacheManager: IDisposable
|
|||
|
||||
private bool IsIpBanned(string ipAddress)
|
||||
{
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0) return false;
|
||||
var ipUInt = IpHelper.IpToUint(ipAddress);
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint ipUInt = IpHelper.IpToUint(ipAddress);
|
||||
return !_cachedIgnoredIps.Contains(ipUInt) && _ipIndex.ContainsKey(ipUInt);
|
||||
}
|
||||
|
||||
|
||||
// public bool IsPlayerBanned(ulong? steamId, string? ipAddress)
|
||||
// {
|
||||
// if (steamId != null && _steamIdIndex.ContainsKey(steamId.Value))
|
||||
|
|
@ -518,13 +555,13 @@ internal class CacheManager: IDisposable
|
|||
public bool IsPlayerBanned(string playerName, ulong? steamId, string? ipAddress)
|
||||
{
|
||||
BanRecord? record;
|
||||
if (steamId.HasValue && _steamIdIndex.TryGetValue(steamId.Value, out var steamRecords))
|
||||
if (steamId.HasValue && _steamIdIndex.TryGetValue(steamId.Value, out List<BanRecord>? steamRecords))
|
||||
{
|
||||
record = steamRecords.FirstOrDefault(r => r.StatusEnum == BanStatus.ACTIVE);
|
||||
if (record != null)
|
||||
{
|
||||
// Double-check the ban is still active in cache (handle race conditions)
|
||||
if (_banCache.TryGetValue(record.Id, out var cachedBan) && cachedBan.StatusEnum == BanStatus.ACTIVE)
|
||||
if (_banCache.TryGetValue(record.Id, out BanRecord? cachedBan) && cachedBan.StatusEnum == BanStatus.ACTIVE)
|
||||
{
|
||||
if ((string.IsNullOrEmpty(record.PlayerIp) && !string.IsNullOrEmpty(ipAddress)) ||
|
||||
(!record.PlayerSteamId.HasValue))
|
||||
|
|
@ -536,21 +573,31 @@ internal class CacheManager: IDisposable
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0 || string.IsNullOrEmpty(ipAddress))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(ipAddress) ||
|
||||
!IpHelper.TryConvertIpToUint(ipAddress, out var ipUInt) ||
|
||||
!IpHelper.TryConvertIpToUint(ipAddress, out uint ipUInt) ||
|
||||
_cachedIgnoredIps.Contains(ipUInt) ||
|
||||
!_ipIndex.TryGetValue(ipUInt, out var ipRecords)) return false;
|
||||
!_ipIndex.TryGetValue(ipUInt, out List<BanRecord>? ipRecords))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
record = ipRecords.FirstOrDefault(r => r.StatusEnum == BanStatus.ACTIVE);
|
||||
if (record == null) return false;
|
||||
if (record == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Double-check the ban is still active in cache (handle race conditions)
|
||||
if (!_banCache.TryGetValue(record.Id, out var cachedBanIp) || cachedBanIp.StatusEnum != BanStatus.ACTIVE)
|
||||
if (!_banCache.TryGetValue(record.Id, out BanRecord? cachedBanIp) || cachedBanIp.StatusEnum != BanStatus.ACTIVE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((string.IsNullOrEmpty(record.PlayerIp) && !string.IsNullOrEmpty(ipAddress)) ||
|
||||
(!record.PlayerSteamId.HasValue && steamId.HasValue))
|
||||
|
|
@ -560,7 +607,7 @@ internal class CacheManager: IDisposable
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// public bool IsPlayerOrAnyIpBanned(ulong steamId, string? ipAddress)
|
||||
// {
|
||||
// if (_steamIdIndex.ContainsKey(steamId))
|
||||
|
|
@ -638,16 +685,18 @@ internal class CacheManager: IDisposable
|
|||
/// <returns>True if the player or their known IPs are banned, otherwise false.</returns>
|
||||
public bool IsPlayerOrAnyIpBanned(string playerName, ulong steamId, string? ipAddress)
|
||||
{
|
||||
if (_steamIdIndex.TryGetValue(steamId, out var steamBans))
|
||||
if (_steamIdIndex.TryGetValue(steamId, out List<BanRecord>? steamBans))
|
||||
{
|
||||
var activeBan = steamBans.FirstOrDefault(b => b.StatusEnum == BanStatus.ACTIVE);
|
||||
BanRecord? activeBan = steamBans.FirstOrDefault(b => b.StatusEnum == BanStatus.ACTIVE);
|
||||
if (activeBan != null)
|
||||
{
|
||||
// Double-check the ban is still active in cache (handle race conditions)
|
||||
if (_banCache.TryGetValue(activeBan.Id, out var cachedBan) && cachedBan.StatusEnum == BanStatus.ACTIVE)
|
||||
if (_banCache.TryGetValue(activeBan.Id, out BanRecord? cachedBan) && cachedBan.StatusEnum == BanStatus.ACTIVE)
|
||||
{
|
||||
if (string.IsNullOrEmpty(activeBan.PlayerName) || string.IsNullOrEmpty(activeBan.PlayerIp) && !string.IsNullOrEmpty(ipAddress))
|
||||
{
|
||||
_ = Task.Run(() => UpdatePlayerData(playerName, steamId, ipAddress));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -655,25 +704,34 @@ internal class CacheManager: IDisposable
|
|||
}
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0 || string.IsNullOrEmpty(ipAddress))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IpHelper.TryConvertIpToUint(ipAddress, out var ipUInt))
|
||||
if (!IpHelper.TryConvertIpToUint(ipAddress, out uint ipUInt))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_cachedIgnoredIps.Contains(ipUInt))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Direct ip ban (ban record has player_ip set)
|
||||
if (_ipIndex.TryGetValue(ipUInt, out var ipBanRecords))
|
||||
if (_ipIndex.TryGetValue(ipUInt, out List<BanRecord>? ipBanRecords))
|
||||
{
|
||||
var ipBan = ipBanRecords.FirstOrDefault(r => r.StatusEnum == BanStatus.ACTIVE);
|
||||
if (ipBan != null && _banCache.TryGetValue(ipBan.Id, out var cachedIpBan) && cachedIpBan.StatusEnum == BanStatus.ACTIVE)
|
||||
BanRecord? ipBan = ipBanRecords.FirstOrDefault(r => r.StatusEnum == BanStatus.ACTIVE);
|
||||
if (ipBan != null && _banCache.TryGetValue(ipBan.Id, out BanRecord? cachedIpBan) && cachedIpBan.StatusEnum == BanStatus.ACTIVE)
|
||||
{
|
||||
var expireOldIpBans = CS2_SimpleAdmin.Instance.Config.OtherSettings.ExpireOldIpBans;
|
||||
int expireOldIpBans = CS2_SimpleAdmin.Instance.Config.OtherSettings.ExpireOldIpBans;
|
||||
if (expireOldIpBans <= 0 || ipBan.Created >= Time.ActualDateTime().AddDays(-expireOldIpBans))
|
||||
{
|
||||
if (string.IsNullOrEmpty(ipBan.PlayerName))
|
||||
{
|
||||
ipBan.PlayerName = playerName;
|
||||
}
|
||||
|
||||
ipBan.PlayerSteamId ??= steamId;
|
||||
_ = Task.Run(() => UpdatePlayerData(playerName, steamId, ipAddress));
|
||||
return true;
|
||||
|
|
@ -684,19 +742,32 @@ internal class CacheManager: IDisposable
|
|||
// Multiaccount ban - check if other accounts using current ip are banned
|
||||
if (!_playerIpsCache.IsEmpty)
|
||||
{
|
||||
foreach (var (otherSteamId, ipSet) in _playerIpsCache)
|
||||
foreach ((ulong otherSteamId, HashSet<IpRecord>? ipSet) in _playerIpsCache)
|
||||
{
|
||||
// Skip current player
|
||||
if (otherSteamId == steamId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this ip is in the other accounts ip history
|
||||
if (ipSet.All(record => record.Ip != ipUInt)) continue;
|
||||
if (ipSet.All(record => record.Ip != ipUInt))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Found another account using this ip - check if its banned
|
||||
if (!_steamIdIndex.TryGetValue(otherSteamId, out var otherSteamBans)) continue;
|
||||
var activeBan = otherSteamBans.FirstOrDefault(b => b.StatusEnum == BanStatus.ACTIVE);
|
||||
if (activeBan == null || !_banCache.TryGetValue(activeBan.Id, out var cachedBan) ||
|
||||
cachedBan.StatusEnum != BanStatus.ACTIVE) continue;
|
||||
if (!_steamIdIndex.TryGetValue(otherSteamId, out List<BanRecord>? otherSteamBans))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BanRecord? activeBan = otherSteamBans.FirstOrDefault(b => b.StatusEnum == BanStatus.ACTIVE);
|
||||
if (activeBan == null || !_banCache.TryGetValue(activeBan.Id, out BanRecord? cachedBan) ||
|
||||
cachedBan.StatusEnum != BanStatus.ACTIVE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_ = Task.Run(() => UpdatePlayerData(playerName, steamId, ipAddress));
|
||||
return true;
|
||||
}
|
||||
|
|
@ -705,32 +776,44 @@ internal class CacheManager: IDisposable
|
|||
// Multiaccount ban - check if this player used any ip where other banned accounts are connected
|
||||
// Search sa_players_ips for all accounts sharing the same ips as current player
|
||||
if (!CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_playerIpsCache.TryGetValue(steamId, out var playerIps))
|
||||
if (!_playerIpsCache.TryGetValue(steamId, out HashSet<IpRecord>? playerIps))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// For each ip the player used (current or historical)
|
||||
foreach (var playerIpRecord in playerIps)
|
||||
foreach (IpRecord playerIpRecord in playerIps)
|
||||
{
|
||||
// Search sa_players_ips for other accounts using this same ip (as uint)
|
||||
foreach (var (otherSteamId, otherIpSet) in _playerIpsCache)
|
||||
foreach ((ulong otherSteamId, HashSet<IpRecord>? otherIpSet) in _playerIpsCache)
|
||||
{
|
||||
if (otherSteamId == steamId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this other account used the player ip
|
||||
if (otherIpSet.All(record => record.Ip != playerIpRecord.Ip))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this other account is banned
|
||||
if (!_steamIdIndex.TryGetValue(otherSteamId, out var otherSteamBans))
|
||||
if (!_steamIdIndex.TryGetValue(otherSteamId, out List<BanRecord>? otherSteamBans))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var activeBan = otherSteamBans.FirstOrDefault(b => b.StatusEnum == BanStatus.ACTIVE);
|
||||
if (activeBan == null || !_banCache.TryGetValue(activeBan.Id, out var cachedBan) ||
|
||||
BanRecord? activeBan = otherSteamBans.FirstOrDefault(b => b.StatusEnum == BanStatus.ACTIVE);
|
||||
if (activeBan == null || !_banCache.TryGetValue(activeBan.Id, out BanRecord? cachedBan) ||
|
||||
cachedBan.StatusEnum != BanStatus.ACTIVE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_ = Task.Run(() => UpdatePlayerData(playerName, steamId, ipAddress));
|
||||
return true;
|
||||
|
|
@ -749,15 +832,19 @@ internal class CacheManager: IDisposable
|
|||
public bool HasIpForPlayer(ulong steamId, string ipAddress)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ipAddress))
|
||||
{
|
||||
return false;
|
||||
|
||||
if (!IpHelper.TryConvertIpToUint(ipAddress, out var ipUint))
|
||||
return false;
|
||||
}
|
||||
|
||||
return _playerIpsCache.TryGetValue(steamId, out var ipData) &&
|
||||
if (!IpHelper.TryConvertIpToUint(ipAddress, out uint ipUint))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _playerIpsCache.TryGetValue(steamId, out HashSet<IpRecord>? ipData) &&
|
||||
ipData.Contains(new IpRecord(ipUint, default, null!));
|
||||
}
|
||||
|
||||
|
||||
// public bool HasIpForPlayer(ulong steamId, string ipAddress)
|
||||
// {
|
||||
// if (string.IsNullOrWhiteSpace(ipAddress))
|
||||
|
|
@ -778,9 +865,11 @@ internal class CacheManager: IDisposable
|
|||
private async Task UpdatePlayerData(string? playerName, ulong? steamId, string? ipAddress)
|
||||
{
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var baseSql = """
|
||||
string baseSql = """
|
||||
UPDATE sa_bans
|
||||
SET
|
||||
player_ip = COALESCE(player_ip, @PlayerIP),
|
||||
|
|
@ -796,44 +885,56 @@ internal class CacheManager: IDisposable
|
|||
baseSql += " AND server_id = @ServerId;";
|
||||
}
|
||||
|
||||
string? playerIp = ipAddress;
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0
|
||||
|| string.IsNullOrEmpty(ipAddress)
|
||||
|| CS2_SimpleAdmin.Instance.Config.OtherSettings.IgnoredIps.Contains(ipAddress))
|
||||
{
|
||||
playerIp = null;
|
||||
}
|
||||
|
||||
var parameters = new
|
||||
{
|
||||
PlayerSteamID = steamId,
|
||||
PlayerIP = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0
|
||||
|| string.IsNullOrEmpty(ipAddress)
|
||||
|| CS2_SimpleAdmin.Instance.Config.OtherSettings.IgnoredIps.Contains(ipAddress)
|
||||
? null
|
||||
: ipAddress,
|
||||
PlayerName = string.IsNullOrEmpty(playerName) ? string.Empty : playerName,
|
||||
PlayerIP = playerIp,
|
||||
PlayerName = playerName ?? string.Empty,
|
||||
CurrentTime = Time.ActualDateTime(),
|
||||
CS2_SimpleAdmin.ServerId
|
||||
};
|
||||
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
|
||||
await using DbConnection connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
await connection.ExecuteAsync(baseSql, parameters);
|
||||
|
||||
if (steamId.HasValue && _steamIdIndex.TryGetValue(steamId.Value, out var steamRecords))
|
||||
|
||||
if (steamId.HasValue && _steamIdIndex.TryGetValue(steamId.Value, out List<BanRecord>? steamRecords))
|
||||
{
|
||||
foreach (var rec in steamRecords.Where(r => r.StatusEnum == BanStatus.ACTIVE))
|
||||
foreach (BanRecord? rec in steamRecords.Where(r => r.StatusEnum == BanStatus.ACTIVE))
|
||||
{
|
||||
if (string.IsNullOrEmpty(rec.PlayerIp) && !string.IsNullOrEmpty(ipAddress))
|
||||
{
|
||||
rec.PlayerIp = ipAddress;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(rec.PlayerName) && !string.IsNullOrEmpty(playerName))
|
||||
{
|
||||
rec.PlayerName = playerName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ipAddress) && IpHelper.TryConvertIpToUint(ipAddress, out var ipUInt)
|
||||
&& _ipIndex.TryGetValue(ipUInt, out var ipRecords))
|
||||
if (!string.IsNullOrEmpty(ipAddress) && IpHelper.TryConvertIpToUint(ipAddress, out uint ipUInt)
|
||||
&& _ipIndex.TryGetValue(ipUInt, out List<BanRecord>? ipRecords))
|
||||
{
|
||||
foreach (var rec in ipRecords.Where(r => r.StatusEnum == BanStatus.ACTIVE))
|
||||
foreach (BanRecord? rec in ipRecords.Where(r => r.StatusEnum == BanStatus.ACTIVE))
|
||||
{
|
||||
if (!rec.PlayerSteamId.HasValue && steamId.HasValue)
|
||||
{
|
||||
rec.PlayerSteamId = steamId;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(rec.PlayerName) && !string.IsNullOrEmpty(playerName))
|
||||
{
|
||||
rec.PlayerName = playerName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -847,16 +948,30 @@ internal class CacheManager: IDisposable
|
|||
_playerIpsCache.Clear();
|
||||
_cachedIgnoredIps.Clear();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Clears and disposes of all cached data and marks the object as disposed.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Clear();
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
private static string NameOrDefault(string? name, string defaultName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
return defaultName;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
public class IpRecordComparer : IEqualityComparer<IpRecord>
|
||||
|
|
@ -866,4 +981,4 @@ public class IpRecordComparer : IEqualityComparer<IpRecord>
|
|||
|
||||
public int GetHashCode(IpRecord obj)
|
||||
=> obj.Ip.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ namespace CS2_SimpleAdmin.Managers;
|
|||
|
||||
public class DiscordManager(string webhookUrl)
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sends a plain text message asynchronously to the configured Discord webhook URL.
|
||||
/// </summary>
|
||||
|
|
@ -16,23 +16,23 @@ public class DiscordManager(string webhookUrl)
|
|||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||
public async Task SendMessageAsync(string message)
|
||||
{
|
||||
var client = CS2_SimpleAdmin.HttpClient;
|
||||
var payload = new
|
||||
HttpClient client = CS2_SimpleAdmin.HttpClient;
|
||||
Dictionary<string, object> payload = new Dictionary<string, object>
|
||||
{
|
||||
content = message
|
||||
["content"] = message
|
||||
};
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
JsonSerializerOptions options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = false
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(payload, options);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
string json = JsonSerializer.Serialize(payload, options);
|
||||
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
try
|
||||
{
|
||||
var response = await client.PostAsync(webhookUrl, content);
|
||||
HttpResponseMessage response = await client.PostAsync(webhookUrl, content);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
|
|
@ -54,48 +54,75 @@ public class DiscordManager(string webhookUrl)
|
|||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||
public async Task SendEmbedAsync(Embed embed)
|
||||
{
|
||||
var httpClient = CS2_SimpleAdmin.HttpClient;
|
||||
HttpClient httpClient = CS2_SimpleAdmin.HttpClient;
|
||||
|
||||
var payload = new
|
||||
// Same JSON as before: every key is written, null when the part is empty
|
||||
Dictionary<string, object?> embedPayload = new Dictionary<string, object?>
|
||||
{
|
||||
embeds = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
color = embed.Color,
|
||||
title = !string.IsNullOrEmpty(embed.Title) ? embed.Title : null,
|
||||
description = !string.IsNullOrEmpty(embed.Description) ? embed.Description : null,
|
||||
thumbnail = !string.IsNullOrEmpty(embed.ThumbnailUrl) ? new { url = embed.ThumbnailUrl } : null,
|
||||
image = !string.IsNullOrEmpty(embed.ImageUrl) ? new { url = embed.ImageUrl } : null,
|
||||
footer = !string.IsNullOrEmpty(embed.Footer?.Text) ? new { text = embed.Footer.Text, icon_url = embed.Footer.IconUrl } : null,
|
||||
timestamp = embed.Timestamp,
|
||||
fields = embed.Fields.Count > 0 ? embed.Fields.Select(field => new
|
||||
{
|
||||
name = field.Name,
|
||||
value = field.Value,
|
||||
inline = field.Inline
|
||||
}).ToArray() : null
|
||||
}
|
||||
}
|
||||
["color"] = embed.Color,
|
||||
["title"] = null,
|
||||
["description"] = null,
|
||||
["thumbnail"] = null,
|
||||
["image"] = null,
|
||||
["footer"] = null,
|
||||
["timestamp"] = embed.Timestamp,
|
||||
["fields"] = null
|
||||
};
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
if (!string.IsNullOrEmpty(embed.Title))
|
||||
{
|
||||
embedPayload["title"] = embed.Title;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(embed.Description))
|
||||
{
|
||||
embedPayload["description"] = embed.Description;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(embed.ThumbnailUrl))
|
||||
{
|
||||
embedPayload["thumbnail"] = new Dictionary<string, object?> { ["url"] = embed.ThumbnailUrl };
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(embed.ImageUrl))
|
||||
{
|
||||
embedPayload["image"] = new Dictionary<string, object?> { ["url"] = embed.ImageUrl };
|
||||
}
|
||||
|
||||
if (embed.Footer != null && !string.IsNullOrEmpty(embed.Footer.Text))
|
||||
{
|
||||
embedPayload["footer"] = new Dictionary<string, object?> { ["text"] = embed.Footer.Text, ["icon_url"] = embed.Footer.IconUrl };
|
||||
}
|
||||
|
||||
if (embed.Fields.Count > 0)
|
||||
{
|
||||
embedPayload["fields"] = embed.Fields
|
||||
.Select(field => new Dictionary<string, object?> { ["name"] = field.Name, ["value"] = field.Value, ["inline"] = field.Inline })
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
Dictionary<string, object> payload = new Dictionary<string, object>
|
||||
{
|
||||
["embeds"] = new[] { embedPayload }
|
||||
};
|
||||
|
||||
JsonSerializerOptions options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = false
|
||||
};
|
||||
|
||||
var jsonPayload = JsonSerializer.Serialize(payload, options);
|
||||
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
|
||||
string jsonPayload = JsonSerializer.Serialize(payload, options);
|
||||
StringContent content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await httpClient.PostAsync(webhookUrl, content);
|
||||
HttpResponseMessage response = await httpClient.PostAsync(webhookUrl, content);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var errorMessage = await response.Content.ReadAsStringAsync();
|
||||
string errorMessage = await response.Content.ReadAsStringAsync();
|
||||
CS2_SimpleAdmin._logger?.LogError($"Failed to send embed: {response.StatusCode} - {errorMessage}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts a hexadecimal color string (e.g. "#FF0000") to its integer representation.
|
||||
/// </summary>
|
||||
|
|
@ -135,13 +162,13 @@ public class Embed
|
|||
/// <param name="inline">Whether the field should be displayed inline with other fields.</param>
|
||||
public void AddField(string name, string value, bool inline)
|
||||
{
|
||||
var field = new EmbedField
|
||||
EmbedField field = new EmbedField
|
||||
{
|
||||
Name = name,
|
||||
Value = value,
|
||||
Inline = inline
|
||||
};
|
||||
|
||||
|
||||
Fields.Add(field);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using CS2_SimpleAdmin.Database;
|
||||
using System.Data.Common;
|
||||
using CS2_SimpleAdmin.Database;
|
||||
using CS2_SimpleAdminApi;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -18,12 +19,15 @@ internal class MuteManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>Mute ID if successfully added, otherwise null.</returns>
|
||||
public async Task<int?> MutePlayer(PlayerInfo player, PlayerInfo? issuer, string reason, int time = 0, int type = 0)
|
||||
{
|
||||
if (databaseProvider == null) return null;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
var futureTime = now.AddMinutes(time);
|
||||
DateTime now = Time.ActualDateTime();
|
||||
DateTime futureTime = now.AddMinutes(time);
|
||||
|
||||
var muteType = type switch
|
||||
string muteType = type switch
|
||||
{
|
||||
1 => "MUTE",
|
||||
2 => "SILENCE",
|
||||
|
|
@ -32,10 +36,10 @@ internal class MuteManager(IDatabaseProvider? databaseProvider)
|
|||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetAddMuteQuery(true);
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
string sql = databaseProvider.GetAddMuteQuery(true);
|
||||
|
||||
var muteId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
int? muteId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
{
|
||||
playerSteamid = player.SteamId.SteamId64,
|
||||
playerName = player.Name,
|
||||
|
|
@ -69,12 +73,15 @@ internal class MuteManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>Mute ID if successful, otherwise null.</returns>
|
||||
public async Task<int?> AddMuteBySteamid(ulong playerSteamId, PlayerInfo? issuer, string reason, int time = 0, int type = 0)
|
||||
{
|
||||
if (databaseProvider == null) return null;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
var futureTime = now.AddMinutes(time);
|
||||
DateTime now = Time.ActualDateTime();
|
||||
DateTime futureTime = now.AddMinutes(time);
|
||||
|
||||
var muteType = type switch
|
||||
string muteType = type switch
|
||||
{
|
||||
1 => "MUTE",
|
||||
2 => "SILENCE",
|
||||
|
|
@ -83,10 +90,10 @@ internal class MuteManager(IDatabaseProvider? databaseProvider)
|
|||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetAddMuteQuery(false);
|
||||
|
||||
var muteId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
string sql = databaseProvider.GetAddMuteQuery(false);
|
||||
|
||||
int? muteId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
{
|
||||
playerSteamid = playerSteamId,
|
||||
adminSteamid = issuer?.SteamId.SteamId64 ?? 0,
|
||||
|
|
@ -114,7 +121,10 @@ internal class MuteManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>List of active mute records; empty list if none or on error.</returns>
|
||||
public async Task<List<dynamic>> IsPlayerMuted(string steamId)
|
||||
{
|
||||
if (databaseProvider == null) return [];
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(steamId))
|
||||
{
|
||||
|
|
@ -123,18 +133,20 @@ internal class MuteManager(IDatabaseProvider? databaseProvider)
|
|||
|
||||
#if DEBUG
|
||||
if (CS2_SimpleAdmin._logger != null)
|
||||
{
|
||||
CS2_SimpleAdmin._logger.LogCritical($"IsPlayerMuted for {steamId}");
|
||||
}
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var currentTime = Time.ActualDateTime();
|
||||
|
||||
var sql = databaseProvider.GetIsMutedQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode, CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode);
|
||||
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
DateTime currentTime = Time.ActualDateTime();
|
||||
|
||||
string sql = databaseProvider.GetIsMutedQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode, CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode);
|
||||
|
||||
var parameters = new { PlayerSteamID = steamId, CurrentTime = currentTime, serverid = CS2_SimpleAdmin.ServerId };
|
||||
var activeMutes = (await connection.QueryAsync(sql, parameters)).ToList();
|
||||
List<dynamic> activeMutes = (await connection.QueryAsync(sql, parameters)).ToList();
|
||||
return activeMutes;
|
||||
}
|
||||
catch (Exception)
|
||||
|
|
@ -153,14 +165,17 @@ internal class MuteManager(IDatabaseProvider? databaseProvider)
|
|||
/// </returns>
|
||||
public async Task<(int TotalMutes, int TotalGags, int TotalSilences)> GetPlayerMutes(PlayerInfo playerInfo)
|
||||
{
|
||||
if (databaseProvider == null) return (0,0,0);
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return (0, 0, 0);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetMuteStatsQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
var result = await connection.QuerySingleAsync<(int TotalMutes, int TotalGags, int TotalSilences)>(sql, new
|
||||
string sql = databaseProvider.GetMuteStatsQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
(int TotalMutes, int TotalGags, int TotalSilences) result = await connection.QuerySingleAsync<(int TotalMutes, int TotalGags, int TotalSilences)>(sql, new
|
||||
{
|
||||
PlayerSteamID = playerInfo.SteamId.SteamId64,
|
||||
CS2_SimpleAdmin.ServerId
|
||||
|
|
@ -173,7 +188,7 @@ internal class MuteManager(IDatabaseProvider? databaseProvider)
|
|||
return (0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Processes a batch of online players to update their mute status and remove expired penalties.
|
||||
/// </summary>
|
||||
|
|
@ -181,21 +196,24 @@ internal class MuteManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>Task representing the asynchronous operation.</returns>
|
||||
public async Task CheckOnlineModeMutes(List<(ulong SteamID, int? UserId, int Slot)> players)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
const int batchSize = 20;
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetUpdateMutePassedQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
string sql = databaseProvider.GetUpdateMutePassedQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
|
||||
for (var i = 0; i < players.Count; i += batchSize)
|
||||
for (int i = 0; i < players.Count; i += batchSize)
|
||||
{
|
||||
var batch = players.Skip(i).Take(batchSize);
|
||||
var parametersList = new List<object>();
|
||||
IEnumerable<(ulong SteamID, int? UserId, int Slot)> batch = players.Skip(i).Take(batchSize);
|
||||
List<object> parametersList = new List<object>();
|
||||
|
||||
foreach (var (steamId, _, _) in batch)
|
||||
foreach ((ulong steamId, int? _, int _) in batch)
|
||||
{
|
||||
parametersList.Add(new { PlayerSteamID = steamId, serverid = CS2_SimpleAdmin.ServerId });
|
||||
}
|
||||
|
|
@ -205,11 +223,11 @@ internal class MuteManager(IDatabaseProvider? databaseProvider)
|
|||
|
||||
sql = databaseProvider.GetCheckExpiredMutesQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
|
||||
foreach (var (steamId, _, slot) in players)
|
||||
foreach ((ulong steamId, int? _, int slot) in players)
|
||||
{
|
||||
var muteRecords = await connection.QueryAsync(sql, new { PlayerSteamID = steamId, serverid = CS2_SimpleAdmin.ServerId });
|
||||
IEnumerable<dynamic> muteRecords = await connection.QueryAsync(sql, new { PlayerSteamID = steamId, serverid = CS2_SimpleAdmin.ServerId });
|
||||
|
||||
foreach (var muteRecord in muteRecords)
|
||||
foreach (dynamic muteRecord in muteRecords)
|
||||
{
|
||||
DateTime endDateTime = muteRecord.ends;
|
||||
PlayerPenaltyManager.RemovePenaltiesByDateTime(slot, endDateTime);
|
||||
|
|
@ -230,7 +248,10 @@ internal class MuteManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>Task representing the asynchronous operation.</returns>
|
||||
public async Task UnmutePlayer(string playerPattern, string adminSteamId, string reason, int type = 0)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (playerPattern.Length <= 1)
|
||||
{
|
||||
|
|
@ -239,36 +260,38 @@ internal class MuteManager(IDatabaseProvider? databaseProvider)
|
|||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var muteType = type switch
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
string muteType = type switch
|
||||
{
|
||||
1 => "MUTE",
|
||||
2 => "SILENCE",
|
||||
_ => "GAG"
|
||||
};
|
||||
|
||||
var sqlRetrieveMutes =
|
||||
string sqlRetrieveMutes =
|
||||
databaseProvider.GetRetrieveMutesQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
var mutes = await connection.QueryAsync(sqlRetrieveMutes, new { pattern = playerPattern, muteType, serverid = CS2_SimpleAdmin.ServerId });
|
||||
IEnumerable<dynamic> mutes = await connection.QueryAsync(sqlRetrieveMutes, new { pattern = playerPattern, muteType, serverid = CS2_SimpleAdmin.ServerId });
|
||||
|
||||
var mutesList = mutes as dynamic[] ?? mutes.ToArray();
|
||||
dynamic[] mutesList = mutes as dynamic[] ?? mutes.ToArray();
|
||||
if (mutesList.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sqlAdmin = databaseProvider.GetUnmuteAdminIdQuery();
|
||||
var sqlInsertUnmute = databaseProvider.GetInsertUnmuteQuery(string.IsNullOrEmpty(reason));
|
||||
string sqlAdmin = databaseProvider.GetUnmuteAdminIdQuery();
|
||||
string sqlInsertUnmute = databaseProvider.GetInsertUnmuteQuery(string.IsNullOrEmpty(reason));
|
||||
|
||||
var sqlAdminId = await connection.ExecuteScalarAsync<int?>(sqlAdmin, new { adminSteamId });
|
||||
var adminId = sqlAdminId ?? 0;
|
||||
int? sqlAdminId = await connection.ExecuteScalarAsync<int?>(sqlAdmin, new { adminSteamId });
|
||||
int adminId = sqlAdminId ?? 0;
|
||||
|
||||
foreach (var mute in mutesList)
|
||||
foreach (dynamic mute in mutesList)
|
||||
{
|
||||
int muteId = mute.id;
|
||||
|
||||
int? unmuteId =
|
||||
await connection.ExecuteScalarAsync<int>(sqlInsertUnmute, new { muteId, adminId, reason });
|
||||
|
||||
var sqlUpdateMute = databaseProvider.GetUpdateMuteStatusQuery();
|
||||
string sqlUpdateMute = databaseProvider.GetUpdateMuteStatusQuery();
|
||||
await connection.ExecuteAsync(sqlUpdateMute, new { unmuteId, muteId });
|
||||
}
|
||||
}
|
||||
|
|
@ -284,12 +307,15 @@ internal class MuteManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>Task representing the asynchronous expiration operation.</returns>
|
||||
public async Task ExpireOldMutes()
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetExpireMutesQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode, CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode);
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
string sql = databaseProvider.GetExpireMutesQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode, CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode);
|
||||
await connection.ExecuteAsync(sql, new { CurrentTime = Time.ActualDateTime(), serverid = CS2_SimpleAdmin.ServerId });
|
||||
}
|
||||
catch (Exception)
|
||||
|
|
@ -297,4 +323,4 @@ internal class MuteManager(IDatabaseProvider? databaseProvider)
|
|||
CS2_SimpleAdmin._logger?.LogCritical("Unable to remove expired mutes");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Data.Common;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CS2_SimpleAdmin.Database;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text.Json;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using CS2_SimpleAdmin.Database;
|
||||
|
||||
namespace CS2_SimpleAdmin.Managers;
|
||||
|
||||
|
|
@ -18,58 +19,60 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
|||
/// Retrieves all players' flags and associated data asynchronously.
|
||||
/// </summary>
|
||||
/// <returns>A list of tuples containing player SteamID, name, flags, immunity, and expiration time.</returns>
|
||||
private async Task<List<(ulong, string ,List<string>, int, DateTime?)>> GetAllPlayersFlags()
|
||||
private async Task<List<(ulong, string, List<string>, int, DateTime?)>> GetAllPlayersFlags()
|
||||
{
|
||||
if (databaseProvider == null)
|
||||
return new List<(ulong, string, List<string>, int, DateTime?)>();
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return new List<(ulong, string, List<string>, int, DateTime?)>();
|
||||
}
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
DateTime now = Time.ActualDateTime();
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetAdminsQuery();
|
||||
var admins = (await connection.QueryAsync(sql, new { CurrentTime = now, serverid = CS2_SimpleAdmin.ServerId })).ToList();
|
||||
try
|
||||
{
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
string sql = databaseProvider.GetAdminsQuery();
|
||||
List<dynamic> admins = (await connection.QueryAsync(sql, new { CurrentTime = now, serverid = CS2_SimpleAdmin.ServerId })).ToList();
|
||||
|
||||
var groupedPlayers = admins
|
||||
.GroupBy(r => new { playerSteamId = r.player_steamid, playerName = r.player_name, r.immunity, r.ends })
|
||||
.Select(g =>
|
||||
{
|
||||
ulong steamId = g.Key.playerSteamId switch
|
||||
{
|
||||
long l => (ulong)l,
|
||||
int i => (ulong)i,
|
||||
string s when ulong.TryParse(s, out var parsed) => parsed,
|
||||
_ => 0UL
|
||||
};
|
||||
List<(ulong steamId, string playerName, List<string> flags, int immunity, DateTime? ends)> groupedPlayers = admins
|
||||
.GroupBy(r => new { playerSteamId = r.player_steamid, playerName = r.player_name, r.immunity, r.ends })
|
||||
.Select(g =>
|
||||
{
|
||||
ulong steamId = g.Key.playerSteamId switch
|
||||
{
|
||||
long l => (ulong)l,
|
||||
int i => (ulong)i,
|
||||
string s when ulong.TryParse(s, out ulong parsed) => parsed,
|
||||
_ => 0UL
|
||||
};
|
||||
|
||||
int immunity = g.Key.immunity switch
|
||||
{
|
||||
int i => i,
|
||||
string s when int.TryParse(s, out var parsed) => parsed,
|
||||
_ => 0
|
||||
};
|
||||
int immunity = g.Key.immunity switch
|
||||
{
|
||||
int i => i,
|
||||
string s when int.TryParse(s, out int parsed) => parsed,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
DateTime? ends = g.Key.ends as DateTime?;
|
||||
DateTime? ends = g.Key.ends as DateTime?;
|
||||
|
||||
string playerName = g.Key.playerName as string ?? string.Empty;
|
||||
string playerName = g.Key.playerName as string ?? string.Empty;
|
||||
|
||||
// Dapper returns string here, not dynamic
|
||||
var flags = g.Select(r => r.flag as string ?? string.Empty)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
// Dapper returns string here, not dynamic
|
||||
List<string> flags = g.Select(r => r.flag as string ?? string.Empty)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
return (steamId, playerName, flags, immunity, ends);
|
||||
})
|
||||
.ToList();
|
||||
return (steamId, playerName, flags, immunity, ends);
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return groupedPlayers;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError("Unable to load admins from database! {exception}", ex.Message);
|
||||
throw; // an empty list would strip every admin; abort the reload and keep the current permissions
|
||||
}
|
||||
return groupedPlayers;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError("Unable to load admins from database! {exception}", ex.Message);
|
||||
throw; // an empty list would strip every admin; abort the reload and keep the current permissions
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -78,24 +81,27 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>A dictionary with group names as keys and tuples of flags and immunity as values.</returns>
|
||||
private async Task<Dictionary<string, (List<string>, int)>> GetAllGroupsData()
|
||||
{
|
||||
if (databaseProvider == null) return [];
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetGroupsQuery();
|
||||
var groupData = connection.Query(sql, new { serverid = CS2_SimpleAdmin.ServerId }).ToList();
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
string sql = databaseProvider.GetGroupsQuery();
|
||||
List<dynamic> groupData = connection.Query(sql, new { serverid = CS2_SimpleAdmin.ServerId }).ToList();
|
||||
if (groupData.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var groupInfoDictionary = new Dictionary<string, (List<string>, int)>();
|
||||
foreach (var row in groupData)
|
||||
Dictionary<string, (List<string>, int)> groupInfoDictionary = new Dictionary<string, (List<string>, int)>();
|
||||
foreach (dynamic row in groupData)
|
||||
{
|
||||
var groupName = (string)row.group_name;
|
||||
var flag = (string)row.flag;
|
||||
var immunity = (int)row.immunity;
|
||||
string groupName = (string)row.group_name;
|
||||
string flag = (string)row.flag;
|
||||
int immunity = (int)row.immunity;
|
||||
|
||||
if (!groupInfoDictionary.TryGetValue(groupName, out (List<string>, int) value))
|
||||
{
|
||||
|
|
@ -121,12 +127,12 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
|||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||
public async Task CreateGroupsJsonFile()
|
||||
{
|
||||
var groupsData = await GetAllGroupsData();
|
||||
var jsonData = new Dictionary<string, object>();
|
||||
Dictionary<string, (List<string>, int)> groupsData = await GetAllGroupsData();
|
||||
Dictionary<string, object> jsonData = new Dictionary<string, object>();
|
||||
|
||||
foreach (var kvp in groupsData)
|
||||
foreach (KeyValuePair<string, (List<string>, int)> kvp in groupsData)
|
||||
{
|
||||
var groupData = new Dictionary<string, object>
|
||||
Dictionary<string, object> groupData = new Dictionary<string, object>
|
||||
{
|
||||
["flags"] = kvp.Value.Item1,
|
||||
["immunity"] = kvp.Value.Item2
|
||||
|
|
@ -135,14 +141,14 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
|||
jsonData[kvp.Key] = groupData;
|
||||
}
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
JsonSerializerOptions options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
WriteIndented = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(jsonData, options);
|
||||
var filePath = Path.Combine(CS2_SimpleAdmin.Instance.ModuleDirectory, "data", "groups.json");
|
||||
string json = JsonSerializer.Serialize(jsonData, options);
|
||||
string filePath = Path.Combine(CS2_SimpleAdmin.Instance.ModuleDirectory, "data", "groups.json");
|
||||
await File.WriteAllTextAsync(filePath, json);
|
||||
}
|
||||
|
||||
|
|
@ -153,55 +159,55 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
|||
public async Task<List<(SteamID steamId, DateTime? ends, List<string> flags)>> CreateAdminsJsonFile()
|
||||
{
|
||||
List<(ulong identity, string name, List<string> flags, int immunity, DateTime? ends)> allPlayers = await GetAllPlayersFlags();
|
||||
var validPlayers = allPlayers
|
||||
List<(ulong identity, string name, List<string> flags, int immunity, DateTime? ends)> validPlayers = allPlayers
|
||||
.Where(player => SteamID.TryParse(player.identity.ToString(), out _))
|
||||
.ToList();
|
||||
|
||||
var jsonData = validPlayers
|
||||
.GroupBy(player => player.name)
|
||||
.ToDictionary(
|
||||
group => group.Key,
|
||||
object (group) =>
|
||||
{
|
||||
var consolidatedData = group.Aggregate(
|
||||
new
|
||||
{
|
||||
identity = string.Empty,
|
||||
immunity = 0,
|
||||
flags = new List<string>(),
|
||||
groups = new List<string>()
|
||||
},
|
||||
(acc, player) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(acc.identity) && !string.IsNullOrEmpty(player.identity.ToString()))
|
||||
{
|
||||
acc = acc with { identity = player.identity.ToString() };
|
||||
}
|
||||
Dictionary<string, object> jsonData = validPlayers
|
||||
.GroupBy(player => player.name)
|
||||
.ToDictionary(
|
||||
group => group.Key,
|
||||
object (group) =>
|
||||
{
|
||||
var consolidatedData = group.Aggregate(
|
||||
new
|
||||
{
|
||||
identity = string.Empty,
|
||||
immunity = 0,
|
||||
flags = new List<string>(),
|
||||
groups = new List<string>()
|
||||
},
|
||||
(acc, player) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(acc.identity) && !string.IsNullOrEmpty(player.identity.ToString()))
|
||||
{
|
||||
acc = acc with { identity = player.identity.ToString() };
|
||||
}
|
||||
|
||||
acc = acc with { immunity = Math.Max(acc.immunity, player.immunity) };
|
||||
acc = acc with { immunity = Math.Max(acc.immunity, player.immunity) };
|
||||
|
||||
acc = acc with
|
||||
{
|
||||
flags = acc.flags.Concat(player.flags.Where(flag => flag.StartsWith($"@"))).Distinct().ToList(),
|
||||
groups = acc.groups.Concat(player.flags.Where(flag => flag.StartsWith($"#"))).Distinct().ToList()
|
||||
};
|
||||
acc = acc with
|
||||
{
|
||||
flags = acc.flags.Concat(player.flags.Where(flag => flag.StartsWith($"@"))).Distinct().ToList(),
|
||||
groups = acc.groups.Concat(player.flags.Where(flag => flag.StartsWith($"#"))).Distinct().ToList()
|
||||
};
|
||||
|
||||
return acc;
|
||||
});
|
||||
return acc;
|
||||
});
|
||||
|
||||
return consolidatedData;
|
||||
});
|
||||
return consolidatedData;
|
||||
});
|
||||
|
||||
var newCache = validPlayers.Select(player => (new SteamID(player.identity), player.ends, player.flags)).ToList();
|
||||
List<(SteamID, DateTime? ends, List<string> flags)> newCache = validPlayers.Select(player => (new SteamID(player.identity), player.ends, player.flags)).ToList();
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
JsonSerializerOptions options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(jsonData, options);
|
||||
var filePath = Path.Combine(CS2_SimpleAdmin.Instance.ModuleDirectory, "data", "admins.json");
|
||||
string json = JsonSerializer.Serialize(jsonData, options);
|
||||
string filePath = Path.Combine(CS2_SimpleAdmin.Instance.ModuleDirectory, "data", "admins.json");
|
||||
await File.WriteAllTextAsync(filePath, json);
|
||||
|
||||
return newCache;
|
||||
|
|
@ -214,25 +220,34 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
|||
/// <param name="admins">Admins loaded from the database.</param>
|
||||
public static void ApplyAdminCache(List<(SteamID steamId, DateTime? ends, List<string> flags)> admins)
|
||||
{
|
||||
foreach (var steamId in AdminCache.Keys.ToList())
|
||||
foreach (SteamID? steamId in AdminCache.Keys.ToList())
|
||||
{
|
||||
if (!AdminCache.TryRemove(steamId, out var cached)) continue;
|
||||
if (!AdminCache.TryRemove(steamId, out (DateTime? ExpirationTime, List<string> Flags) cached))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var data = AdminManager.GetPlayerAdminData(steamId);
|
||||
if (data == null) continue;
|
||||
AdminData? data = AdminManager.GetPlayerAdminData(steamId);
|
||||
if (data == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var flagsArray = cached.Flags.ToArray();
|
||||
string[] flagsArray = cached.Flags.ToArray();
|
||||
AdminManager.RemovePlayerPermissions(steamId, flagsArray);
|
||||
AdminManager.RemovePlayerFromGroup(steamId, true, flagsArray);
|
||||
|
||||
data = AdminManager.GetPlayerAdminData(steamId);
|
||||
if (data == null || (data.Flags.Count != 0 && data.Groups.Count != 0)) continue;
|
||||
if (data == null || (data.Flags.Count != 0 && data.Groups.Count != 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AdminManager.ClearPlayerPermissions(steamId);
|
||||
AdminManager.RemovePlayerAdminData(steamId);
|
||||
}
|
||||
|
||||
foreach (var (steamId, ends, flags) in admins)
|
||||
foreach ((SteamID? steamId, DateTime? ends, List<string>? flags) in admins)
|
||||
{
|
||||
AdminCache.TryAdd(steamId, (ends, flags));
|
||||
}
|
||||
|
|
@ -245,13 +260,20 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
|||
/// <param name="globalDelete">Whether to delete the admin globally or only for the current server.</param>
|
||||
public async Task DeleteAdminBySteamId(string playerSteamId, bool globalDelete = false)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
if (string.IsNullOrEmpty(playerSteamId)) return;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(playerSteamId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetDeleteAdminQuery(globalDelete);
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
string sql = databaseProvider.GetDeleteAdminQuery(globalDelete);
|
||||
await connection.ExecuteAsync(sql, new { PlayerSteamID = playerSteamId, CS2_SimpleAdmin.ServerId });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -271,31 +293,52 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
|||
/// <param name="globalAdmin">Whether the admin is global or server-specific.</param>
|
||||
public async Task AddAdminBySteamId(string playerSteamId, string playerName, List<string> flagsList, int immunity = 0, int time = 0, bool globalAdmin = false)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(playerSteamId) || flagsList.Count == 0) return;
|
||||
if (string.IsNullOrEmpty(playerSteamId) || flagsList.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
DateTime? futureTime = time != 0 ? now.AddMinutes(time) : null;
|
||||
DateTime now = Time.ActualDateTime();
|
||||
DateTime? futureTime;
|
||||
|
||||
if (time != 0)
|
||||
{
|
||||
futureTime = now.AddMinutes(time);
|
||||
}
|
||||
else
|
||||
{
|
||||
futureTime = null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var insertAdminSql = databaseProvider.GetAddAdminQuery();
|
||||
var adminId = await connection.ExecuteScalarAsync<int>(insertAdminSql, new
|
||||
int? serverId = CS2_SimpleAdmin.ServerId;
|
||||
if (globalAdmin)
|
||||
{
|
||||
serverId = null;
|
||||
}
|
||||
|
||||
string insertAdminSql = databaseProvider.GetAddAdminQuery();
|
||||
int adminId = await connection.ExecuteScalarAsync<int>(insertAdminSql, new
|
||||
{
|
||||
playerSteamId,
|
||||
playerName,
|
||||
immunity,
|
||||
ends = futureTime,
|
||||
created = now,
|
||||
serverid = globalAdmin ? null : CS2_SimpleAdmin.ServerId
|
||||
serverid = serverId
|
||||
});
|
||||
|
||||
foreach (var flag in flagsList)
|
||||
foreach (string flag in flagsList)
|
||||
{
|
||||
var insertFlagsSql = databaseProvider.GetAddAdminFlagsQuery();
|
||||
string insertFlagsSql = databaseProvider.GetAddAdminFlagsQuery();
|
||||
await connection.ExecuteAsync(insertFlagsSql, new
|
||||
{
|
||||
adminId,
|
||||
|
|
@ -323,24 +366,30 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
|||
/// <param name="globalGroup">Whether the group is global or server-specific.</param>
|
||||
public async Task AddGroup(string groupName, List<string> flagsList, int immunity = 0, bool globalGroup = false)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(groupName) || flagsList.Count == 0) return;
|
||||
if (string.IsNullOrEmpty(groupName) || flagsList.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var insertGroup = databaseProvider.GetAddGroupQuery();
|
||||
var groupId = await connection.ExecuteScalarAsync<int>(insertGroup, new
|
||||
string insertGroup = databaseProvider.GetAddGroupQuery();
|
||||
int groupId = await connection.ExecuteScalarAsync<int>(insertGroup, new
|
||||
{
|
||||
groupName,
|
||||
immunity
|
||||
});
|
||||
|
||||
foreach (var flag in flagsList)
|
||||
foreach (string flag in flagsList)
|
||||
{
|
||||
var insertFlagsSql = databaseProvider.GetAddGroupFlagsQuery();
|
||||
string insertFlagsSql = databaseProvider.GetAddGroupFlagsQuery();
|
||||
await connection.ExecuteAsync(insertFlagsSql, new
|
||||
{
|
||||
groupId,
|
||||
|
|
@ -348,8 +397,14 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
|||
});
|
||||
}
|
||||
|
||||
var insertGroupServer = databaseProvider.GetAddGroupServerQuery();
|
||||
await connection.ExecuteAsync(insertGroupServer, new { groupId, server_id = globalGroup ? null : CS2_SimpleAdmin.ServerId });
|
||||
string insertGroupServer = databaseProvider.GetAddGroupServerQuery();
|
||||
int? groupServerId = CS2_SimpleAdmin.ServerId;
|
||||
if (globalGroup)
|
||||
{
|
||||
groupServerId = null;
|
||||
}
|
||||
|
||||
await connection.ExecuteAsync(insertGroupServer, new { groupId, server_id = groupServerId });
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.ReloadAdmins(null);
|
||||
|
|
@ -367,14 +422,20 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
|||
/// <param name="groupName">Name of the group to delete.</param>
|
||||
public async Task DeleteGroup(string groupName)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(groupName)) return;
|
||||
if (string.IsNullOrEmpty(groupName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetDeleteGroupQuery();
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
string sql = databaseProvider.GetDeleteGroupQuery();
|
||||
await connection.ExecuteAsync(sql, new { groupName });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -388,13 +449,16 @@ public class PermissionManager(IDatabaseProvider? databaseProvider)
|
|||
/// </summary>
|
||||
public async Task DeleteOldAdmins()
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetDeleteOldAdminsQuery();
|
||||
string sql = databaseProvider.GetDeleteOldAdminsQuery();
|
||||
await connection.ExecuteAsync(sql, new { CurrentTime = Time.ActualDateTime() });
|
||||
}
|
||||
catch (Exception)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Data.Common;
|
||||
using System.Globalization;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
|
|
@ -37,20 +38,30 @@ internal class PlayerManager
|
|||
return;
|
||||
}
|
||||
|
||||
var userId = player.UserId.Value;
|
||||
var slot = player.Slot;
|
||||
var steamId = player.SteamID;
|
||||
var playerName = !string.IsNullOrEmpty(player.PlayerName)
|
||||
? player.PlayerName
|
||||
: CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
||||
var ipAddress = player.IpAddress?.Split(":")[0];
|
||||
int userId = player.UserId.Value;
|
||||
int slot = player.Slot;
|
||||
ulong steamId = player.SteamID;
|
||||
string playerName;
|
||||
|
||||
if (CS2_SimpleAdmin.RenamedPlayers.TryGetValue(steamId, out var renamedTo))
|
||||
if (!string.IsNullOrEmpty(player.PlayerName))
|
||||
{
|
||||
playerName = player.PlayerName;
|
||||
}
|
||||
else
|
||||
{
|
||||
playerName = CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
||||
}
|
||||
string? ipAddress = player.IpAddress?.Split(":")[0];
|
||||
|
||||
if (CS2_SimpleAdmin.RenamedPlayers.TryGetValue(steamId, out string? renamedTo))
|
||||
{
|
||||
player.Rename(renamedTo);
|
||||
}
|
||||
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null || CS2_SimpleAdmin.Instance.CacheManager == null) return;
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null || CS2_SimpleAdmin.Instance.CacheManager == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
|
|
@ -62,14 +73,8 @@ internal class PlayerManager
|
|||
await SavePlayerIpAddress(steamId, playerName, ipAddress);
|
||||
|
||||
// Always check bans first, regardless of PlayersInfo state
|
||||
var isBanned = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType switch
|
||||
{
|
||||
0 => CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(playerName, steamId, null),
|
||||
_ => CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp
|
||||
? CS2_SimpleAdmin.Instance.CacheManager.IsPlayerOrAnyIpBanned(playerName, steamId,
|
||||
ipAddress)
|
||||
: CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(playerName, steamId, ipAddress)
|
||||
};
|
||||
bool isBanned = IsBanned(CS2_SimpleAdmin.Instance.CacheManager, CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType,
|
||||
CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp, playerName, steamId, ipAddress);
|
||||
|
||||
CS2_SimpleAdmin._logger?.LogInformation($"[BAN CHECK] Player {playerName} ({steamId}) IP: {ipAddress} - BanType: {CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType} - CheckMultiAccounts: {CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp} - isBanned: {isBanned}");
|
||||
|
||||
|
|
@ -87,7 +92,7 @@ internal class PlayerManager
|
|||
|
||||
if (!CS2_SimpleAdmin.PlayersInfo.ContainsKey(steamId))
|
||||
{
|
||||
var playerInfo = new PlayerInfo(userId, slot, new SteamID(steamId), playerName, ipAddress);
|
||||
PlayerInfo playerInfo = new PlayerInfo(userId, slot, new SteamID(steamId), playerName, ipAddress);
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId] = playerInfo;
|
||||
|
||||
if (_config.OtherSettings.CheckMultiAccountsByIp && ipAddress != null)
|
||||
|
|
@ -109,8 +114,8 @@ internal class PlayerManager
|
|||
|
||||
if (CS2_SimpleAdmin.PlayersInfo.TryGetValue(steamId, out PlayerInfo? value)) // Temp skip
|
||||
{
|
||||
var warns = await CS2_SimpleAdmin.Instance.WarnManager.GetPlayerWarns(value, false);
|
||||
var (totalMutes, totalGags, totalSilences) =
|
||||
List<dynamic> warns = await CS2_SimpleAdmin.Instance.WarnManager.GetPlayerWarns(value, false);
|
||||
(int totalMutes, int totalGags, int totalSilences) =
|
||||
await CS2_SimpleAdmin.Instance.MuteManager.GetPlayerMutes(value);
|
||||
value.TotalBans = CS2_SimpleAdmin.Instance.CacheManager
|
||||
?.GetPlayerBansBySteamId(value.SteamId.SteamId64)
|
||||
|
|
@ -120,13 +125,13 @@ internal class PlayerManager
|
|||
value.TotalSilences = totalSilences;
|
||||
value.TotalWarns = warns.Count;
|
||||
|
||||
var activeMutes =
|
||||
List<dynamic> activeMutes =
|
||||
await CS2_SimpleAdmin.Instance.MuteManager.IsPlayerMuted(value.SteamId.SteamId64
|
||||
.ToString());
|
||||
|
||||
if (activeMutes.Count > 0)
|
||||
{
|
||||
foreach (var mute in activeMutes)
|
||||
foreach (dynamic mute in activeMutes)
|
||||
{
|
||||
string muteType = mute.type;
|
||||
DateTime ends = mute.ends;
|
||||
|
|
@ -171,7 +176,7 @@ internal class PlayerManager
|
|||
{
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
foreach (var admin in Helper.GetValidPlayers()
|
||||
foreach (CCSPlayerController? admin in Helper.GetValidPlayers()
|
||||
.Where(p => (AdminManager.PlayerHasPermissions(
|
||||
new SteamID(p.SteamID),
|
||||
"@css/kick") ||
|
||||
|
|
@ -182,7 +187,11 @@ internal class PlayerManager
|
|||
!CS2_SimpleAdmin.AdminDisabledJoinComms
|
||||
.Contains(p.SteamID)))
|
||||
{
|
||||
if (CS2_SimpleAdmin._localizer == null || admin == player) continue;
|
||||
if (CS2_SimpleAdmin._localizer == null || admin == player)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
admin.SendLocalizedMessage(CS2_SimpleAdmin._localizer,
|
||||
"sa_admin_penalty_info",
|
||||
player.PlayerName,
|
||||
|
|
@ -195,10 +204,10 @@ internal class PlayerManager
|
|||
|
||||
if (CS2_SimpleAdmin.PlayersInfo[steamId].AccountsAssociated.Count >= 2)
|
||||
{
|
||||
var associatedAcccountsChunks =
|
||||
List<IEnumerable<(ulong SteamId, string PlayerName)>> associatedAcccountsChunks =
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId].AccountsAssociated.ChunkBy(5)
|
||||
.ToList();
|
||||
foreach (var chunk in associatedAcccountsChunks)
|
||||
foreach (IEnumerable<(ulong SteamId, string PlayerName)> chunk in associatedAcccountsChunks)
|
||||
{
|
||||
admin.SendLocalizedMessage(CS2_SimpleAdmin._localizer,
|
||||
"sa_admin_associated_accounts",
|
||||
|
|
@ -235,17 +244,22 @@ internal class PlayerManager
|
|||
/// <param name="type">Optional filter: bans, gags, mutes, silences or warns.</param>
|
||||
public async Task<List<dynamic>> GetPenaltyHistory(ulong steamId, string? type = null)
|
||||
{
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null) return [];
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
var sql = CS2_SimpleAdmin.DatabaseProvider.GetPenaltyHistoryQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
var rows = (await connection.QueryAsync(sql, new { PlayerSteamID = steamId, serverid = CS2_SimpleAdmin.ServerId })).ToList();
|
||||
await using DbConnection connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
string sql = CS2_SimpleAdmin.DatabaseProvider.GetPenaltyHistoryQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
List<dynamic> rows = (await connection.QueryAsync(sql, new { PlayerSteamID = steamId, serverid = CS2_SimpleAdmin.ServerId })).ToList();
|
||||
|
||||
// Filter word is the plural (bans, gags, mutes, silences, warns); rows carry BAN/GAG/MUTE/SILENCE/WARN
|
||||
if (type != null)
|
||||
{
|
||||
rows.RemoveAll(r => !type.Equals((string)r.type + "s", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
|
@ -263,7 +277,7 @@ internal class PlayerManager
|
|||
value switch
|
||||
{
|
||||
DateTime d => d,
|
||||
string s when DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsed) => parsed,
|
||||
string s when DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime parsed) => parsed,
|
||||
_ => null
|
||||
};
|
||||
|
||||
|
|
@ -272,17 +286,22 @@ internal class PlayerManager
|
|||
/// </summary>
|
||||
public async Task LoadRenamedPlayers()
|
||||
{
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null) return;
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
var rows = await connection.QueryAsync<(long steamId, string name)>(CS2_SimpleAdmin.DatabaseProvider.GetRenamesQuery());
|
||||
await using DbConnection connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
IEnumerable<(long steamId, string name)> rows = await connection.QueryAsync<(long steamId, string name)>(CS2_SimpleAdmin.DatabaseProvider.GetRenamesQuery());
|
||||
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
foreach (var (steamId, name) in rows)
|
||||
foreach ((long steamId, string? name) in rows)
|
||||
{
|
||||
CS2_SimpleAdmin.RenamedPlayers[(ulong)steamId] = name;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -298,15 +317,22 @@ internal class PlayerManager
|
|||
/// <param name="name">Forced name, or null/empty to remove the rename.</param>
|
||||
public async Task SaveRenamedPlayer(ulong steamId, string? name)
|
||||
{
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null) return;
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
await connection.ExecuteAsync(CS2_SimpleAdmin.DatabaseProvider.GetDeleteRenameQuery(), new { steamId });
|
||||
}
|
||||
else
|
||||
{
|
||||
await connection.ExecuteAsync(CS2_SimpleAdmin.DatabaseProvider.GetUpsertRenameQuery(), new { steamId, name });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -321,16 +347,18 @@ internal class PlayerManager
|
|||
private async Task SavePlayerIpAddress(ulong steamId, string playerName, string? ipAddress)
|
||||
{
|
||||
if (!_config.OtherSettings.CheckMultiAccountsByIp || ipAddress == null || CS2_SimpleAdmin.DatabaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
|
||||
var steamId64 = steamId;
|
||||
var ipUint = IpHelper.IpToUint(ipAddress);
|
||||
ulong steamId64 = steamId;
|
||||
uint ipUint = IpHelper.IpToUint(ipAddress);
|
||||
|
||||
var upsertQuery = CS2_SimpleAdmin.DatabaseProvider.GetUpsertPlayerIpQuery();
|
||||
string upsertQuery = CS2_SimpleAdmin.DatabaseProvider.GetUpsertPlayerIpQuery();
|
||||
|
||||
await connection.ExecuteAsync(upsertQuery, new
|
||||
{
|
||||
|
|
@ -365,40 +393,46 @@ internal class PlayerManager
|
|||
{
|
||||
CS2_SimpleAdmin.Instance.AddTimer(5f, () =>
|
||||
{
|
||||
foreach (var (steamid, name) in CS2_SimpleAdmin.RenamedPlayers)
|
||||
foreach ((ulong steamid, string? name) in CS2_SimpleAdmin.RenamedPlayers)
|
||||
{
|
||||
var player = Helper.GetPlayerFromSteamid64(steamid);
|
||||
if (player == null || !player.IsValid || player.PlayerName == name) continue;
|
||||
CCSPlayerController? player = Helper.GetPlayerFromSteamid64(steamid);
|
||||
if (player == null || !player.IsValid || player.PlayerName == name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
player.Rename(name);
|
||||
}
|
||||
}, TimerFlags.REPEAT);
|
||||
|
||||
|
||||
CS2_SimpleAdmin.Instance.PlayersTimer = CS2_SimpleAdmin.Instance.AddTimer(61.0f, () =>
|
||||
{
|
||||
#if DEBUG
|
||||
CS2_SimpleAdmin._logger?.LogCritical("[OnMapStart] Expired check");
|
||||
#endif
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null)
|
||||
{
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// Optimization: Get players once and avoid allocating anonymous types
|
||||
var validPlayers = Helper.GetValidPlayers();
|
||||
List<CCSPlayerController> validPlayers = Helper.GetValidPlayers();
|
||||
// Use ValueTuple instead of anonymous type - better performance and less allocations
|
||||
var tempPlayers = new List<(string PlayerName, ulong SteamID, string? IpAddress, int? UserId, int Slot)>(validPlayers.Count);
|
||||
foreach (var p in validPlayers)
|
||||
List<(string PlayerName, ulong SteamID, string? IpAddress, int? UserId, int Slot)> tempPlayers = new List<(string PlayerName, ulong SteamID, string? IpAddress, int? UserId, int Slot)>(validPlayers.Count);
|
||||
foreach (CCSPlayerController p in validPlayers)
|
||||
{
|
||||
tempPlayers.Add((p.PlayerName, p.SteamID, p.IpAddress, p.UserId, p.Slot));
|
||||
}
|
||||
|
||||
var pluginInstance = CS2_SimpleAdmin.Instance;
|
||||
var config = _config.OtherSettings; // Cache config access
|
||||
CS2_SimpleAdmin pluginInstance = CS2_SimpleAdmin.Instance;
|
||||
OtherSettings config = _config.OtherSettings; // Cache config access
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// Run all expire tasks in parallel
|
||||
var expireTasks = new[]
|
||||
Task[] expireTasks = new[]
|
||||
{
|
||||
pluginInstance.BanManager.ExpireOldBans(),
|
||||
pluginInstance.MuteManager.ExpireOldMutes(),
|
||||
|
|
@ -415,7 +449,7 @@ internal class PlayerManager
|
|||
|
||||
if (ex is AggregateException aggregate)
|
||||
{
|
||||
foreach (var inner in aggregate.InnerExceptions)
|
||||
foreach (Exception inner in aggregate.InnerExceptions)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError($"Inner exception: {inner.Message}");
|
||||
}
|
||||
|
|
@ -423,28 +457,24 @@ internal class PlayerManager
|
|||
}
|
||||
|
||||
if (pluginInstance.CacheManager == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Optimization: Cache ban type and multi-account check to avoid repeated config access
|
||||
var banType = config.BanType;
|
||||
var checkMultiAccounts = config.CheckMultiAccountsByIp;
|
||||
int banType = config.BanType;
|
||||
bool checkMultiAccounts = config.CheckMultiAccountsByIp;
|
||||
|
||||
var bannedPlayers = new List<(string PlayerName, ulong SteamID, string? IpAddress, int? UserId, int Slot)>();
|
||||
List<(string PlayerName, ulong SteamID, string? IpAddress, int? UserId, int Slot)> bannedPlayers = new List<(string PlayerName, ulong SteamID, string? IpAddress, int? UserId, int Slot)>();
|
||||
|
||||
// Manual loop instead of LINQ - better performance
|
||||
foreach (var player in tempPlayers)
|
||||
foreach ((string PlayerName, ulong SteamID, string? IpAddress, int? UserId, int Slot) player in tempPlayers)
|
||||
{
|
||||
var playerName = player.PlayerName;
|
||||
var steamId = player.SteamID;
|
||||
var ip = player.IpAddress?.Split(':')[0];
|
||||
string playerName = player.PlayerName;
|
||||
ulong steamId = player.SteamID;
|
||||
string? ip = player.IpAddress?.Split(':')[0];
|
||||
|
||||
bool isBanned = banType switch
|
||||
{
|
||||
0 => pluginInstance.CacheManager.IsPlayerBanned(playerName, steamId, null),
|
||||
_ => checkMultiAccounts
|
||||
? pluginInstance.CacheManager.IsPlayerOrAnyIpBanned(playerName, steamId, ip)
|
||||
: pluginInstance.CacheManager.IsPlayerBanned(playerName, steamId, ip)
|
||||
};
|
||||
bool isBanned = IsBanned(pluginInstance.CacheManager, banType, checkMultiAccounts, playerName, steamId, ip);
|
||||
|
||||
if (isBanned)
|
||||
{
|
||||
|
|
@ -454,23 +484,29 @@ internal class PlayerManager
|
|||
|
||||
if (bannedPlayers.Count > 0)
|
||||
{
|
||||
foreach (var player in bannedPlayers)
|
||||
foreach ((string PlayerName, ulong SteamID, string? IpAddress, int? UserId, int Slot) player in bannedPlayers)
|
||||
{
|
||||
if (!player.UserId.HasValue) continue;
|
||||
if (!player.UserId.HasValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
if (Helper.GetPlayerFromSteamid64(player.SteamID) != null)
|
||||
{
|
||||
Helper.KickPlayer((int)player.UserId,
|
||||
NetworkDisconnectionReason.NETWORK_DISCONNECT_REJECT_BANNED);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (config.TimeMode == 0)
|
||||
{
|
||||
// Optimization: Manual projection instead of LINQ
|
||||
var onlinePlayers = new List<(ulong, int?, int)>(tempPlayers.Count);
|
||||
foreach (var player in tempPlayers)
|
||||
List<(ulong, int?, int)> onlinePlayers = new List<(ulong, int?, int)>(tempPlayers.Count);
|
||||
foreach ((string PlayerName, ulong SteamID, string? IpAddress, int? UserId, int Slot) player in tempPlayers)
|
||||
{
|
||||
onlinePlayers.Add((player.SteamID, player.UserId, player.Slot));
|
||||
}
|
||||
|
|
@ -485,14 +521,16 @@ internal class PlayerManager
|
|||
try
|
||||
{
|
||||
// Optimization: Process penalties without LINQ allocations
|
||||
var players = Helper.GetValidPlayers();
|
||||
foreach (var player in players)
|
||||
List<CCSPlayerController> players = Helper.GetValidPlayers();
|
||||
foreach (CCSPlayerController player in players)
|
||||
{
|
||||
if (!PlayerPenaltyManager.IsSlotInPenalties(player.Slot))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var isMuted = PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Mute, out _);
|
||||
var isSilenced = PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence, out _);
|
||||
bool isMuted = PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Mute, out _);
|
||||
bool isSilenced = PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence, out _);
|
||||
|
||||
// Only reset voice flags if not muted or silenced
|
||||
if (!isMuted && !isSilenced)
|
||||
|
|
@ -509,4 +547,22 @@ internal class PlayerManager
|
|||
}
|
||||
}, TimerFlags.REPEAT);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// BanType 0 checks the SteamID only; otherwise the IP too, and every IP the player used when multi-account checks are on.
|
||||
/// </summary>
|
||||
private static bool IsBanned(CacheManager cacheManager, int banType, bool checkMultiAccounts, string playerName, ulong steamId, string? ipAddress)
|
||||
{
|
||||
if (banType == 0)
|
||||
{
|
||||
return cacheManager.IsPlayerBanned(playerName, steamId, null);
|
||||
}
|
||||
|
||||
if (checkMultiAccounts)
|
||||
{
|
||||
return cacheManager.IsPlayerOrAnyIpBanned(playerName, steamId, ipAddress);
|
||||
}
|
||||
|
||||
return cacheManager.IsPlayerBanned(playerName, steamId, ipAddress);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using CS2_SimpleAdminApi;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Concurrent;
|
||||
using CS2_SimpleAdminApi;
|
||||
|
||||
namespace CS2_SimpleAdmin.Managers;
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ public static class PlayerPenaltyManager
|
|||
Penalties.AddOrUpdate(slot,
|
||||
(_) =>
|
||||
{
|
||||
var dict = new Dictionary<PenaltyType, List<(DateTime, int, bool)>>
|
||||
Dictionary<PenaltyType, List<(DateTime, int, bool)>> dict = new Dictionary<PenaltyType, List<(DateTime, int, bool)>>
|
||||
{
|
||||
[penaltyType] = [(endDateTime, durationInMinutes, false)]
|
||||
};
|
||||
|
|
@ -28,7 +28,7 @@ public static class PlayerPenaltyManager
|
|||
},
|
||||
(_, existingDict) =>
|
||||
{
|
||||
if (!existingDict.TryGetValue(penaltyType, out var value))
|
||||
if (!existingDict.TryGetValue(penaltyType, out List<(DateTime EndDateTime, int Duration, bool Passed)>? value))
|
||||
{
|
||||
value = new List<(DateTime, int, bool)>();
|
||||
existingDict[penaltyType] = value;
|
||||
|
|
@ -50,21 +50,27 @@ public static class PlayerPenaltyManager
|
|||
{
|
||||
endDateTime = null;
|
||||
|
||||
if (!Penalties.TryGetValue(slot, out var penaltyDict) ||
|
||||
!penaltyDict.TryGetValue(penaltyType, out var penaltiesList)) return false;
|
||||
if (!Penalties.TryGetValue(slot, out Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>>? penaltyDict) ||
|
||||
!penaltyDict.TryGetValue(penaltyType, out List<(DateTime EndDateTime, int Duration, bool Passed)>? penaltiesList))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode == 0)
|
||||
{
|
||||
if (penaltiesList.Count == 0) return false;
|
||||
|
||||
if (penaltiesList.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
endDateTime = penaltiesList.First().EndDateTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
DateTime now = Time.ActualDateTime();
|
||||
|
||||
// Check if any active penalties exist
|
||||
foreach (var penalty in penaltiesList.ToList())
|
||||
foreach ((DateTime EndDateTime, int Duration, bool Passed) penalty in penaltiesList.ToList())
|
||||
{
|
||||
// Check if the penalty is still active
|
||||
if (penalty.Duration > 0 && now >= penalty.EndDateTime)
|
||||
|
|
@ -95,14 +101,14 @@ public static class PlayerPenaltyManager
|
|||
/// <returns>A list of penalties if found, otherwise an empty list.</returns>
|
||||
public static List<(DateTime EndDateTime, int Duration, bool Passed)> GetPlayerPenalties(int slot, PenaltyType penaltyType)
|
||||
{
|
||||
if (Penalties.TryGetValue(slot, out var penaltyDict) &&
|
||||
penaltyDict.TryGetValue(penaltyType, out var penaltiesList))
|
||||
if (Penalties.TryGetValue(slot, out Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>>? penaltyDict) &&
|
||||
penaltyDict.TryGetValue(penaltyType, out List<(DateTime EndDateTime, int Duration, bool Passed)>? penaltiesList))
|
||||
{
|
||||
return penaltiesList;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all penalties for a player across multiple penalty types.
|
||||
/// </summary>
|
||||
|
|
@ -113,11 +119,11 @@ public static class PlayerPenaltyManager
|
|||
{
|
||||
List<(DateTime EndDateTime, int Duration, bool Passed)> result = [];
|
||||
|
||||
if (Penalties.TryGetValue(slot, out var penaltyDict))
|
||||
if (Penalties.TryGetValue(slot, out Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>>? penaltyDict))
|
||||
{
|
||||
foreach (var type in penaltyType)
|
||||
foreach (PenaltyType type in penaltyType)
|
||||
{
|
||||
if (penaltyDict.TryGetValue(type, out var penaltiesList))
|
||||
if (penaltyDict.TryGetValue(type, out List<(DateTime EndDateTime, int Duration, bool Passed)>? penaltiesList))
|
||||
{
|
||||
result.AddRange(penaltiesList);
|
||||
}
|
||||
|
|
@ -126,7 +132,7 @@ public static class PlayerPenaltyManager
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all penalties for a player across all penalty types.
|
||||
/// </summary>
|
||||
|
|
@ -134,12 +140,12 @@ public static class PlayerPenaltyManager
|
|||
/// <returns>A dictionary with penalty types as keys and lists of penalties as values.</returns>
|
||||
public static Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>> GetAllPlayerPenalties(int slot)
|
||||
{
|
||||
// Check if the player has any penalties in the dictionary
|
||||
return Penalties.TryGetValue(slot, out var penaltyDict) ?
|
||||
// Return all penalty types and their respective penalties for the player
|
||||
penaltyDict :
|
||||
// If the player has no penalties, return an empty dictionary
|
||||
new Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>>();
|
||||
if (Penalties.TryGetValue(slot, out Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>>? penaltyDict))
|
||||
{
|
||||
return penaltyDict;
|
||||
}
|
||||
|
||||
return new Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -179,7 +185,7 @@ public static class PlayerPenaltyManager
|
|||
/// <param name="penaltyType">The penalty type to remove.</param>
|
||||
public static void RemovePenaltiesByType(int slot, PenaltyType penaltyType)
|
||||
{
|
||||
if (Penalties.TryGetValue(slot, out var penaltyDict) &&
|
||||
if (Penalties.TryGetValue(slot, out Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>>? penaltyDict) &&
|
||||
penaltyDict.ContainsKey(penaltyType))
|
||||
{
|
||||
penaltyDict.Remove(penaltyType);
|
||||
|
|
@ -193,15 +199,21 @@ public static class PlayerPenaltyManager
|
|||
/// <param name="dateTime">The end datetime of penalties to mark as passed.</param>
|
||||
public static void RemovePenaltiesByDateTime(int slot, DateTime dateTime)
|
||||
{
|
||||
if (!Penalties.TryGetValue(slot, out var penaltyDict)) return;
|
||||
|
||||
foreach (var penaltiesList in penaltyDict.Values)
|
||||
if (!Penalties.TryGetValue(slot, out Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>>? penaltyDict))
|
||||
{
|
||||
for (var i = 0; i < penaltiesList.Count; i++)
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (List<(DateTime EndDateTime, int Duration, bool Passed)> penaltiesList in penaltyDict.Values)
|
||||
{
|
||||
for (int i = 0; i < penaltiesList.Count; i++)
|
||||
{
|
||||
if (penaltiesList[i].EndDateTime != dateTime) continue;
|
||||
if (penaltiesList[i].EndDateTime != dateTime)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Create a copy of the penalty
|
||||
var penalty = penaltiesList[i];
|
||||
(DateTime EndDateTime, int Duration, bool Passed) penalty = penaltiesList[i];
|
||||
|
||||
// Update the end datetime of the copied penalty to the current datetime
|
||||
penalty.Passed = true;
|
||||
|
|
@ -223,10 +235,10 @@ public static class PlayerPenaltyManager
|
|||
{
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode == 0)
|
||||
{
|
||||
foreach (var (playerSlot, penaltyDict) in Penalties.ToList()) // Use ToList to avoid modification while iterating
|
||||
foreach ((int playerSlot, Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>>? penaltyDict) in Penalties.ToList()) // Use ToList to avoid modification while iterating
|
||||
{
|
||||
// Remove expired penalties for the player
|
||||
foreach (var penaltiesList in penaltyDict.Values)
|
||||
foreach (List<(DateTime EndDateTime, int Duration, bool Passed)>? penaltiesList in penaltyDict.Values)
|
||||
{
|
||||
penaltiesList.RemoveAll(p => p is { Duration: > 0, Passed: true });
|
||||
}
|
||||
|
|
@ -241,10 +253,10 @@ public static class PlayerPenaltyManager
|
|||
return;
|
||||
}
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
foreach (var (playerSlot, penaltyDict) in Penalties.ToList()) // Use ToList to avoid modification while iterating
|
||||
DateTime now = Time.ActualDateTime();
|
||||
foreach ((int playerSlot, Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>>? penaltyDict) in Penalties.ToList()) // Use ToList to avoid modification while iterating
|
||||
{
|
||||
foreach (var penaltiesList in penaltyDict.Values)
|
||||
foreach (List<(DateTime EndDateTime, int Duration, bool Passed)>? penaltiesList in penaltyDict.Values)
|
||||
{
|
||||
penaltiesList.RemoveAll(p => p.Duration > 0 && now >= p.EndDateTime);
|
||||
}
|
||||
|
|
@ -255,4 +267,4 @@ public static class PlayerPenaltyManager
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using System.Data.Common;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -15,13 +15,15 @@ public class ServerManager
|
|||
/// </summary>
|
||||
public static void CheckHibernationStatus()
|
||||
{
|
||||
var convar = ConVar.Find("sv_hibernate_when_empty");
|
||||
ConVar? convar = ConVar.Find("sv_hibernate_when_empty");
|
||||
if (convar == null || !convar.GetPrimitiveValue<bool>())
|
||||
{
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
CS2_SimpleAdmin._logger?.LogError("Detected setting \"sv_hibernate_when_empty true\", set false to make plugin work properly");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initiates the asynchronous process to load server data such as IP address, port, hostname, and RCON password.
|
||||
/// Handles retry attempts if IP address is not immediately available.
|
||||
|
|
@ -33,11 +35,14 @@ public class ServerManager
|
|||
{
|
||||
CS2_SimpleAdmin.Instance.AddTimer(1.0f, () =>
|
||||
{
|
||||
if (CS2_SimpleAdmin.ServerLoaded || CS2_SimpleAdmin.DatabaseProvider == null) return;
|
||||
if (CS2_SimpleAdmin.ServerLoaded || CS2_SimpleAdmin.DatabaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Optimization: Get server IP once and reuse
|
||||
var serverIp = Helper.GetServerIp();
|
||||
var isInvalidIp = string.IsNullOrEmpty(serverIp) || serverIp.StartsWith("0.0.0");
|
||||
string serverIp = Helper.GetServerIp();
|
||||
bool isInvalidIp = string.IsNullOrEmpty(serverIp) || serverIp.StartsWith("0.0.0");
|
||||
|
||||
// Check if we've exceeded retry limit with invalid IP
|
||||
if (_getIpTryCount > 32 && isInvalidIp)
|
||||
|
|
@ -49,8 +54,8 @@ public class ServerManager
|
|||
}
|
||||
|
||||
// Optimization: Cache ConVar lookups
|
||||
var ipConVar = ConVar.Find("ip");
|
||||
var ipAddress = ipConVar?.StringValue;
|
||||
ConVar? ipConVar = ConVar.Find("ip");
|
||||
string? ipAddress = ipConVar?.StringValue;
|
||||
|
||||
// Use Helper IP if ConVar IP is invalid
|
||||
if (string.IsNullOrEmpty(ipAddress) || ipAddress.StartsWith("0.0.0"))
|
||||
|
|
@ -67,20 +72,20 @@ public class ServerManager
|
|||
}
|
||||
|
||||
// Optimization: Cache remaining ConVar lookups
|
||||
var hostportConVar = ConVar.Find("hostport");
|
||||
var hostnameConVar = ConVar.Find("hostname");
|
||||
var rconPasswordConVar = ConVar.Find("rcon_password");
|
||||
ConVar? hostportConVar = ConVar.Find("hostport");
|
||||
ConVar? hostnameConVar = ConVar.Find("hostname");
|
||||
ConVar? rconPasswordConVar = ConVar.Find("rcon_password");
|
||||
|
||||
var address = $"{ipAddress}:{hostportConVar?.GetPrimitiveValue<int>()}";
|
||||
var hostname = hostnameConVar?.StringValue ?? CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
||||
var rconPassword = rconPasswordConVar?.StringValue ?? "";
|
||||
string address = $"{ipAddress}:{hostportConVar?.GetPrimitiveValue<int>()}";
|
||||
string hostname = hostnameConVar?.StringValue ?? CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
||||
string rconPassword = rconPasswordConVar?.StringValue ?? "";
|
||||
CS2_SimpleAdmin.IpAddress = address;
|
||||
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
int? serverId = await connection.ExecuteScalarAsync<int?>(
|
||||
"SELECT id FROM sa_servers WHERE address = @address",
|
||||
new { address });
|
||||
|
|
@ -126,8 +131,8 @@ public class ServerManager
|
|||
|
||||
if (CS2_SimpleAdmin.Instance.Config.EnableMetrics)
|
||||
{
|
||||
var queryString = $"?address={address}&hostname={hostname}";
|
||||
var client = CS2_SimpleAdmin.HttpClient;
|
||||
string queryString = $"?address={address}&hostname={hostname}";
|
||||
HttpClient client = CS2_SimpleAdmin.HttpClient;
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -139,8 +144,8 @@ public class ServerManager
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
CS2_SimpleAdmin.SimpleAdminApi?.OnSimpleAdminReadyEvent();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using CS2_SimpleAdmin.Database;
|
||||
using System.Data.Common;
|
||||
using CS2_SimpleAdmin.Database;
|
||||
using CS2_SimpleAdminApi;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -17,17 +18,20 @@ internal class WarnManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>The identifier of the inserted warning, or null if the operation failed.</returns>
|
||||
public async Task<int?> WarnPlayer(PlayerInfo player, PlayerInfo? issuer, string reason, int time = 0)
|
||||
{
|
||||
if (databaseProvider == null) return null;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
var futureTime = now.AddMinutes(time);
|
||||
DateTime now = Time.ActualDateTime();
|
||||
DateTime futureTime = now.AddMinutes(time);
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetAddWarnQuery(true);
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
string sql = databaseProvider.GetAddWarnQuery(true);
|
||||
|
||||
var warnId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
int? warnId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
{
|
||||
playerSteamid = player.SteamId.SteamId64,
|
||||
playerName = player.Name,
|
||||
|
|
@ -42,7 +46,7 @@ internal class WarnManager(IDatabaseProvider? databaseProvider)
|
|||
|
||||
return warnId;
|
||||
}
|
||||
catch(Exception)
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
|
@ -58,17 +62,20 @@ internal class WarnManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>The identifier of the inserted warning, or null if the operation failed.</returns>
|
||||
public async Task<int?> AddWarnBySteamid(ulong playerSteamId, PlayerInfo? issuer, string reason, int time = 0)
|
||||
{
|
||||
if (databaseProvider == null) return null;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
var futureTime = now.AddMinutes(time);
|
||||
DateTime now = Time.ActualDateTime();
|
||||
DateTime futureTime = now.AddMinutes(time);
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetAddWarnQuery(false);
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
string sql = databaseProvider.GetAddWarnQuery(false);
|
||||
|
||||
var warnId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
int? warnId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
{
|
||||
playerSteamid = playerSteamId,
|
||||
adminSteamid = issuer?.SteamId.SteamId64 ?? 0,
|
||||
|
|
@ -96,15 +103,18 @@ internal class WarnManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>A list of dynamic objects representing warnings, or an empty list if none found or on failure.</returns>
|
||||
public async Task<List<dynamic>> GetPlayerWarns(PlayerInfo player, bool active = true)
|
||||
{
|
||||
if (databaseProvider == null) return [];
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetPlayerWarnsQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode, active);
|
||||
string sql = databaseProvider.GetPlayerWarnsQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode, active);
|
||||
var parameters = new { PlayerSteamID = player.SteamId.SteamId64, serverid = CS2_SimpleAdmin.ServerId };
|
||||
var warns = await connection.QueryAsync<dynamic>(sql, parameters);
|
||||
IEnumerable<dynamic> warns = await connection.QueryAsync<dynamic>(sql, parameters);
|
||||
|
||||
return warns.ToList();
|
||||
}
|
||||
|
|
@ -122,14 +132,17 @@ internal class WarnManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>The count of warnings as an integer, or 0 if none found or on failure.</returns>
|
||||
public async Task<int> GetPlayerWarnsCount(ulong steamId, bool active = true)
|
||||
{
|
||||
if (databaseProvider == null) return 0;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetPlayerWarnsCountQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode, active);
|
||||
var warnsCount = await connection.ExecuteScalarAsync<int>(sql, new { PlayerSteamID = steamId, serverid = CS2_SimpleAdmin.ServerId });
|
||||
string sql = databaseProvider.GetPlayerWarnsCountQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode, active);
|
||||
int warnsCount = await connection.ExecuteScalarAsync<int>(sql, new { PlayerSteamID = steamId, serverid = CS2_SimpleAdmin.ServerId });
|
||||
return warnsCount;
|
||||
}
|
||||
catch (Exception)
|
||||
|
|
@ -146,13 +159,16 @@ internal class WarnManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
public async Task UnwarnPlayer(PlayerInfo player, int warnId)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetUnwarnByIdQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
string sql = databaseProvider.GetUnwarnByIdQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
await connection.ExecuteAsync(sql, new { steamid = player.SteamId.SteamId64, warnId, serverid = CS2_SimpleAdmin.ServerId });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -160,7 +176,7 @@ internal class WarnManager(IDatabaseProvider? databaseProvider)
|
|||
CS2_SimpleAdmin._logger?.LogCritical($"Unable to remove warn + {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes the most recent warning matching a player pattern (usually SteamID string).
|
||||
/// </summary>
|
||||
|
|
@ -168,13 +184,16 @@ internal class WarnManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
public async Task UnwarnPlayer(string playerPattern)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetUnwarnLastQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
string sql = databaseProvider.GetUnwarnLastQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
await connection.ExecuteAsync(sql, new { steamid = playerPattern, serverid = CS2_SimpleAdmin.ServerId });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -189,13 +208,16 @@ internal class WarnManager(IDatabaseProvider? databaseProvider)
|
|||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
public async Task ExpireOldWarns()
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
if (databaseProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
await using DbConnection connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetExpireWarnsQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
string sql = databaseProvider.GetExpireWarnsQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
await connection.ExecuteAsync(sql, new { CurrentTime = Time.ActualDateTime(), serverid = CS2_SimpleAdmin.ServerId });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -203,4 +225,4 @@ internal class WarnManager(IDatabaseProvider? databaseProvider)
|
|||
CS2_SimpleAdmin._logger?.LogCritical($"Unable to remove expired warns + {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,75 +0,0 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Menu;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class AdminMenu
|
||||
{
|
||||
public static void OpenMenu(CCSPlayerController admin)
|
||||
{
|
||||
MenuManager.Instance.OpenMainMenu(admin);
|
||||
}
|
||||
|
||||
public static IMenu? CreateMenu(string title, Action<CCSPlayerController>? backAction = null)
|
||||
{
|
||||
return Helper.CreateMenu(title, backAction);
|
||||
// return CS2_SimpleAdmin.Instance.Config.UseChatMenu ? new ChatMenu(title) : new CenterHtmlMenu(title, CS2_SimpleAdmin.Instance);
|
||||
}
|
||||
|
||||
public static void OpenMenu(CCSPlayerController player, IMenu menu)
|
||||
{
|
||||
menu.Open(player);
|
||||
// switch (menu)
|
||||
// {
|
||||
// case CenterHtmlMenu centerHtmlMenu:
|
||||
// MenuManager.OpenCenterHtmlMenu(CS2_SimpleAdmin.Instance, player, centerHtmlMenu);
|
||||
// break;
|
||||
// case ChatMenu chatMenu:
|
||||
// MenuManager.OpenChatMenu(player, chatMenu);
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
|
||||
// public static void OpenMenu(CCSPlayerController admin)
|
||||
// {
|
||||
// if (admin.IsValid == false)
|
||||
// return;
|
||||
//
|
||||
// var localizer = CS2_SimpleAdmin._localizer;
|
||||
// if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic") == false)
|
||||
// {
|
||||
// admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||
// "[SimpleAdmin] " +
|
||||
// (localizer?["sa_no_permission"] ?? "You do not have permissions to use this command")
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// var menu = CreateMenu(localizer?["sa_title"] ?? "SimpleAdmin");
|
||||
// List<ChatMenuOptionData> options =
|
||||
// [
|
||||
// new(localizer?["sa_menu_players_manage"] ?? "Players Manage", () => ManagePlayersMenu.OpenMenu(admin)),
|
||||
// new(localizer?["sa_menu_server_manage"] ?? "Server Manage", () => ManageServerMenu.OpenMenu(admin)),
|
||||
// new(localizer?["sa_menu_fun_commands"] ?? "Fun Commands", () => FunActionsMenu.OpenMenu(admin)),
|
||||
// ];
|
||||
//
|
||||
// var customCommands = CS2_SimpleAdmin.Instance.Config.CustomServerCommands;
|
||||
// if (customCommands.Count > 0)
|
||||
// {
|
||||
// options.Add(new ChatMenuOptionData(localizer?["sa_menu_custom_commands"] ?? "Custom Commands", () => CustomCommandsMenu.OpenMenu(admin)));
|
||||
// }
|
||||
//
|
||||
// if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/root"))
|
||||
// options.Add(new ChatMenuOptionData(localizer?["sa_menu_admins_manage"] ?? "Admins Manage", () => ManageAdminsMenu.OpenMenu(admin)));
|
||||
//
|
||||
// foreach (var menuOptionData in options)
|
||||
// {
|
||||
// var menuName = menuOptionData.Name;
|
||||
// menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
||||
// }
|
||||
//
|
||||
// if (menu != null) OpenMenu(admin, menu);
|
||||
// }
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,8 +0,0 @@
|
|||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public class ChatMenuOptionData(string name, Action action, bool disabled = false)
|
||||
{
|
||||
public readonly string Name = name;
|
||||
public readonly Action Action = action;
|
||||
public readonly bool Disabled = disabled;
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class CustomCommandsMenu
|
||||
{
|
||||
public static void OpenMenu(CCSPlayerController admin)
|
||||
{
|
||||
if (!admin.IsValid)
|
||||
return;
|
||||
|
||||
var localizer = CS2_SimpleAdmin._localizer;
|
||||
if (!AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic"))
|
||||
{
|
||||
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||
"[SimpleAdmin] " +
|
||||
(localizer?["sa_no_permission"] ?? "You do not have permissions to use this command")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = AdminMenu.CreateMenu(localizer?["sa_menu_custom_commands"] ?? "Custom Commands");
|
||||
List<ChatMenuOptionData> options = [];
|
||||
|
||||
var customCommands = CS2_SimpleAdmin.Instance.Config.CustomServerCommands;
|
||||
options.AddRange(from customCommand in customCommands
|
||||
where !string.IsNullOrEmpty(customCommand.DisplayName) && !string.IsNullOrEmpty(customCommand.Command)
|
||||
let hasRights = AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), customCommand.Flag)
|
||||
where hasRights
|
||||
select new ChatMenuOptionData(customCommand.DisplayName, () =>
|
||||
{
|
||||
Helper.TryLogCommandOnDiscord(admin, customCommand.Command);
|
||||
|
||||
if (customCommand.ExecuteOnClient)
|
||||
admin.ExecuteClientCommandFromServer(customCommand.Command);
|
||||
else
|
||||
Server.ExecuteCommand(customCommand.Command);
|
||||
}));
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CS2_SimpleAdmin.Models;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class DurationMenu
|
||||
{
|
||||
public static void OpenMenu(CCSPlayerController admin, string menuName, CCSPlayerController player, Action<CCSPlayerController, CCSPlayerController, int> onSelectAction)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu(menuName);
|
||||
if (menu == null)
|
||||
return;
|
||||
|
||||
var durations = CS2_SimpleAdmin.Instance.Config.MenuConfigs.Durations;
|
||||
|
||||
// Capture admin and player to avoid closure issues
|
||||
var capturedAdmin = admin;
|
||||
var capturedPlayer = player;
|
||||
var capturedAction = onSelectAction;
|
||||
|
||||
foreach (var durationItem in durations)
|
||||
{
|
||||
var duration = durationItem.Duration; // Capture in local variable
|
||||
var name = durationItem.Name;
|
||||
|
||||
menu.AddMenuOption(name, (controller, option) =>
|
||||
{
|
||||
capturedAction(capturedAdmin, capturedPlayer, duration);
|
||||
});
|
||||
}
|
||||
|
||||
AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
public static void OpenMenu(CCSPlayerController admin, string menuName, DisconnectedPlayer player, Action<CCSPlayerController, DisconnectedPlayer, int> onSelectAction)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu(menuName);
|
||||
foreach (var durationItem in CS2_SimpleAdmin.Instance.Config.MenuConfigs.Durations)
|
||||
{
|
||||
menu?.AddMenuOption(durationItem.Name, (_, _) => { onSelectAction(admin, player, durationItem.Duration); });
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -264,4 +264,4 @@
|
|||
// {
|
||||
// CS2_SimpleAdmin.SetMoney(admin, player, money);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class ManageAdminsMenu
|
||||
{
|
||||
public static void OpenMenu(CCSPlayerController admin)
|
||||
{
|
||||
if (!admin.IsValid)
|
||||
return;
|
||||
|
||||
var localizer = CS2_SimpleAdmin._localizer;
|
||||
if (!AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/root"))
|
||||
{
|
||||
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||
"[SimpleAdmin] " +
|
||||
(localizer?["sa_no_permission"] ?? "You do not have permissions to use this command")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = AdminMenu.CreateMenu(localizer?["sa_menu_admins_manage"] ?? "Admins Manage");
|
||||
List<ChatMenuOptionData> options =
|
||||
[
|
||||
new(localizer?["sa_admin_add"] ?? "Add Admin",
|
||||
() => PlayersMenu.OpenRealPlayersMenu(admin, localizer?["sa_admin_add"] ?? "Add Admin", AddAdminMenu)),
|
||||
new(localizer?["sa_admin_remove"] ?? "Remove Admin",
|
||||
() => PlayersMenu.OpenAdminPlayersMenu(admin, localizer?["sa_admin_remove"] ?? "Remove Admin", RemoveAdmin,
|
||||
player => player != admin && admin.CanTarget(player))),
|
||||
new(localizer?["sa_admin_reload"] ?? "Reload Admins", () => ReloadAdmins(admin))
|
||||
];
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void AddAdminMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_admin_add"] ?? "Add Admin"}: {player.PlayerName}");
|
||||
|
||||
foreach (var adminFlag in CS2_SimpleAdmin.Instance.Config.MenuConfigs.AdminFlags)
|
||||
{
|
||||
var disabled = AdminManager.PlayerHasPermissions(player, adminFlag.Flag);
|
||||
menu?.AddMenuOption(adminFlag.Name, (_, _) => { AddAdmin(admin, player, adminFlag.Flag); }, disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void AddAdmin(CCSPlayerController admin, CCSPlayerController player, string flag)
|
||||
{
|
||||
// TODO: Change default immunity?
|
||||
CS2_SimpleAdmin.AddAdmin(admin, player.SteamID.ToString(), player.PlayerName, flag, 10);
|
||||
}
|
||||
|
||||
private static void RemoveAdmin(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.RemoveAdmin(admin, player.SteamID.ToString());
|
||||
}
|
||||
|
||||
private static void ReloadAdmins(CCSPlayerController admin)
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.ReloadAdmins(admin);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,325 +0,0 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using CS2_SimpleAdminApi;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class ManagePlayersMenu
|
||||
{
|
||||
public static void OpenMenu(CCSPlayerController admin)
|
||||
{
|
||||
if (!admin.IsValid)
|
||||
return;
|
||||
|
||||
var localizer = CS2_SimpleAdmin._localizer;
|
||||
if (!AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic"))
|
||||
{
|
||||
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||
"[SimpleAdmin] " +
|
||||
(localizer?["sa_no_permission"] ?? "You do not have permissions to use this command")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = AdminMenu.CreateMenu(localizer?["sa_menu_players_manage"] ?? "Manage Players");
|
||||
List<ChatMenuOptionData> options = [];
|
||||
|
||||
// permissions
|
||||
var hasSlay = AdminManager.CommandIsOverriden("css_slay") ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_slay")) : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay");
|
||||
var hasKick = AdminManager.CommandIsOverriden("css_kick") ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_kick")) : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/kick");
|
||||
var hasBan = AdminManager.CommandIsOverriden("css_ban") ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_ban")) : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/ban");
|
||||
var hasChat = AdminManager.CommandIsOverriden("css_gag") ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_gag")) : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/chat");
|
||||
|
||||
// TODO: Localize options
|
||||
// options added in order
|
||||
|
||||
if (hasSlay)
|
||||
{
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_slap"] ?? "Slap", () => PlayersMenu.OpenMenu(admin, localizer?["sa_slap"] ?? "Slap", SlapMenu)));
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_slay"] ?? "Slay", () => PlayersMenu.OpenMenu(admin, localizer?["sa_slay"] ?? "Slay", Slay)));
|
||||
}
|
||||
|
||||
if (hasKick)
|
||||
{
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_kick"] ?? "Kick", () => PlayersMenu.OpenMenu(admin, localizer?["sa_kick"] ?? "Kick", KickMenu)));
|
||||
}
|
||||
|
||||
if (AdminManager.CommandIsOverriden("css_warn")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_warn"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/kick"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_warn"] ?? "Warn", () => PlayersMenu.OpenRealPlayersMenu(admin, localizer?["sa_warn"] ?? "Warn", (a, p) => DurationMenu.OpenMenu(a, $"{localizer?["sa_warn"] ?? "Warn"}: {p.PlayerName}", p, WarnMenu))));
|
||||
|
||||
if (hasBan)
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_ban"] ?? "Ban", () =>
|
||||
PlayersMenu.OpenRealPlayersMenu(admin, localizer?["sa_ban"] ?? "Ban", (a, p) =>
|
||||
DurationMenu.OpenMenu(a, $"{localizer?["sa_ban"] ?? "Ban"}: {p.PlayerName}", p, BanMenu))));
|
||||
|
||||
if (hasChat)
|
||||
{
|
||||
if (AdminManager.CommandIsOverriden("css_gag")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_gag"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/chat"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_gag"] ?? "Gag", () => PlayersMenu.OpenRealPlayersMenu(admin, localizer?["sa_gag"] ?? "Gag", (a, p) => DurationMenu.OpenMenu(a, $"{localizer?["sa_gag"] ?? "Gag"}: {p.PlayerName}", p, GagMenu))));
|
||||
if (AdminManager.CommandIsOverriden("css_mute")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_mute"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/chat"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_mute"] ?? "Mute", () => PlayersMenu.OpenRealPlayersMenu(admin, localizer?["sa_mute"] ?? "Mute", (a, p) => DurationMenu.OpenMenu(a, $"{localizer?["sa_mute"] ?? "Mute"}: {p.PlayerName}", p, MuteMenu))));
|
||||
if (AdminManager.CommandIsOverriden("css_silence")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_silence"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/chat"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_silence"] ?? "Silence", () => PlayersMenu.OpenRealPlayersMenu(admin, localizer?["sa_silence"] ?? "Silence", (a, p) => DurationMenu.OpenMenu(a, $"{localizer?["sa_silence"] ?? "Silence"}: {p.PlayerName}", p, SilenceMenu))));
|
||||
}
|
||||
|
||||
if (AdminManager.CommandIsOverriden("css_team")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_team"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/kick"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_team_force"] ?? "Force Team", () => PlayersMenu.OpenMenu(admin, localizer?["sa_team_force"] ?? "Force Team", ForceTeamMenu)));
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void SlapMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_slap"] ?? "Slap"}: {player.PlayerName}");
|
||||
List<ChatMenuOptionData> options =
|
||||
[
|
||||
// options added in order
|
||||
new("0 hp", () => ApplySlapAndKeepMenu(admin, player, 0)),
|
||||
new("1 hp", () => ApplySlapAndKeepMenu(admin, player, 1)),
|
||||
new("5 hp", () => ApplySlapAndKeepMenu(admin, player, 5)),
|
||||
new("10 hp", () => ApplySlapAndKeepMenu(admin, player, 10)),
|
||||
new("50 hp", () => ApplySlapAndKeepMenu(admin, player, 50)),
|
||||
new("100 hp", () => ApplySlapAndKeepMenu(admin, player, 100)),
|
||||
];
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void ApplySlapAndKeepMenu(CCSPlayerController admin, CCSPlayerController player, int damage)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Slap(admin, player, damage);
|
||||
SlapMenu(admin, player);
|
||||
}
|
||||
|
||||
private static void Slay(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Slay(admin, player);
|
||||
}
|
||||
|
||||
private static void KickMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
ReasonMenu.OpenMenu(admin, PenaltyType.Kick,
|
||||
$"{CS2_SimpleAdmin._localizer?["sa_kick"] ?? "Kick"}: {player.PlayerName}", player, (_, _, reason) =>
|
||||
{
|
||||
if (player is { IsValid: true })
|
||||
Kick(admin, player, reason);
|
||||
});
|
||||
|
||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_kick"] ?? "Kick"}: {player?.PlayerName}");
|
||||
//
|
||||
// foreach (var option in CS2_SimpleAdmin.Instance.Config.MenuConfigs.KickReasons)
|
||||
// {
|
||||
// menu?.AddMenuOption(option, (_, _) =>
|
||||
// {
|
||||
// if (player is { IsValid: true })
|
||||
// Kick(admin, player, option);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void Kick(CCSPlayerController admin, CCSPlayerController player, string? reason)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Instance.Kick(admin, player, reason, admin.PlayerName);
|
||||
}
|
||||
|
||||
internal static void BanMenu(CCSPlayerController admin, CCSPlayerController player, int duration)
|
||||
{
|
||||
ReasonMenu.OpenMenu(admin, PenaltyType.Ban,
|
||||
$"{CS2_SimpleAdmin._localizer?["sa_ban"] ?? "Ban"}: {player.PlayerName}", player, (_, _, reason) =>
|
||||
{
|
||||
if (player is { IsValid: true })
|
||||
Ban(admin, player, duration, reason);
|
||||
|
||||
CS2_SimpleAdmin.MenuApi?.CloseMenu(admin);
|
||||
});
|
||||
}
|
||||
|
||||
private static void Ban(CCSPlayerController admin, CCSPlayerController player, int duration, string reason)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Instance.Ban(admin, player, duration, reason, admin.PlayerName);
|
||||
}
|
||||
|
||||
private static void WarnMenu(CCSPlayerController admin, CCSPlayerController player, int duration)
|
||||
{
|
||||
ReasonMenu.OpenMenu(admin, PenaltyType.Warn,
|
||||
$"{CS2_SimpleAdmin._localizer?["sa_warn"] ?? "Warn"}: {player.PlayerName}", player, (_, _, reason) =>
|
||||
{
|
||||
if (player is { IsValid: true })
|
||||
Warn(admin, player, duration, reason);
|
||||
});
|
||||
|
||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_warn"] ?? "Warn"}: {player?.PlayerName}");
|
||||
//
|
||||
// foreach (var option in CS2_SimpleAdmin.Instance.Config.MenuConfigs.WarnReasons)
|
||||
// {
|
||||
// menu?.AddMenuOption(option, (_, _) =>
|
||||
// {
|
||||
// if (player is { IsValid: true })
|
||||
// Warn(admin, player, duration, option);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void Warn(CCSPlayerController admin, CCSPlayerController player, int duration, string reason)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Instance.Warn(admin, player, duration, reason, admin.PlayerName);
|
||||
}
|
||||
|
||||
internal static void GagMenu(CCSPlayerController admin, CCSPlayerController player, int duration)
|
||||
{
|
||||
ReasonMenu.OpenMenu(admin, PenaltyType.Gag,
|
||||
$"{CS2_SimpleAdmin._localizer?["sa_gag"] ?? "Gag"}: {player.PlayerName}", player, (_, _, reason) =>
|
||||
{
|
||||
if (player is { IsValid: true })
|
||||
Gag(admin, player, duration, reason);
|
||||
});
|
||||
|
||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_gag"] ?? "Gag"}: {player?.PlayerName}");
|
||||
//
|
||||
// foreach (var option in CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons)
|
||||
// {
|
||||
// menu?.AddMenuOption(option, (_, _) =>
|
||||
// {
|
||||
// if (player is { IsValid: true })
|
||||
// Gag(admin, player, duration, option);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void Gag(CCSPlayerController admin, CCSPlayerController player, int duration, string reason)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Instance.Gag(admin, player, duration, reason);
|
||||
}
|
||||
|
||||
internal static void MuteMenu(CCSPlayerController admin, CCSPlayerController player, int duration)
|
||||
{
|
||||
ReasonMenu.OpenMenu(admin, PenaltyType.Mute,
|
||||
$"{CS2_SimpleAdmin._localizer?["sa_mute"] ?? "mute"}: {player.PlayerName}", player, (_, _, reason) =>
|
||||
{
|
||||
if (player is { IsValid: true })
|
||||
Mute(admin, player, duration, reason);
|
||||
});
|
||||
|
||||
// // TODO: Localize and make options in config?
|
||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_mute"] ?? "Mute"}: {player?.PlayerName}");
|
||||
//
|
||||
// foreach (var option in CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons)
|
||||
// {
|
||||
// menu?.AddMenuOption(option, (_, _) =>
|
||||
// {
|
||||
// if (player is { IsValid: true })
|
||||
// Mute(admin, player, duration, option);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void Mute(CCSPlayerController admin, CCSPlayerController player, int duration, string reason)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Instance.Mute(admin, player, duration, reason);
|
||||
}
|
||||
|
||||
internal static void SilenceMenu(CCSPlayerController admin, CCSPlayerController player, int duration)
|
||||
{
|
||||
ReasonMenu.OpenMenu(admin, PenaltyType.Silence,
|
||||
$"{CS2_SimpleAdmin._localizer?["sa_silence"] ?? "Silence"}: {player.PlayerName}", player, (_, _, reason) =>
|
||||
{
|
||||
if (player is { IsValid: true })
|
||||
Silence(admin, player, duration, reason);
|
||||
});
|
||||
|
||||
// // TODO: Localize and make options in config?
|
||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_silence"] ?? "Silence"}: {player?.PlayerName}");
|
||||
//
|
||||
// foreach (var option in CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons)
|
||||
// {
|
||||
// menu?.AddMenuOption(option, (_, _) =>
|
||||
// {
|
||||
// if (player is { IsValid: true })
|
||||
// Silence(admin, player, duration, option);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void Silence(CCSPlayerController admin, CCSPlayerController player, int duration, string reason)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Instance.Silence(admin, player, duration, reason);
|
||||
}
|
||||
|
||||
private static void ForceTeamMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
// TODO: Localize
|
||||
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_team_force"] ?? "Force Team"} {player.PlayerName}");
|
||||
List<ChatMenuOptionData> options =
|
||||
[
|
||||
new(CS2_SimpleAdmin._localizer?["sa_team_ct"] ?? "CT", () => ForceTeam(admin, player, "ct", CsTeam.CounterTerrorist)),
|
||||
new(CS2_SimpleAdmin._localizer?["sa_team_t"] ?? "T", () => ForceTeam(admin, player, "t", CsTeam.Terrorist)),
|
||||
new(CS2_SimpleAdmin._localizer?["sa_team_swap"] ?? "Swap", () => ForceTeam(admin, player, "swap", CsTeam.Spectator)),
|
||||
new(CS2_SimpleAdmin._localizer?["sa_team_spec"] ?? "Spec", () => ForceTeam(admin, player, "spec", CsTeam.Spectator)),
|
||||
];
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void ForceTeam(CCSPlayerController admin, CCSPlayerController player, string teamName, CsTeam teamNum)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.ChangeTeam(admin, player, teamName, teamNum, true);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class ManageServerMenu
|
||||
{
|
||||
public static void OpenMenu(CCSPlayerController admin)
|
||||
{
|
||||
if (!admin.IsValid)
|
||||
return;
|
||||
|
||||
var localizer = CS2_SimpleAdmin._localizer;
|
||||
if (!AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic"))
|
||||
{
|
||||
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||
"[SimpleAdmin] " +
|
||||
(localizer?["sa_no_permission"] ?? "You do not have permissions to use this command")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = AdminMenu.CreateMenu(localizer?["sa_menu_server_manage"] ?? "Server Manage");
|
||||
List<ChatMenuOptionData> options = [];
|
||||
|
||||
// permissions
|
||||
var hasMap = AdminManager.CommandIsOverriden("css_map") ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_map")) : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/changemap");
|
||||
var hasPlugins = AdminManager.CommandIsOverriden("css_pluginsmanager") ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_pluginsmanager")) : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/root");
|
||||
|
||||
//bool hasMap = AdminManager.PlayerHasPermissions(admin, "@css/changemap");
|
||||
|
||||
// options added in order
|
||||
|
||||
if (hasPlugins)
|
||||
{
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_menu_pluginsmanager_title"] ?? "Manage Plugins", () => admin.ExecuteClientCommandFromServer("css_pluginsmanager")));
|
||||
}
|
||||
|
||||
if (hasMap)
|
||||
{
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_changemap"] ?? "Change Map", () => ChangeMapMenu(admin)));
|
||||
}
|
||||
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_restart_game"] ?? "Restart Game", () => CS2_SimpleAdmin.RestartGame(admin)));
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void ChangeMapMenu(CCSPlayerController admin)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu(CS2_SimpleAdmin._localizer?["sa_changemap"] ?? "Change Map");
|
||||
List<ChatMenuOptionData> options = [];
|
||||
|
||||
var maps = CS2_SimpleAdmin.Instance.Config.DefaultMaps;
|
||||
options.AddRange(maps.Select(map => new ChatMenuOptionData(map, () => ExecuteChangeMap(admin, map, false))));
|
||||
|
||||
var wsMaps = CS2_SimpleAdmin.Instance.Config.WorkshopMaps;
|
||||
options.AddRange(wsMaps.Select(map => new ChatMenuOptionData($"{map.Key} (WS)", () => ExecuteChangeMap(admin, map.Value?.ToString() ?? map.Key, true))));
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void ExecuteChangeMap(CCSPlayerController admin, string mapName, bool workshop)
|
||||
{
|
||||
if (workshop)
|
||||
CS2_SimpleAdmin.Instance.ChangeWorkshopMap(admin, mapName);
|
||||
else
|
||||
CS2_SimpleAdmin.Instance.ChangeMap(admin, mapName);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +1,29 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Translations;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using PanoramaUiKit.Shared.Api;
|
||||
using PanoramaUiKit.Shared.Menus;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
/// <summary>
|
||||
/// Describes a menu and renders it through the Panorama UI Kit.
|
||||
/// Sub-menus are pushed on top of their parent, the kit draws the back button.
|
||||
/// </summary>
|
||||
public class MenuBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Bumped every time a menu is opened for a player, so an option can tell whether its action opened another one.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<ulong, int> OpenCounters = [];
|
||||
|
||||
private readonly string _title;
|
||||
private readonly CCSPlayerController? _player;
|
||||
private readonly IStringLocalizer? _localizer;
|
||||
private readonly List<MenuOption> _options = [];
|
||||
private MenuBuilder? _parentMenu;
|
||||
private Action<CCSPlayerController>? _backAction;
|
||||
private Action<CCSPlayerController>? _resetAction;
|
||||
private bool _isSubMenu;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for player-localized menu with translation key
|
||||
|
|
@ -24,7 +36,7 @@ public class MenuBuilder
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for static title (backward compatibility)
|
||||
/// Constructor for static title
|
||||
/// </summary>
|
||||
public MenuBuilder(string title)
|
||||
{
|
||||
|
|
@ -33,21 +45,6 @@ public class MenuBuilder
|
|||
_localizer = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the localized title for the player
|
||||
/// </summary>
|
||||
private string GetLocalizedTitle()
|
||||
{
|
||||
if (_player != null && _localizer != null)
|
||||
{
|
||||
using (new WithTemporaryCulture(_player.GetLanguage()))
|
||||
{
|
||||
return _localizer[_title];
|
||||
}
|
||||
}
|
||||
return _title;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a menu option with an action.
|
||||
/// </summary>
|
||||
|
|
@ -56,7 +53,9 @@ public class MenuBuilder
|
|||
/// <param name="disabled">Whether the option is disabled</param>
|
||||
/// <param name="permission">Required permission</param>
|
||||
/// <param name="isTranslationKey">If true, name is a translation key to be localized</param>
|
||||
public MenuBuilder AddOption(string name, Action<CCSPlayerController> action, bool disabled = false, string? permission = null, bool isTranslationKey = false)
|
||||
/// <param name="keepOpen">If true, the menu stays open after the action</param>
|
||||
public MenuBuilder AddOption(string name, Action<CCSPlayerController> action, bool disabled = false, string? permission = null,
|
||||
bool isTranslationKey = false, bool keepOpen = false)
|
||||
{
|
||||
_options.Add(new MenuOption
|
||||
{
|
||||
|
|
@ -64,46 +63,18 @@ public class MenuBuilder
|
|||
Action = action,
|
||||
Disabled = disabled,
|
||||
Permission = permission,
|
||||
IsTranslationKey = isTranslationKey
|
||||
IsTranslationKey = isTranslationKey,
|
||||
KeepOpen = keepOpen
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the localized name for a menu option
|
||||
/// </summary>
|
||||
private string GetLocalizedOptionName(MenuOption option)
|
||||
{
|
||||
if (option.IsTranslationKey && _player != null && _localizer != null)
|
||||
{
|
||||
using (new WithTemporaryCulture(_player.GetLanguage()))
|
||||
{
|
||||
return _localizer[option.Name];
|
||||
}
|
||||
}
|
||||
return option.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a menu option that opens a submenu.
|
||||
/// </summary>
|
||||
public MenuBuilder AddSubMenu(string name, Func<MenuBuilder> subMenuFactory, bool disabled = false, string? permission = null)
|
||||
{
|
||||
_options.Add(new MenuOption
|
||||
{
|
||||
Name = name,
|
||||
Action = player =>
|
||||
{
|
||||
var subMenu = subMenuFactory();
|
||||
subMenu.SetParent(this);
|
||||
// Automatically add back button to submenu
|
||||
subMenu.WithBackButton();
|
||||
subMenu.OpenMenu(player);
|
||||
},
|
||||
Disabled = disabled,
|
||||
Permission = permission
|
||||
});
|
||||
return this;
|
||||
return AddSubMenu(name, _ => subMenuFactory(), disabled, permission);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -114,14 +85,7 @@ public class MenuBuilder
|
|||
_options.Add(new MenuOption
|
||||
{
|
||||
Name = name,
|
||||
Action = player =>
|
||||
{
|
||||
var subMenu = subMenuFactory(player);
|
||||
subMenu.SetParent(this);
|
||||
// Automatically add back button to submenu
|
||||
subMenu.WithBackButton();
|
||||
subMenu.OpenMenu(player);
|
||||
},
|
||||
SubMenuFactory = subMenuFactory,
|
||||
Disabled = disabled,
|
||||
Permission = permission
|
||||
});
|
||||
|
|
@ -129,66 +93,50 @@ public class MenuBuilder
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a back button to return to the previous menu.
|
||||
/// Opens this menu on top of the current one, with a back button.
|
||||
/// </summary>
|
||||
public MenuBuilder WithBackButton()
|
||||
{
|
||||
if (_parentMenu != null)
|
||||
{
|
||||
_backAction = player => _parentMenu.OpenMenu(player);
|
||||
|
||||
// Add back option at the end of menu
|
||||
// AddOption(backButtonText, _backAction);
|
||||
}
|
||||
_isSubMenu = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the parent menu (for navigation).
|
||||
/// </summary>
|
||||
private void SetParent(MenuBuilder parent)
|
||||
{
|
||||
_parentMenu = parent;
|
||||
_backAction = player => _parentMenu.OpenMenu(player);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the menu for a player.
|
||||
/// </summary>
|
||||
/// <param name="player">The player to open the menu for.</param>
|
||||
public void OpenMenu(CCSPlayerController player)
|
||||
{
|
||||
if (!player.IsValid) return;
|
||||
|
||||
// Get localized title
|
||||
var localizedTitle = GetLocalizedTitle();
|
||||
|
||||
// Use MenuManager dependency
|
||||
var menu = Helper.CreateMenu(localizedTitle, _backAction);
|
||||
if (menu == null) return;
|
||||
|
||||
foreach (var option in _options)
|
||||
if (!player.IsValid)
|
||||
{
|
||||
// Check permissions if required
|
||||
if (!string.IsNullOrEmpty(option.Permission))
|
||||
{
|
||||
var steamId = new CounterStrikeSharp.API.Modules.Entities.SteamID(player.SteamID);
|
||||
if (!CounterStrikeSharp.API.Modules.Admin.AdminManager.PlayerHasPermissions(steamId, option.Permission))
|
||||
{
|
||||
continue; // Skip option if player doesn't have permission
|
||||
}
|
||||
}
|
||||
|
||||
// Get localized option name
|
||||
var localizedName = GetLocalizedOptionName(option);
|
||||
|
||||
menu.AddMenuOption(localizedName, (menuPlayer, menuOption) =>
|
||||
{
|
||||
option.Action?.Invoke(menuPlayer);
|
||||
}, option.Disabled);
|
||||
return;
|
||||
}
|
||||
|
||||
menu.Open(player);
|
||||
IPanoramaUiKit? kit = UiKit.Instance;
|
||||
if (kit == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MenuOptions menu = Build(player);
|
||||
BumpOpenCounter(player);
|
||||
|
||||
if (_isSubMenu)
|
||||
{
|
||||
kit.Menus.Push(player, menu);
|
||||
}
|
||||
else
|
||||
{
|
||||
kit.Menus.Open(player, menu);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes any SimpleAdmin menu open for the player.
|
||||
/// </summary>
|
||||
public static void CloseMenu(CCSPlayerController player)
|
||||
{
|
||||
UiKit.Instance?.Menus.Close(player, UiKit.MenuLayout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -201,26 +149,101 @@ public class MenuBuilder
|
|||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a reset action for the menu.
|
||||
/// </summary>
|
||||
/// <param name="resetAction">The action to execute on reset.</param>
|
||||
/// <returns>This MenuBuilder instance for chaining.</returns>
|
||||
public MenuBuilder WithResetAction(Action<CCSPlayerController> resetAction)
|
||||
private MenuOptions Build(CCSPlayerController player)
|
||||
{
|
||||
_resetAction = resetAction;
|
||||
return this;
|
||||
List<MenuEntry> entries = [];
|
||||
SteamID steamId = new SteamID(player.SteamID);
|
||||
|
||||
foreach (MenuOption option in _options)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(option.Permission) && !AdminManager.PlayerHasPermissions(steamId, option.Permission))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string label = GetLocalizedOptionName(option);
|
||||
MenuEntry entry;
|
||||
|
||||
if (option.SubMenuFactory != null)
|
||||
{
|
||||
Func<CCSPlayerController, MenuBuilder> subMenuFactory = option.SubMenuFactory;
|
||||
entry = MenuEntry.Open(label, menuPlayer =>
|
||||
{
|
||||
BumpOpenCounter(menuPlayer);
|
||||
return subMenuFactory(menuPlayer).Build(menuPlayer);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
entry = MenuEntry.Button(label, menuPlayer => RunOption(option, menuPlayer));
|
||||
}
|
||||
|
||||
if (option.Disabled)
|
||||
{
|
||||
entry = entry with { Disabled = true };
|
||||
}
|
||||
|
||||
entries.Add(entry);
|
||||
}
|
||||
|
||||
return new MenuOptions
|
||||
{
|
||||
Layout = UiKit.MenuLayout,
|
||||
Title = GetLocalizedTitle(),
|
||||
Entries = entries
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets a custom back action for the menu.
|
||||
/// Runs the option, then closes the menu unless the option keeps it open or its action opened another menu.
|
||||
/// </summary>
|
||||
/// <param name="backAction">The action to execute when going back (nullable).</param>
|
||||
/// <returns>This MenuBuilder instance for chaining.</returns>
|
||||
public MenuBuilder WithBackAction(Action<CCSPlayerController>? backAction)
|
||||
private static void RunOption(MenuOption option, CCSPlayerController player)
|
||||
{
|
||||
_backAction = backAction;
|
||||
return this;
|
||||
int counterBefore = GetOpenCounter(player);
|
||||
|
||||
option.Action?.Invoke(player);
|
||||
|
||||
if (!option.KeepOpen && player.IsValid && GetOpenCounter(player) == counterBefore)
|
||||
{
|
||||
CloseMenu(player);
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetOpenCounter(CCSPlayerController player)
|
||||
{
|
||||
OpenCounters.TryGetValue(player.SteamID, out int counter);
|
||||
return counter;
|
||||
}
|
||||
|
||||
private static void BumpOpenCounter(CCSPlayerController player)
|
||||
{
|
||||
OpenCounters[player.SteamID] = GetOpenCounter(player) + 1;
|
||||
}
|
||||
|
||||
private string GetLocalizedTitle()
|
||||
{
|
||||
if (_player == null || _localizer == null)
|
||||
{
|
||||
return _title;
|
||||
}
|
||||
|
||||
using (new WithTemporaryCulture(_player.GetLanguage()))
|
||||
{
|
||||
return _localizer[_title];
|
||||
}
|
||||
}
|
||||
|
||||
private string GetLocalizedOptionName(MenuOption option)
|
||||
{
|
||||
if (!option.IsTranslationKey || _player == null || _localizer == null)
|
||||
{
|
||||
return option.Name;
|
||||
}
|
||||
|
||||
using (new WithTemporaryCulture(_player.GetLanguage()))
|
||||
{
|
||||
return _localizer[option.Name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -231,8 +254,9 @@ public class MenuOption
|
|||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public Action<CCSPlayerController>? Action { get; set; }
|
||||
public Func<CCSPlayerController, MenuBuilder>? SubMenuFactory { get; set; }
|
||||
public bool Disabled { get; set; }
|
||||
public string? Permission { get; set; }
|
||||
public bool IsTranslationKey { get; set; }
|
||||
public bool KeepOpen { get; set; }
|
||||
}
|
||||
|
||||
|
|
@ -2,6 +2,7 @@ using CounterStrikeSharp.API.Core;
|
|||
using CounterStrikeSharp.API.Core.Translations;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
|
|
@ -125,7 +126,11 @@ public class MenuManager
|
|||
/// <param name="menuId">The menu to unregister.</param>
|
||||
public void UnregisterMenu(string categoryId, string menuId)
|
||||
{
|
||||
if (!_menuCategories.TryGetValue(categoryId, out var category)) return;
|
||||
if (!_menuCategories.TryGetValue(categoryId, out MenuCategory? category))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
category.MenuFactories.Remove(menuId);
|
||||
_menuCategories[categoryId].MenuNames.Remove(menuId);
|
||||
_menuCategories[categoryId].MenuPermissions.Remove(menuId);
|
||||
|
|
@ -139,16 +144,21 @@ public class MenuManager
|
|||
/// <returns>A MenuBuilder instance for the main menu.</returns>
|
||||
public MenuBuilder CreateMainMenu(CCSPlayerController player)
|
||||
{
|
||||
var localizer = CS2_SimpleAdmin._localizer;
|
||||
var mainMenu = new MenuBuilder("sa_title", player, localizer);
|
||||
IStringLocalizer? localizer = CS2_SimpleAdmin._localizer;
|
||||
MenuBuilder mainMenu = new MenuBuilder("sa_title", player, localizer);
|
||||
|
||||
foreach (var category in _menuCategories.Values)
|
||||
foreach (MenuCategory category in _menuCategories.Values)
|
||||
{
|
||||
if (category.MenuFactories.Count <= 0) continue;
|
||||
// Check category permissions
|
||||
var steamId = new SteamID(player.SteamID);
|
||||
if (!AdminManager.PlayerHasPermissions(steamId, category.Permission))
|
||||
if (category.MenuFactories.Count <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Check category permissions
|
||||
SteamID steamId = new SteamID(player.SteamID);
|
||||
if (!AdminManager.PlayerHasPermissions(steamId, category.Permission))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get localized category name for this player
|
||||
// If category has a module localizer, use it; otherwise use main plugin localizer
|
||||
|
|
@ -181,7 +191,7 @@ public class MenuManager
|
|||
/// <returns>A MenuBuilder instance for the category menu.</returns>
|
||||
private MenuBuilder CreateCategoryMenu(MenuCategory category, CCSPlayerController player)
|
||||
{
|
||||
var localizer = CS2_SimpleAdmin._localizer;
|
||||
IStringLocalizer? localizer = CS2_SimpleAdmin._localizer;
|
||||
|
||||
// Get localized category name for this player
|
||||
// If category has a module localizer, use it; otherwise use main plugin localizer
|
||||
|
|
@ -198,18 +208,22 @@ public class MenuManager
|
|||
}
|
||||
}
|
||||
|
||||
var categoryMenu = new MenuBuilder(localizedCategoryName);
|
||||
MenuBuilder categoryMenu = new MenuBuilder(localizedCategoryName);
|
||||
|
||||
foreach (var kvp in category.MenuFactories)
|
||||
foreach (KeyValuePair<string, Func<CCSPlayerController, MenuBuilder>> kvp in category.MenuFactories)
|
||||
{
|
||||
var menuId = kvp.Key;
|
||||
var menuFactory = kvp.Value;
|
||||
var menuName = category.MenuNames.TryGetValue(menuId, out var name) ? name : menuId;
|
||||
var permission = category.MenuPermissions.TryGetValue(menuId, out var perm) ? perm : null;
|
||||
var commandName = category.MenuCommandNames.TryGetValue(menuId, out var cmd) ? cmd : null;
|
||||
string menuId = kvp.Key;
|
||||
Func<CCSPlayerController, MenuBuilder> menuFactory = kvp.Value;
|
||||
if (!category.MenuNames.TryGetValue(menuId, out string? menuName))
|
||||
{
|
||||
menuName = menuId;
|
||||
}
|
||||
|
||||
category.MenuPermissions.TryGetValue(menuId, out string? permission);
|
||||
category.MenuCommandNames.TryGetValue(menuId, out string? commandName);
|
||||
|
||||
// Check permissions with command override support
|
||||
var steamId = new SteamID(player.SteamID);
|
||||
SteamID steamId = new SteamID(player.SteamID);
|
||||
|
||||
// If commandName is provided, check for permission overrides
|
||||
if (!string.IsNullOrEmpty(commandName))
|
||||
|
|
@ -219,7 +233,7 @@ public class MenuManager
|
|||
// Check if command has overridden permissions
|
||||
if (AdminManager.CommandIsOverriden(commandName))
|
||||
{
|
||||
var overriddenPermission = AdminManager.GetPermissionOverrides(commandName);
|
||||
string[] overriddenPermission = AdminManager.GetPermissionOverrides(commandName);
|
||||
hasPermission = AdminManager.PlayerHasPermissions(steamId, overriddenPermission);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(permission))
|
||||
|
|
@ -234,13 +248,17 @@ public class MenuManager
|
|||
}
|
||||
|
||||
if (!hasPermission)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Fallback to standard permission check if no commandName provided
|
||||
else if (!string.IsNullOrEmpty(permission))
|
||||
{
|
||||
if (!AdminManager.PlayerHasPermissions(steamId, permission))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Get localized menu name for this player
|
||||
|
|
@ -248,7 +266,7 @@ public class MenuManager
|
|||
string localizedMenuName;
|
||||
using (new WithTemporaryCulture(player.GetLanguage()))
|
||||
{
|
||||
if (category.MenuLocalizers.TryGetValue(menuId, out var menuLocalizer))
|
||||
if (category.MenuLocalizers.TryGetValue(menuId, out IStringLocalizer? menuLocalizer))
|
||||
{
|
||||
// Menu has its own module localizer
|
||||
localizedMenuName = menuLocalizer[menuName] ?? menuName;
|
||||
|
|
@ -278,9 +296,9 @@ public class MenuManager
|
|||
/// <param name="player">The player to open the menu for.</param>
|
||||
public void OpenMainMenu(CCSPlayerController player)
|
||||
{
|
||||
var localizer = CS2_SimpleAdmin._localizer;
|
||||
|
||||
var steamId = new SteamID(player.SteamID);
|
||||
IStringLocalizer? localizer = CS2_SimpleAdmin._localizer;
|
||||
|
||||
SteamID steamId = new SteamID(player.SteamID);
|
||||
if (!AdminManager.PlayerHasPermissions(steamId, "@css/generic"))
|
||||
{
|
||||
player.PrintToChat(localizer?["sa_prefix"] ?? "[SimpleAdmin] " +
|
||||
|
|
@ -303,17 +321,6 @@ public class MenuManager
|
|||
// RegisterCategory("fun", "sa_menu_fun_commands", "@css/generic");
|
||||
RegisterCategory("admin", "sa_menu_admins_manage", "@css/root");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public method for creating category menus (for API usage).
|
||||
/// </summary>
|
||||
/// <param name="category">The menu category to create.</param>
|
||||
/// <param name="player">The player to create the menu for.</param>
|
||||
/// <returns>A MenuBuilder instance for the category menu.</returns>
|
||||
public MenuBuilder CreateCategoryMenuPublic(MenuCategory category, CCSPlayerController player)
|
||||
{
|
||||
return CreateCategoryMenu(category, player);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using System.Web;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class PlayersMenu
|
||||
{
|
||||
public static void OpenRealPlayersMenu(CCSPlayerController admin, string menuName, Action<CCSPlayerController, CCSPlayerController> onSelectAction, Func<CCSPlayerController, bool>? enableFilter = null)
|
||||
{
|
||||
OpenMenu(admin, menuName, onSelectAction, p => !p.IsBot);
|
||||
}
|
||||
|
||||
public static void OpenAdminPlayersMenu(CCSPlayerController admin, string menuName, Action<CCSPlayerController, CCSPlayerController> onSelectAction, Func<CCSPlayerController?, bool>? enableFilter = null)
|
||||
{
|
||||
OpenMenu(admin, menuName, onSelectAction, p => AdminManager.GetPlayerAdminData(p)?.Flags.Count > 0);
|
||||
}
|
||||
|
||||
public static void OpenAliveMenu(CCSPlayerController admin, string menuName, Action<CCSPlayerController, CCSPlayerController> onSelectAction, Func<CCSPlayerController, bool>? enableFilter = null)
|
||||
{
|
||||
OpenMenu(admin, menuName, onSelectAction, p => p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE);
|
||||
}
|
||||
|
||||
public static void OpenDeadMenu(CCSPlayerController admin, string menuName, Action<CCSPlayerController?, CCSPlayerController> onSelectAction, Func<CCSPlayerController, bool>? enableFilter = null)
|
||||
{
|
||||
OpenMenu(admin, menuName, onSelectAction, p => p.PlayerPawn?.Value?.LifeState != (int)LifeState_t.LIFE_ALIVE);
|
||||
}
|
||||
|
||||
public static void OpenMenu(CCSPlayerController admin, string menuName, Action<CCSPlayerController, CCSPlayerController> onSelectAction, Func<CCSPlayerController, bool>? enableFilter = null)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu(menuName);
|
||||
|
||||
var players = Helper.GetValidPlayersWithBots();
|
||||
|
||||
foreach (var player in players)
|
||||
{
|
||||
var playerName = player != null && player.PlayerName.Length > 26 ? player.PlayerName[..26] : player?.PlayerName;
|
||||
|
||||
var optionName = HttpUtility.HtmlEncode(playerName);
|
||||
if (player != null && enableFilter != null && !enableFilter(player))
|
||||
continue;
|
||||
|
||||
var enabled = admin.CanTarget(player);
|
||||
var capturedPlayer = player; // Capture in local variable to avoid closure issues
|
||||
|
||||
if (optionName != null)
|
||||
menu?.AddMenuOption(optionName, (controller, option) =>
|
||||
{
|
||||
if (capturedPlayer != null) onSelectAction.Invoke(admin, capturedPlayer);
|
||||
},
|
||||
!enabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CS2_SimpleAdmin.Models;
|
||||
using CS2_SimpleAdminApi;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class ReasonMenu
|
||||
{
|
||||
public static void OpenMenu(CCSPlayerController admin, PenaltyType penaltyType, string menuName, CCSPlayerController player, Action<CCSPlayerController, CCSPlayerController, string> onSelectAction)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu(menuName);
|
||||
|
||||
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 => CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons,
|
||||
PenaltyType.Silence => CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons,
|
||||
_ => CS2_SimpleAdmin.Instance.Config.MenuConfigs.BanReasons
|
||||
};
|
||||
|
||||
foreach (var reason in reasons)
|
||||
{
|
||||
menu?.AddMenuOption(reason, (_, _) => onSelectAction(admin, player, reason));
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
public static void OpenMenu(CCSPlayerController admin, PenaltyType penaltyType, string menuName, DisconnectedPlayer player, Action<CCSPlayerController, DisconnectedPlayer, string> onSelectAction)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu(menuName);
|
||||
|
||||
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,
|
||||
_ => CS2_SimpleAdmin.Instance.Config.MenuConfigs.BanReasons
|
||||
};
|
||||
|
||||
foreach (var reason in reasons)
|
||||
{
|
||||
menu?.AddMenuOption(reason, (_, _) => onSelectAction(admin, player, reason));
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
}
|
||||
81
CS2-SimpleAdmin/Menus/UiKit.cs
Normal file
81
CS2-SimpleAdmin/Menus/UiKit.cs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
using CounterStrikeSharp.API.Core.Capabilities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PanoramaUiKit.Shared.Api;
|
||||
using PanoramaUiKit.Shared.Banners;
|
||||
using PanoramaUiKit.Shared.Common;
|
||||
using PanoramaUiKit.Shared.Menus;
|
||||
using PanoramaUiKit.Shared.Toasts;
|
||||
using PanoramaUiKit.Shared.Votes;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
/// <summary>
|
||||
/// Access to the Panorama UI Kit plugin and the layouts SimpleAdmin draws on.
|
||||
/// </summary>
|
||||
internal static class UiKit
|
||||
{
|
||||
private static readonly PluginCapability<IPanoramaUiKit> Capability = new(IPanoramaUiKit.CapabilityName);
|
||||
|
||||
internal static MenuLayout MenuLayout { get; private set; } = new() { Name = "uikit_menu" };
|
||||
|
||||
internal static VoteLayout VoteLayout { get; private set; } = new() { Name = "uikit_vote" };
|
||||
|
||||
internal static BannerLayout BannerLayout { get; private set; } = new() { Name = "uikit_banner" };
|
||||
|
||||
internal static ToastLayout ToastLayout { get; private set; } = new() { Name = "uikit_toast" };
|
||||
|
||||
/// <summary>
|
||||
/// Resolved on every use so a hot reload of the kit is picked up.
|
||||
/// </summary>
|
||||
internal static IPanoramaUiKit? Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return Capability.Get();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Configure(MenuConfig config)
|
||||
{
|
||||
MenuLayout = new MenuLayout { Name = config.MenuLayout };
|
||||
VoteLayout = new VoteLayout { Name = config.VoteLayout };
|
||||
BannerLayout = new BannerLayout { Name = config.BannerLayout };
|
||||
ToastLayout = new ToastLayout { Name = config.ToastLayout };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the layouts up front: creating one on first use sends the client one big burst.
|
||||
/// </summary>
|
||||
internal static bool Preload(ILogger logger)
|
||||
{
|
||||
IPanoramaUiKit? kit = Instance;
|
||||
if (kit == null)
|
||||
{
|
||||
logger.LogError("Panorama UI Kit not found: install PanoramaUiKit and shared/PanoramaUiKit.Shared.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kit.Version != IPanoramaUiKit.ApiVersion)
|
||||
{
|
||||
logger.LogWarning("Panorama UI Kit API v{Running}, SimpleAdmin built against v{Built}.", kit.Version, IPanoramaUiKit.ApiVersion);
|
||||
}
|
||||
|
||||
UiLayout[] layouts = [MenuLayout, VoteLayout, BannerLayout, ToastLayout];
|
||||
foreach (UiLayout layout in layouts)
|
||||
{
|
||||
if (!kit.Preload(layout))
|
||||
{
|
||||
logger.LogError("Layout {Layout} was refused, see the Panorama UI Kit log.", layout);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
|
|||
|
||||
namespace CS2_SimpleAdmin.Models;
|
||||
|
||||
public enum BanStatus
|
||||
public enum BanStatus
|
||||
{
|
||||
[Description("ACTIVE")] ACTIVE,
|
||||
[Description("UNBANNED")] UNBANNED,
|
||||
|
|
@ -15,13 +15,13 @@ public record BanRecord
|
|||
{
|
||||
[Column("id")]
|
||||
public int Id { get; init; }
|
||||
|
||||
|
||||
[Column("player_name")]
|
||||
public string? PlayerName { get; set; }
|
||||
|
||||
|
||||
[Column("player_steamid")]
|
||||
public ulong? PlayerSteamId { get; set; }
|
||||
|
||||
|
||||
[Column("player_ip")]
|
||||
public string? PlayerIp { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -12,4 +12,4 @@ public class DisconnectedPlayer(
|
|||
public string Name { get; set; } = name;
|
||||
public string? IpAddress { get; set; } = ipAddress;
|
||||
public DateTime DisconnectTime = disconnectTime;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Capabilities;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using CS2_SimpleAdmin.Models;
|
||||
using CS2_SimpleAdminApi;
|
||||
using MenuManager;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Concurrent;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using CS2_SimpleAdmin.Database;
|
||||
using CS2_SimpleAdmin.Managers;
|
||||
using CS2_SimpleAdmin.Models;
|
||||
using CS2_SimpleAdminApi;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Timer = CounterStrikeSharp.API.Modules.Timers.Timer;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
|
@ -29,7 +27,6 @@ public partial class CS2_SimpleAdmin
|
|||
public static IStringLocalizer? _localizer;
|
||||
|
||||
// Voting System
|
||||
public static readonly Dictionary<string, int> VoteAnswers = [];
|
||||
public static bool VoteInProgress;
|
||||
|
||||
// Command and Server Settings
|
||||
|
|
@ -62,10 +59,6 @@ public partial class CS2_SimpleAdmin
|
|||
private static MemoryFunctionVoid<CBasePlayerController, CCSPlayerPawn, bool, bool>?
|
||||
_cBasePlayerControllerSetPawnFunc;
|
||||
|
||||
// Menu API and Capabilities
|
||||
internal static IMenuApi? MenuApi;
|
||||
private static readonly PluginCapability<IMenuApi> MenuCapability = new("menu:nfcore");
|
||||
|
||||
// Shared API
|
||||
internal static Api.CS2_SimpleAdminApi? SimpleAdminApi { get; private set; }
|
||||
|
||||
|
|
@ -79,8 +72,8 @@ public partial class CS2_SimpleAdmin
|
|||
|
||||
// Timers
|
||||
internal Timer? PlayersTimer = null;
|
||||
|
||||
|
||||
// Funny list
|
||||
private readonly List<string> _requiredPlugins = ["MenuManagerCore", "PlayerSettings"];
|
||||
private readonly List<string> _requiredShared = ["MenuManagerApi", "PlayerSettingsApi", "AnyBaseLib", "CS2-SimpleAdminApi"];
|
||||
}
|
||||
private readonly List<string> _requiredPlugins = ["PanoramaUiKit"];
|
||||
private readonly List<string> _requiredShared = ["PanoramaUiKit.Shared", "CS2-SimpleAdminApi"];
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue