mirror of
https://github.com/daffyyyy/CS2-SimpleAdmin.git
synced 2026-03-10 00:22:31 +00:00
Compare commits
3 Commits
build-1.7.
...
3a57371be9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a57371be9 | ||
|
|
63ca44bb78 | ||
|
|
3270403ea1 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,4 +9,3 @@ CS2-SimpleAdmin.sln.DotSettings.user
|
|||||||
Modules/CS2-SimpleAdmin_ExampleModule/CS2-SimpleAdmin_ExampleModule.sln.DotSettings.user
|
Modules/CS2-SimpleAdmin_ExampleModule/CS2-SimpleAdmin_ExampleModule.sln.DotSettings.user
|
||||||
CS2-SimpleAdmin_BanSoundModule — kopia
|
CS2-SimpleAdmin_BanSoundModule — kopia
|
||||||
*.user
|
*.user
|
||||||
CLAUDE.md
|
|
||||||
|
|||||||
@@ -1,57 +1,47 @@
|
|||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Core.Commands;
|
using CounterStrikeSharp.API.Core.Commands;
|
||||||
using CounterStrikeSharp.API.Modules.Commands;
|
using CounterStrikeSharp.API.Modules.Commands;
|
||||||
using CounterStrikeSharp.API.Modules.Commands.Targeting;
|
|
||||||
using CounterStrikeSharp.API.Modules.Entities;
|
using CounterStrikeSharp.API.Modules.Entities;
|
||||||
using CS2_SimpleAdmin.Managers;
|
using CS2_SimpleAdmin.Managers;
|
||||||
using CS2_SimpleAdmin.Menus;
|
|
||||||
using CS2_SimpleAdminApi;
|
using CS2_SimpleAdminApi;
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin.Api;
|
namespace CS2_SimpleAdmin.Api;
|
||||||
|
|
||||||
public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
||||||
{
|
{
|
||||||
public event Action? OnSimpleAdminReady;
|
|
||||||
public void OnSimpleAdminReadyEvent() => OnSimpleAdminReady?.Invoke();
|
|
||||||
|
|
||||||
public PlayerInfo GetPlayerInfo(CCSPlayerController player)
|
public PlayerInfo GetPlayerInfo(CCSPlayerController player)
|
||||||
{
|
{
|
||||||
return !player.UserId.HasValue
|
if (!player.UserId.HasValue)
|
||||||
? throw new KeyNotFoundException("Player with specific UserId not found")
|
throw new KeyNotFoundException("Player with specific UserId not found");
|
||||||
: CS2_SimpleAdmin.PlayersInfo[player.SteamID];
|
|
||||||
|
return CS2_SimpleAdmin.PlayersInfo[player.SteamID];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetConnectionString() => CS2_SimpleAdmin.Instance.DbConnectionString;
|
||||||
|
public string GetServerAddress() => CS2_SimpleAdmin.IpAddress;
|
||||||
|
public int? GetServerId() => CS2_SimpleAdmin.ServerId;
|
||||||
|
|
||||||
public string GetConnectionString() => CS2_SimpleAdmin.Instance.DbConnectionString;
|
public Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>> GetPlayerMuteStatus(CCSPlayerController player)
|
||||||
public string GetServerAddress() => CS2_SimpleAdmin.IpAddress;
|
|
||||||
public int? GetServerId() => CS2_SimpleAdmin.ServerId;
|
|
||||||
|
|
||||||
public Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>> GetPlayerMuteStatus(
|
|
||||||
CCSPlayerController player)
|
|
||||||
{
|
{
|
||||||
return PlayerPenaltyManager.GetAllPlayerPenalties(player.Slot);
|
return PlayerPenaltyManager.GetAllPlayerPenalties(player.Slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public event Action<PlayerInfo, PlayerInfo?, PenaltyType, string, int, int?, int?>? OnPlayerPenaltied;
|
public event Action<PlayerInfo, PlayerInfo?, PenaltyType, string, int, int?, int?>? OnPlayerPenaltied;
|
||||||
public event Action<SteamID, PlayerInfo?, PenaltyType, string, int, int?, int?>? OnPlayerPenaltiedAdded;
|
public event Action<SteamID, PlayerInfo?, PenaltyType, string, int, int?, int?>? OnPlayerPenaltiedAdded;
|
||||||
public event Action<string, string?, bool, object>? OnAdminShowActivity;
|
public event Action<string, string?, bool, object>? OnAdminShowActivity;
|
||||||
public event Action<int, bool>? OnAdminToggleSilent;
|
public event Action<int, bool>? OnAdminToggleSilent;
|
||||||
|
|
||||||
public void OnPlayerPenaltiedEvent(PlayerInfo player, PlayerInfo? admin, PenaltyType penaltyType, string reason,
|
public void OnPlayerPenaltiedEvent(PlayerInfo player, PlayerInfo? admin, PenaltyType penaltyType, string reason,
|
||||||
int duration, int? penaltyId) => OnPlayerPenaltied?.Invoke(player, admin, penaltyType, reason, duration,
|
int duration, int? penaltyId) => OnPlayerPenaltied?.Invoke(player, admin, penaltyType, reason, duration, penaltyId, CS2_SimpleAdmin.ServerId);
|
||||||
penaltyId, CS2_SimpleAdmin.ServerId);
|
|
||||||
|
|
||||||
public void OnPlayerPenaltiedAddedEvent(SteamID player, PlayerInfo? admin, PenaltyType penaltyType, string reason,
|
public void OnPlayerPenaltiedAddedEvent(SteamID player, PlayerInfo? admin, PenaltyType penaltyType, string reason,
|
||||||
int duration, int? penaltyId) => OnPlayerPenaltiedAdded?.Invoke(player, admin, penaltyType, reason, duration,
|
int duration, int? penaltyId) => OnPlayerPenaltiedAdded?.Invoke(player, admin, penaltyType, reason, duration, penaltyId, CS2_SimpleAdmin.ServerId);
|
||||||
penaltyId, CS2_SimpleAdmin.ServerId);
|
|
||||||
|
public void OnAdminShowActivityEvent(string messageKey, string? callerName = null, bool dontPublish = false, params object[] messageArgs) => OnAdminShowActivity?.Invoke(messageKey, callerName, dontPublish, messageArgs);
|
||||||
|
|
||||||
public void OnAdminShowActivityEvent(string messageKey, string? callerName = null, bool dontPublish = false,
|
public void OnAdminToggleSilentEvent(int slot, bool status) => OnAdminToggleSilent?.Invoke(slot, status);
|
||||||
params object[] messageArgs) => OnAdminShowActivity?.Invoke(messageKey, callerName, dontPublish, messageArgs);
|
|
||||||
|
|
||||||
public void OnAdminToggleSilentEvent(int slot, bool status) => OnAdminToggleSilent?.Invoke(slot, status);
|
public void IssuePenalty(CCSPlayerController player, CCSPlayerController? admin, PenaltyType penaltyType, string reason, int duration = -1)
|
||||||
|
|
||||||
public void IssuePenalty(CCSPlayerController player, CCSPlayerController? admin, PenaltyType penaltyType,
|
|
||||||
string reason, int duration = -1)
|
|
||||||
{
|
{
|
||||||
switch (penaltyType)
|
switch (penaltyType)
|
||||||
{
|
{
|
||||||
@@ -89,9 +79,8 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||||||
throw new ArgumentOutOfRangeException(nameof(penaltyType), penaltyType, null);
|
throw new ArgumentOutOfRangeException(nameof(penaltyType), penaltyType, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void IssuePenalty(SteamID steamid, CCSPlayerController? admin, PenaltyType penaltyType, string reason,
|
public void IssuePenalty(SteamID steamid, CCSPlayerController? admin, PenaltyType penaltyType, string reason, int duration = -1)
|
||||||
int duration = -1)
|
|
||||||
{
|
{
|
||||||
switch (penaltyType)
|
switch (penaltyType)
|
||||||
{
|
{
|
||||||
@@ -134,12 +123,12 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||||||
{
|
{
|
||||||
Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsAdminSilent(CCSPlayerController player)
|
public bool IsAdminSilent(CCSPlayerController player)
|
||||||
{
|
{
|
||||||
return CS2_SimpleAdmin.SilentPlayers.Contains(player.Slot);
|
return CS2_SimpleAdmin.SilentPlayers.Contains(player.Slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashSet<int> ListSilentAdminsSlots()
|
public HashSet<int> ListSilentAdminsSlots()
|
||||||
{
|
{
|
||||||
return CS2_SimpleAdmin.SilentPlayers;
|
return CS2_SimpleAdmin.SilentPlayers;
|
||||||
@@ -158,7 +147,7 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||||||
list = new List<CommandDefinition>();
|
list = new List<CommandDefinition>();
|
||||||
RegisterCommands._commandDefinitions[name] = list;
|
RegisterCommands._commandDefinitions[name] = list;
|
||||||
}
|
}
|
||||||
|
|
||||||
list.Add(definition);
|
list.Add(definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,145 +162,9 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||||||
CS2_SimpleAdmin.Instance.RemoveCommand(commandName, definition.Callback);
|
CS2_SimpleAdmin.Instance.RemoveCommand(commandName, definition.Callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TargetResult? GetTarget(CommandInfo command)
|
public void ShowAdminActivity(string messageKey, string? callerName = null, bool dontPublish = false, params object[] messageArgs)
|
||||||
{
|
|
||||||
return CS2_SimpleAdmin.GetTarget(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ShowAdminActivity(string messageKey, string? callerName = null, bool dontPublish = false,
|
|
||||||
params object[] messageArgs)
|
|
||||||
{
|
{
|
||||||
Helper.ShowAdminActivity(messageKey, callerName, dontPublish, messageArgs);
|
Helper.ShowAdminActivity(messageKey, callerName, dontPublish, messageArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowAdminActivityTranslated(string translatedMessage, string? callerName = null,
|
|
||||||
bool dontPublish = false)
|
|
||||||
{
|
|
||||||
Helper.ShowAdminActivityTranslated(translatedMessage, callerName, dontPublish);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ShowAdminActivityLocalized(object moduleLocalizer, string messageKey, string? callerName = null,
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterMenuCategory(string categoryId, string categoryName, string permission = "@css/generic")
|
|
||||||
{
|
|
||||||
Menus.MenuManager.Instance.RegisterCategory(categoryId, categoryName, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterMenu(string categoryId, string menuId, string menuName,
|
|
||||||
Func<CCSPlayerController, object> menuFactory, string? permission = null)
|
|
||||||
{
|
|
||||||
Menus.MenuManager.Instance.RegisterMenu(categoryId, menuId, menuName, BuilderFactory, permission);
|
|
||||||
return;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return menuBuilder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void UnregisterMenu(string categoryId, string menuId)
|
|
||||||
{
|
|
||||||
Menus.MenuManager.Instance.UnregisterMenu(categoryId, menuId);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<CCSPlayerController> GetValidPlayers()
|
|
||||||
{
|
|
||||||
return Helper.GetValidPlayers();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
menu.AddOption(playerName, _ =>
|
|
||||||
{
|
|
||||||
if (player.IsValid)
|
|
||||||
{
|
|
||||||
onSelect(admin, player);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddMenuOption(object menu, string name, Action<CCSPlayerController> action, bool disabled = false,
|
|
||||||
string? permission = null)
|
|
||||||
{
|
|
||||||
if (menu is not MenuBuilder menuBuilder)
|
|
||||||
throw new InvalidOperationException("Menu must be a MenuBuilder instance");
|
|
||||||
|
|
||||||
menuBuilder.AddOption(name, action, disabled, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddSubMenu(object menu, string name, Func<CCSPlayerController, object> subMenuFactory,
|
|
||||||
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);
|
|
||||||
if (subMenu is not MenuBuilder builder)
|
|
||||||
throw new InvalidOperationException("SubMenu factory must return MenuBuilder");
|
|
||||||
return builder;
|
|
||||||
}, disabled, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using CounterStrikeSharp.API;
|
using System.Reflection;
|
||||||
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Core.Attributes;
|
using CounterStrikeSharp.API.Core.Attributes;
|
||||||
using CounterStrikeSharp.API.Core.Capabilities;
|
using CounterStrikeSharp.API.Core.Capabilities;
|
||||||
@@ -7,7 +8,6 @@ using CounterStrikeSharp.API.Modules.Commands.Targeting;
|
|||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CS2_SimpleAdmin.Database;
|
using CS2_SimpleAdmin.Database;
|
||||||
using CS2_SimpleAdmin.Managers;
|
using CS2_SimpleAdmin.Managers;
|
||||||
using CS2_SimpleAdmin.Menus;
|
|
||||||
using CS2_SimpleAdminApi;
|
using CS2_SimpleAdminApi;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using MySqlConnector;
|
using MySqlConnector;
|
||||||
@@ -22,7 +22,7 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||||||
public override string ModuleName => "CS2-SimpleAdmin" + (Helper.IsDebugBuild ? " (DEBUG)" : " (RELEASE)");
|
public override string ModuleName => "CS2-SimpleAdmin" + (Helper.IsDebugBuild ? " (DEBUG)" : " (RELEASE)");
|
||||||
public override string ModuleDescription => "Simple admin plugin for Counter-Strike 2 :)";
|
public override string ModuleDescription => "Simple admin plugin for Counter-Strike 2 :)";
|
||||||
public override string ModuleAuthor => "daffyy & Dliix66";
|
public override string ModuleAuthor => "daffyy & Dliix66";
|
||||||
public override string ModuleVersion => "1.7.8-beta-1";
|
public override string ModuleVersion => "1.7.7-alpha-10";
|
||||||
|
|
||||||
public override void Load(bool hotReload)
|
public override void Load(bool hotReload)
|
||||||
{
|
{
|
||||||
@@ -67,9 +67,6 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||||||
PlayersTimer?.Kill();
|
PlayersTimer?.Kill();
|
||||||
PlayersTimer = null;
|
PlayersTimer = null;
|
||||||
PlayerManager.CheckPlayersTimer();
|
PlayerManager.CheckPlayersTimer();
|
||||||
|
|
||||||
Menus.MenuManager.Instance.InitializeDefaultCategories();
|
|
||||||
BasicMenu.Initialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnAllPluginsLoaded(bool hotReload)
|
public override void OnAllPluginsLoaded(bool hotReload)
|
||||||
@@ -86,7 +83,7 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||||||
|
|
||||||
AddTimer(6.0f, () => ReloadAdmins(null));
|
AddTimer(6.0f, () => ReloadAdmins(null));
|
||||||
RegisterEvents();
|
RegisterEvents();
|
||||||
AddTimer(0.5f, RegisterCommands.InitializeCommands);
|
RegisterCommands.InitializeCommands();
|
||||||
|
|
||||||
if (!CoreConfig.UnlockConCommands)
|
if (!CoreConfig.UnlockConCommands)
|
||||||
{
|
{
|
||||||
@@ -235,7 +232,7 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||||||
WarnManager = new WarnManager(DatabaseProvider);
|
WarnManager = new WarnManager(DatabaseProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static TargetResult? GetTarget(CommandInfo command, int argument = 1)
|
private static TargetResult? GetTarget(CommandInfo command, int argument = 1)
|
||||||
{
|
{
|
||||||
var matches = command.GetArgTargetResult(argument);
|
var matches = command.GetArgTargetResult(argument);
|
||||||
|
|
||||||
|
|||||||
@@ -81,9 +81,6 @@
|
|||||||
<None Update="Database\Migrations\Mysql\015_steamidToBigInt.sql">
|
<None Update="Database\Migrations\Mysql\015_steamidToBigInt.sql">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="Database\Migrations\Mysql\016_OptimizeTablesAndIndexes.sql">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="Database\Migrations\Sqlite\001_CreateTables.sql">
|
<None Update="Database\Migrations\Sqlite\001_CreateTables.sql">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
@@ -129,9 +126,6 @@
|
|||||||
<None Update="Database\Migrations\Sqlite\015_steamidToBigInt.sql">
|
<None Update="Database\Migrations\Sqlite\015_steamidToBigInt.sql">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="Database\Migrations\Sqlite\016_OptimizeTablesAndIndexes.sql">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -65,11 +65,24 @@ public static class RegisterCommands
|
|||||||
|
|
||||||
new("css_vote", CS2_SimpleAdmin.Instance.OnVoteCommand),
|
new("css_vote", CS2_SimpleAdmin.Instance.OnVoteCommand),
|
||||||
|
|
||||||
|
new("css_noclip", CS2_SimpleAdmin.Instance.OnNoclipCommand),
|
||||||
|
new("css_freeze", CS2_SimpleAdmin.Instance.OnFreezeCommand),
|
||||||
|
new("css_unfreeze", CS2_SimpleAdmin.Instance.OnUnfreezeCommand),
|
||||||
|
new("css_godmode", CS2_SimpleAdmin.Instance.OnGodCommand),
|
||||||
|
|
||||||
new("css_slay", CS2_SimpleAdmin.Instance.OnSlayCommand),
|
new("css_slay", CS2_SimpleAdmin.Instance.OnSlayCommand),
|
||||||
new("css_slap", CS2_SimpleAdmin.Instance.OnSlapCommand),
|
new("css_slap", CS2_SimpleAdmin.Instance.OnSlapCommand),
|
||||||
|
new("css_give", CS2_SimpleAdmin.Instance.OnGiveCommand),
|
||||||
|
new("css_strip", CS2_SimpleAdmin.Instance.OnStripCommand),
|
||||||
|
new("css_hp", CS2_SimpleAdmin.Instance.OnHpCommand),
|
||||||
|
new("css_speed", CS2_SimpleAdmin.Instance.OnSpeedCommand),
|
||||||
|
new("css_gravity", CS2_SimpleAdmin.Instance.OnGravityCommand),
|
||||||
|
new("css_resize", CS2_SimpleAdmin.Instance.OnResizeCommand),
|
||||||
|
new("css_money", CS2_SimpleAdmin.Instance.OnMoneyCommand),
|
||||||
new("css_team", CS2_SimpleAdmin.Instance.OnTeamCommand),
|
new("css_team", CS2_SimpleAdmin.Instance.OnTeamCommand),
|
||||||
new("css_rename", CS2_SimpleAdmin.Instance.OnRenameCommand),
|
new("css_rename", CS2_SimpleAdmin.Instance.OnRenameCommand),
|
||||||
new("css_prename", CS2_SimpleAdmin.Instance.OnPrenameCommand),
|
new("css_prename", CS2_SimpleAdmin.Instance.OnPrenameCommand),
|
||||||
|
new("css_respawn", CS2_SimpleAdmin.Instance.OnRespawnCommand),
|
||||||
new("css_tp", CS2_SimpleAdmin.Instance.OnGotoCommand),
|
new("css_tp", CS2_SimpleAdmin.Instance.OnGotoCommand),
|
||||||
new("css_bring", CS2_SimpleAdmin.Instance.OnBringCommand),
|
new("css_bring", CS2_SimpleAdmin.Instance.OnBringCommand),
|
||||||
new("css_pluginsmanager", CS2_SimpleAdmin.Instance.OnPluginManagerCommand),
|
new("css_pluginsmanager", CS2_SimpleAdmin.Instance.OnPluginManagerCommand),
|
||||||
@@ -147,12 +160,23 @@ public static class RegisterCommands
|
|||||||
{ "css_addsilence", new Command { Aliases = ["css_addsilence"] } },
|
{ "css_addsilence", new Command { Aliases = ["css_addsilence"] } },
|
||||||
{ "css_unsilence", new Command { Aliases = ["css_unsilence"] } },
|
{ "css_unsilence", new Command { Aliases = ["css_unsilence"] } },
|
||||||
{ "css_vote", new Command { Aliases = ["css_vote"] } },
|
{ "css_vote", new Command { Aliases = ["css_vote"] } },
|
||||||
|
{ "css_noclip", new Command { Aliases = ["css_noclip"] } },
|
||||||
|
{ "css_freeze", new Command { Aliases = ["css_freeze"] } },
|
||||||
|
{ "css_unfreeze", new Command { Aliases = ["css_unfreeze"] } },
|
||||||
|
{ "css_godmode", new Command { Aliases = ["css_godmode"] } },
|
||||||
{ "css_slay", new Command { Aliases = ["css_slay"] } },
|
{ "css_slay", new Command { Aliases = ["css_slay"] } },
|
||||||
{ "css_slap", new Command { Aliases = ["css_slap"] } },
|
{ "css_slap", new Command { Aliases = ["css_slap"] } },
|
||||||
|
{ "css_give", new Command { Aliases = ["css_give"] } },
|
||||||
|
{ "css_strip", new Command { Aliases = ["css_strip"] } },
|
||||||
|
{ "css_hp", new Command { Aliases = ["css_hp"] } },
|
||||||
|
{ "css_speed", new Command { Aliases = ["css_speed"] } },
|
||||||
|
{ "css_gravity", new Command { Aliases = ["css_gravity"] } },
|
||||||
|
{ "css_resize", new Command { Aliases = ["css_resize", "css_size"] } },
|
||||||
|
{ "css_money", new Command { Aliases = ["css_money"] } },
|
||||||
{ "css_team", new Command { Aliases = ["css_team"] } },
|
{ "css_team", new Command { Aliases = ["css_team"] } },
|
||||||
{ "css_rename", new Command { Aliases = ["css_rename"] } },
|
{ "css_rename", new Command { Aliases = ["css_rename"] } },
|
||||||
{ "css_prename", new Command { Aliases = ["css_prename"] } },
|
{ "css_prename", new Command { Aliases = ["css_prename"] } },
|
||||||
{ "css_resize", new Command { Aliases = ["css_resize", "css_size"] } },
|
{ "css_respawn", new Command { Aliases = ["css_respawn"] } },
|
||||||
{ "css_tp", new Command { Aliases = ["css_tp", "css_tpto", "css_goto"] } },
|
{ "css_tp", new Command { Aliases = ["css_tp", "css_tpto", "css_goto"] } },
|
||||||
{ "css_bring", new Command { Aliases = ["css_bring", "css_tphere"] } },
|
{ "css_bring", new Command { Aliases = ["css_bring", "css_tphere"] } },
|
||||||
{ "css_pluginsmanager", new Command { Aliases = ["css_pluginsmanager", "css_pluginmanager"] } },
|
{ "css_pluginsmanager", new Command { Aliases = ["css_pluginsmanager", "css_pluginmanager"] } },
|
||||||
@@ -181,26 +205,25 @@ public static class RegisterCommands
|
|||||||
var commandsConfig = JsonSerializer.Deserialize<CommandsConfig>(json,
|
var commandsConfig = JsonSerializer.Deserialize<CommandsConfig>(json,
|
||||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||||
|
|
||||||
if (commandsConfig?.Commands != null)
|
if (commandsConfig?.Commands == null) return;
|
||||||
|
|
||||||
|
foreach (var command in commandsConfig.Commands)
|
||||||
{
|
{
|
||||||
foreach (var command in commandsConfig.Commands)
|
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)
|
||||||
{
|
{
|
||||||
if (command.Value.Aliases == null) continue;
|
CS2_SimpleAdmin.Instance.AddCommand(alias, "", mapping.Callback);
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.Instance.AddCommand(alias, "", mapping.Callback);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var (name, definitions) in _commandDefinitions)
|
foreach (var (name, definitions) in RegisterCommands._commandDefinitions)
|
||||||
{
|
{
|
||||||
foreach (var definition in definitions)
|
foreach (var definition in definitions)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
|
|
||||||
var canPermBan = AdminManager.PlayerHasPermissions(new SteamID(caller.SteamID), "@css/permban");
|
var canPermBan = AdminManager.PlayerHasPermissions(new SteamID(caller.SteamID), "@css/permban");
|
||||||
|
|
||||||
if (duration <= 0 && !canPermBan)
|
if (duration <= 0 && canPermBan == false)
|
||||||
{
|
{
|
||||||
caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_perm_restricted"]}");
|
caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_perm_restricted"]}");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
[CommandHelper(usage: "[#userid or name]", whoCanExecute: CommandUsage.CLIENT_ONLY)]
|
[CommandHelper(usage: "[#userid or name]", whoCanExecute: CommandUsage.CLIENT_ONLY)]
|
||||||
public void OnPenaltiesCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnPenaltiesCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (caller == null || !caller.IsValid || !caller.UserId.HasValue || DatabaseProvider == null)
|
if (caller == null || caller.IsValid == false || !caller.UserId.HasValue || DatabaseProvider == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var userId = caller.UserId.Value;
|
var userId = caller.UserId.Value;
|
||||||
@@ -160,7 +160,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)]
|
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)]
|
||||||
public void OnAdminVoiceCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnAdminVoiceCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (caller == null || !caller.IsValid)
|
if (caller == null || caller.IsValid == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (command.ArgCount > 1)
|
if (command.ArgCount > 1)
|
||||||
@@ -205,7 +205,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)]
|
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)]
|
||||||
public void OnAdminCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnAdminCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (caller == null || !caller.IsValid)
|
if (caller == null || caller.IsValid == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AdminMenu.OpenMenu(caller);
|
AdminMenu.OpenMenu(caller);
|
||||||
|
|||||||
@@ -959,7 +959,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
|
|
||||||
var canPermMute = AdminManager.PlayerHasPermissions(new SteamID(caller.SteamID), "@css/permmute");
|
var canPermMute = AdminManager.PlayerHasPermissions(new SteamID(caller.SteamID), "@css/permmute");
|
||||||
|
|
||||||
if (duration <= 0 && !canPermMute)
|
if (duration <= 0 && canPermMute == false)
|
||||||
{
|
{
|
||||||
caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_perm_restricted"]}");
|
caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_perm_restricted"]}");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,307 +1,307 @@
|
|||||||
// using System.Globalization;
|
using System.Globalization;
|
||||||
// using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
// using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
// using CounterStrikeSharp.API.Modules.Admin;
|
using CounterStrikeSharp.API.Modules.Admin;
|
||||||
// using CounterStrikeSharp.API.Modules.Commands;
|
using CounterStrikeSharp.API.Modules.Commands;
|
||||||
//
|
|
||||||
// namespace CS2_SimpleAdmin;
|
namespace CS2_SimpleAdmin;
|
||||||
//
|
|
||||||
// public partial class CS2_SimpleAdmin
|
public partial class CS2_SimpleAdmin
|
||||||
// {
|
{
|
||||||
// /// <summary>
|
/// <summary>
|
||||||
// /// Enables or disables no-clip mode for specified player(s).
|
/// Enables or disables no-clip mode for specified player(s).
|
||||||
// /// </summary>
|
/// </summary>
|
||||||
// /// <param name="caller">The player issuing the command.</param>
|
/// <param name="caller">The player issuing the command.</param>
|
||||||
// /// <param name="command">The command input containing targets.</param>
|
/// <param name="command">The command input containing targets.</param>
|
||||||
// [CommandHelper(1, "<#userid or name>")]
|
[CommandHelper(1, "<#userid or name>")]
|
||||||
// [RequiresPermissions("@css/cheats")]
|
[RequiresPermissions("@css/cheats")]
|
||||||
// public void OnNoclipCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnNoclipCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
// {
|
{
|
||||||
// var callerName = caller == null ? _localizer?["sa_console"] ?? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
var callerName = caller == null ? _localizer?["sa_console"] ?? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
//
|
|
||||||
// var targets = GetTarget(command);
|
var targets = GetTarget(command);
|
||||||
// if (targets == null) return;
|
if (targets == null) return;
|
||||||
// var playersToTarget = targets.Players.Where(player =>
|
var playersToTarget = targets.Players.Where(player =>
|
||||||
// player.IsValid &&
|
player.IsValid &&
|
||||||
// player is { IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
player is { IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||||
//
|
|
||||||
// playersToTarget.ForEach(player =>
|
playersToTarget.ForEach(player =>
|
||||||
// {
|
{
|
||||||
// if (caller!.CanTarget(player))
|
if (caller!.CanTarget(player))
|
||||||
// {
|
{
|
||||||
// NoClip(caller, player, callerName);
|
NoClip(caller, player, callerName);
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
//
|
|
||||||
// Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// /// <summary>
|
/// <summary>
|
||||||
// /// Toggles no-clip mode for a player and shows admin activity messages.
|
/// Toggles no-clip mode for a player and shows admin activity messages.
|
||||||
// /// </summary>
|
/// </summary>
|
||||||
// /// <param name="caller">The player/admin toggling no-clip.</param>
|
/// <param name="caller">The player/admin toggling no-clip.</param>
|
||||||
// /// <param name="player">The target player whose no-clip state changes.</param>
|
/// <param name="player">The target player whose no-clip state changes.</param>
|
||||||
// /// <param name="callerName">Optional caller name for messages.</param>
|
/// <param name="callerName">Optional caller name for messages.</param>
|
||||||
// /// <param name="command">Optional command info for logging.</param>
|
/// <param name="command">Optional command info for logging.</param>
|
||||||
// internal static void NoClip(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
internal static void NoClip(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
||||||
// {
|
{
|
||||||
// if (!player.IsValid) return;
|
if (!player.IsValid) return;
|
||||||
// if (!caller.CanTarget(player)) return;
|
if (!caller.CanTarget(player)) return;
|
||||||
//
|
|
||||||
// // Set default caller name if not provided
|
// Set default caller name if not provided
|
||||||
// callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||||
//
|
|
||||||
// // Toggle no-clip mode for the player
|
// Toggle no-clip mode for the player
|
||||||
// player.Pawn.Value?.ToggleNoclip();
|
player.Pawn.Value?.ToggleNoclip();
|
||||||
//
|
|
||||||
// // Determine message keys and arguments for the no-clip notification
|
// Determine message keys and arguments for the no-clip notification
|
||||||
// var (activityMessageKey, adminActivityArgs) =
|
var (activityMessageKey, adminActivityArgs) =
|
||||||
// ("sa_admin_noclip_message",
|
("sa_admin_noclip_message",
|
||||||
// new object[] { "CALLER", player.PlayerName });
|
new object[] { "CALLER", player.PlayerName });
|
||||||
//
|
|
||||||
// // Display admin activity message to other players
|
// Display admin activity message to other players
|
||||||
// if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
// {
|
{
|
||||||
// Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// // Log the command
|
// Log the command
|
||||||
// if (command == null)
|
if (command == null)
|
||||||
// Helper.LogCommand(caller, $"css_noclip {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
Helper.LogCommand(caller, $"css_noclip {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// /// <summary>
|
/// <summary>
|
||||||
// /// Enables or disables god mode for specified player(s).
|
/// Enables or disables god mode for specified player(s).
|
||||||
// /// </summary>
|
/// </summary>
|
||||||
// /// <param name="caller">The player issuing the command.</param>
|
/// <param name="caller">The player issuing the command.</param>
|
||||||
// /// <param name="command">The command input containing targets.</param>
|
/// <param name="command">The command input containing targets.</param>
|
||||||
//
|
|
||||||
// [RequiresPermissions("@css/cheats")]
|
[RequiresPermissions("@css/cheats")]
|
||||||
// [CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
// public void OnGodCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnGodCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
// {
|
{
|
||||||
// var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
// var targets = GetTarget(command);
|
var targets = GetTarget(command);
|
||||||
// if (targets == null) return;
|
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();
|
var playersToTarget = targets.Players.Where(player => player.IsValid && player is {IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||||
//
|
|
||||||
// playersToTarget.ForEach(player =>
|
playersToTarget.ForEach(player =>
|
||||||
// {
|
{
|
||||||
// if (player.Connected != PlayerConnectedState.PlayerConnected)
|
if (player.Connected != PlayerConnectedState.PlayerConnected)
|
||||||
// return;
|
return;
|
||||||
//
|
|
||||||
// if (caller!.CanTarget(player))
|
if (caller!.CanTarget(player))
|
||||||
// {
|
{
|
||||||
// God(caller, player, command);
|
God(caller, player, command);
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
//
|
|
||||||
// Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// /// <summary>
|
/// <summary>
|
||||||
// /// Toggles god mode for a player and notifies admins.
|
/// Toggles god mode for a player and notifies admins.
|
||||||
// /// </summary>
|
/// </summary>
|
||||||
// /// <param name="caller">The player/admin toggling god mode.</param>
|
/// <param name="caller">The player/admin toggling god mode.</param>
|
||||||
// /// <param name="player">The target player whose god mode changes.</param>
|
/// <param name="player">The target player whose god mode changes.</param>
|
||||||
// /// <param name="command">Optional command info for logging.</param>
|
/// <param name="command">Optional command info for logging.</param>
|
||||||
// internal static void God(CCSPlayerController? caller, CCSPlayerController player, CommandInfo? command = null)
|
internal static void God(CCSPlayerController? caller, CCSPlayerController player, CommandInfo? command = null)
|
||||||
// {
|
{
|
||||||
// if (!caller.CanTarget(player)) return;
|
if (!caller.CanTarget(player)) return;
|
||||||
//
|
|
||||||
// // Set default caller name if not provided
|
// Set default caller name if not provided
|
||||||
// var callerName = caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
var callerName = caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||||
//
|
|
||||||
// // Toggle god mode for the player
|
// Toggle god mode for the player
|
||||||
// if (!GodPlayers.Add(player.Slot))
|
if (!GodPlayers.Add(player.Slot))
|
||||||
// {
|
{
|
||||||
// GodPlayers.Remove(player.Slot);
|
GodPlayers.Remove(player.Slot);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// // Log the command
|
// Log the command
|
||||||
// if (command == null)
|
if (command == null)
|
||||||
// Helper.LogCommand(caller, $"css_god {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
Helper.LogCommand(caller, $"css_god {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||||
//
|
|
||||||
// // Determine message key and arguments for the god mode notification
|
// Determine message key and arguments for the god mode notification
|
||||||
// var (activityMessageKey, adminActivityArgs) =
|
var (activityMessageKey, adminActivityArgs) =
|
||||||
// ("sa_admin_god_message",
|
("sa_admin_god_message",
|
||||||
// new object[] { "CALLER", player.PlayerName });
|
new object[] { "CALLER", player.PlayerName });
|
||||||
//
|
|
||||||
// // Display admin activity message to other players
|
// Display admin activity message to other players
|
||||||
// if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
// {
|
{
|
||||||
// Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// /// <summary>
|
/// <summary>
|
||||||
// /// Freezes target player(s) for an optional specified duration.
|
/// Freezes target player(s) for an optional specified duration.
|
||||||
// /// </summary>
|
/// </summary>
|
||||||
// /// <param name="caller">The player issuing the freeze command.</param>
|
/// <param name="caller">The player issuing the freeze command.</param>
|
||||||
// /// <param name="command">The command input containing targets and duration.</param>
|
/// <param name="command">The command input containing targets and duration.</param>
|
||||||
// [CommandHelper(1, "<#userid or name> [duration]")]
|
[CommandHelper(1, "<#userid or name> [duration]")]
|
||||||
// [RequiresPermissions("@css/slay")]
|
[RequiresPermissions("@css/slay")]
|
||||||
// public void OnFreezeCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnFreezeCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
// {
|
{
|
||||||
// var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
// int.TryParse(command.GetArg(2), out var time);
|
int.TryParse(command.GetArg(2), out var time);
|
||||||
//
|
|
||||||
// var targets = GetTarget(command);
|
var targets = GetTarget(command);
|
||||||
// if (targets == null) return;
|
if (targets == null) return;
|
||||||
// var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||||
//
|
|
||||||
// playersToTarget.ForEach(player =>
|
playersToTarget.ForEach(player =>
|
||||||
// {
|
{
|
||||||
// if (caller!.CanTarget(player))
|
if (caller!.CanTarget(player))
|
||||||
// {
|
{
|
||||||
// Freeze(caller, player, time, callerName, command);
|
Freeze(caller, player, time, callerName, command);
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
//
|
|
||||||
// Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// /// <summary>
|
/// <summary>
|
||||||
// /// Resizes the target player(s) models to a specified scale.
|
/// Resizes the target player(s) models to a specified scale.
|
||||||
// /// </summary>
|
/// </summary>
|
||||||
// /// <param name="caller">The player issuing the resize command.</param>
|
/// <param name="caller">The player issuing the resize command.</param>
|
||||||
// /// <param name="command">The command input containing targets and scale factor.</param>
|
/// <param name="command">The command input containing targets and scale factor.</param>
|
||||||
// [CommandHelper(1, "<#userid or name> [size]")]
|
[CommandHelper(1, "<#userid or name> [size]")]
|
||||||
// [RequiresPermissions("@css/slay")]
|
[RequiresPermissions("@css/slay")]
|
||||||
// public void OnResizeCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnResizeCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
// {
|
{
|
||||||
// var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
// float.TryParse(command.GetArg(2), NumberStyles.Float, CultureInfo.InvariantCulture, out var size);
|
float.TryParse(command.GetArg(2), NumberStyles.Float, CultureInfo.InvariantCulture, out var size);
|
||||||
//
|
|
||||||
// var targets = GetTarget(command);
|
var targets = GetTarget(command);
|
||||||
// if (targets == null) return;
|
if (targets == null) return;
|
||||||
// var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||||
//
|
|
||||||
// playersToTarget.ForEach(player =>
|
playersToTarget.ForEach(player =>
|
||||||
// {
|
{
|
||||||
// if (!caller!.CanTarget(player)) return;
|
if (!caller!.CanTarget(player)) return;
|
||||||
//
|
|
||||||
// var sceneNode = player.PlayerPawn.Value!.CBodyComponent?.SceneNode;
|
var sceneNode = player.PlayerPawn.Value!.CBodyComponent?.SceneNode;
|
||||||
// if (sceneNode == null) return;
|
if (sceneNode == null) return;
|
||||||
//
|
|
||||||
// sceneNode.GetSkeletonInstance().Scale = size;
|
sceneNode.GetSkeletonInstance().Scale = size;
|
||||||
// player.PlayerPawn.Value.AcceptInput("SetScale", null, null, size.ToString(CultureInfo.InvariantCulture));
|
player.PlayerPawn.Value.AcceptInput("SetScale", null, null, size.ToString(CultureInfo.InvariantCulture));
|
||||||
//
|
|
||||||
// Server.NextWorldUpdate(() =>
|
Server.NextWorldUpdate(() =>
|
||||||
// {
|
{
|
||||||
// Utilities.SetStateChanged(player.PlayerPawn.Value, "CBaseEntity", "m_CBodyComponent");
|
Utilities.SetStateChanged(player.PlayerPawn.Value, "CBaseEntity", "m_CBodyComponent");
|
||||||
// });
|
});
|
||||||
//
|
|
||||||
// var (activityMessageKey, adminActivityArgs) =
|
var (activityMessageKey, adminActivityArgs) =
|
||||||
// ("sa_admin_resize_message",
|
("sa_admin_resize_message",
|
||||||
// new object[] { "CALLER", player.PlayerName });
|
new object[] { "CALLER", player.PlayerName });
|
||||||
//
|
|
||||||
// // Display admin activity message to other players
|
// Display admin activity message to other players
|
||||||
// if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
// {
|
{
|
||||||
// Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
//
|
|
||||||
// Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// /// <summary>
|
/// <summary>
|
||||||
// /// Freezes a single player and optionally schedules automatic unfreeze after a duration.
|
/// Freezes a single player and optionally schedules automatic unfreeze after a duration.
|
||||||
// /// </summary>
|
/// </summary>
|
||||||
// /// <param name="caller">The player/admin freezing the player.</param>
|
/// <param name="caller">The player/admin freezing the player.</param>
|
||||||
// /// <param name="player">The player to freeze.</param>
|
/// <param name="player">The player to freeze.</param>
|
||||||
// /// <param name="time">Duration of freeze in seconds.</param>
|
/// <param name="time">Duration of freeze in seconds.</param>
|
||||||
// /// <param name="callerName">Optional name for notifications.</param>
|
/// <param name="callerName">Optional name for notifications.</param>
|
||||||
// /// <param name="command">Optional command info for logging.</param>
|
/// <param name="command">Optional command info for logging.</param>
|
||||||
// internal static void Freeze(CCSPlayerController? caller, CCSPlayerController player, int time, string? callerName = null, CommandInfo? command = null)
|
internal static void Freeze(CCSPlayerController? caller, CCSPlayerController player, int time, string? callerName = null, CommandInfo? command = null)
|
||||||
// {
|
{
|
||||||
// if (!player.IsValid) return;
|
if (!player.IsValid) return;
|
||||||
// if (!caller.CanTarget(player)) return;
|
if (!caller.CanTarget(player)) return;
|
||||||
//
|
|
||||||
// // Set default caller name if not provided
|
// Set default caller name if not provided
|
||||||
// callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||||
//
|
|
||||||
// // Freeze player pawn
|
// Freeze player pawn
|
||||||
// player.Pawn.Value?.Freeze();
|
player.Pawn.Value?.Freeze();
|
||||||
//
|
|
||||||
// // Determine message keys and arguments for the freeze notification
|
// Determine message keys and arguments for the freeze notification
|
||||||
// var (activityMessageKey, adminActivityArgs) =
|
var (activityMessageKey, adminActivityArgs) =
|
||||||
// ("sa_admin_freeze_message",
|
("sa_admin_freeze_message",
|
||||||
// new object[] { "CALLER", player.PlayerName });
|
new object[] { "CALLER", player.PlayerName });
|
||||||
//
|
|
||||||
// // Display admin activity message to other players
|
// Display admin activity message to other players
|
||||||
// if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
// {
|
{
|
||||||
// Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// // Schedule unfreeze for the player if time is specified
|
// Schedule unfreeze for the player if time is specified
|
||||||
// if (time > 0)
|
if (time > 0)
|
||||||
// {
|
{
|
||||||
// Instance.AddTimer(time, () => player.Pawn.Value?.Unfreeze(), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
Instance.AddTimer(time, () => player.Pawn.Value?.Unfreeze(), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// // Log the command and send Discord notification
|
// Log the command and send Discord notification
|
||||||
// if (command == null)
|
if (command == null)
|
||||||
// Helper.LogCommand(caller, $"css_freeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time}");
|
Helper.LogCommand(caller, $"css_freeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time}");
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// /// <summary>
|
/// <summary>
|
||||||
// /// Unfreezes target player(s).
|
/// Unfreezes target player(s).
|
||||||
// /// </summary>
|
/// </summary>
|
||||||
// /// <param name="caller">The player issuing the unfreeze command.</param>
|
/// <param name="caller">The player issuing the unfreeze command.</param>
|
||||||
// /// <param name="command">The command input with targets.</param>
|
/// <param name="command">The command input with targets.</param>
|
||||||
// [CommandHelper(1, "<#userid or name>")]
|
[CommandHelper(1, "<#userid or name>")]
|
||||||
// [RequiresPermissions("@css/slay")]
|
[RequiresPermissions("@css/slay")]
|
||||||
// public void OnUnfreezeCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnUnfreezeCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
// {
|
{
|
||||||
// var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
//
|
|
||||||
// var targets = GetTarget(command);
|
var targets = GetTarget(command);
|
||||||
// if (targets == null) return;
|
if (targets == null) return;
|
||||||
// var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||||
//
|
|
||||||
// playersToTarget.ForEach(player =>
|
playersToTarget.ForEach(player =>
|
||||||
// {
|
{
|
||||||
// Unfreeze(caller, player, callerName, command);
|
Unfreeze(caller, player, callerName, command);
|
||||||
// });
|
});
|
||||||
//
|
|
||||||
// Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// /// <summary>
|
/// <summary>
|
||||||
// /// Unfreezes a single player and notifies admins.
|
/// Unfreezes a single player and notifies admins.
|
||||||
// /// </summary>
|
/// </summary>
|
||||||
// /// <param name="caller">The player/admin unfreezing the player.</param>
|
/// <param name="caller">The player/admin unfreezing the player.</param>
|
||||||
// /// <param name="player">The player to unfreeze.</param>
|
/// <param name="player">The player to unfreeze.</param>
|
||||||
// /// <param name="callerName">Optional name for notifications.</param>
|
/// <param name="callerName">Optional name for notifications.</param>
|
||||||
// /// <param name="command">Optional command info for logging.</param>
|
/// <param name="command">Optional command info for logging.</param>
|
||||||
// internal static void Unfreeze(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
internal static void Unfreeze(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
||||||
// {
|
{
|
||||||
// if (!player.IsValid) return;
|
if (!player.IsValid) return;
|
||||||
// if (!caller.CanTarget(player)) return;
|
if (!caller.CanTarget(player)) return;
|
||||||
//
|
|
||||||
// // Set default caller name if not provided
|
// Set default caller name if not provided
|
||||||
// callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||||
//
|
|
||||||
// // Unfreeze player pawn
|
// Unfreeze player pawn
|
||||||
// player.Pawn.Value?.Unfreeze();
|
player.Pawn.Value?.Unfreeze();
|
||||||
//
|
|
||||||
// // Determine message keys and arguments for the unfreeze notification
|
// Determine message keys and arguments for the unfreeze notification
|
||||||
// var (activityMessageKey, adminActivityArgs) =
|
var (activityMessageKey, adminActivityArgs) =
|
||||||
// ("sa_admin_unfreeze_message",
|
("sa_admin_unfreeze_message",
|
||||||
// new object[] { "CALLER", player.PlayerName });
|
new object[] { "CALLER", player.PlayerName });
|
||||||
//
|
|
||||||
// // Display admin activity message to other players
|
// Display admin activity message to other players
|
||||||
// if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
// {
|
{
|
||||||
// Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// // Log the command and send Discord notification
|
// Log the command and send Discord notification
|
||||||
// if (command == null)
|
if (command == null)
|
||||||
// Helper.LogCommand(caller, $"css_unfreeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
Helper.LogCommand(caller, $"css_unfreeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
@@ -11,6 +11,9 @@ namespace CS2_SimpleAdmin;
|
|||||||
|
|
||||||
public partial class CS2_SimpleAdmin
|
public partial class CS2_SimpleAdmin
|
||||||
{
|
{
|
||||||
|
internal static readonly Dictionary<CCSPlayerController, float> SpeedPlayers = [];
|
||||||
|
internal static readonly Dictionary<CCSPlayerController, float> GravityPlayers = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Executes the 'slay' command, forcing the targeted players to commit suicide.
|
/// Executes the 'slay' command, forcing the targeted players to commit suicide.
|
||||||
/// Checks player validity and permissions.
|
/// Checks player validity and permissions.
|
||||||
@@ -69,6 +72,451 @@ public partial class CS2_SimpleAdmin
|
|||||||
Helper.LogCommand(caller, $"css_slay {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
Helper.LogCommand(caller, $"css_slay {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the 'give' command to provide a specified weapon to targeted players.
|
||||||
|
/// Enforces server rules for prohibited weapons.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">Player or console issuing the command.</param>
|
||||||
|
/// <param name="command">Command details, including targets and weapon name.</param>
|
||||||
|
[RequiresPermissions("@css/cheats")]
|
||||||
|
[CommandHelper(minArgs: 2, usage: "<#userid or name> <weapon>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
|
public void OnGiveCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
|
{
|
||||||
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
|
var 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();
|
||||||
|
var weaponName = command.GetArg(2);
|
||||||
|
|
||||||
|
// check if weapon is knife
|
||||||
|
if (weaponName.Contains("_knife") || weaponName.Contains("bayonet"))
|
||||||
|
{
|
||||||
|
if (CoreConfig.FollowCS2ServerGuidelines)
|
||||||
|
{
|
||||||
|
command.ReplyToCommand($"Cannot Give {weaponName} because it's illegal to be given.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
playersToTarget.ForEach(player =>
|
||||||
|
{
|
||||||
|
if (player.Connected != PlayerConnectedState.PlayerConnected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
GiveWeapon(caller, player, weaponName, callerName, command);
|
||||||
|
});
|
||||||
|
|
||||||
|
Helper.LogCommand(caller, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gives a weapon identified by name to a player, handling ambiguous matches and logging.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">Admin issuing the command.</param>
|
||||||
|
/// <param name="player">Target player to receive the weapon.</param>
|
||||||
|
/// <param name="weaponName">Weapon name or partial name.</param>
|
||||||
|
/// <param name="callerName">Optional name to display in notifications.</param>
|
||||||
|
/// <param name="command">Optional command info for logging.</param>
|
||||||
|
private static void GiveWeapon(CCSPlayerController? caller, CCSPlayerController player, string weaponName, string? callerName = null, CommandInfo? command = null)
|
||||||
|
{
|
||||||
|
if (!caller.CanTarget(player)) return;
|
||||||
|
|
||||||
|
// Set default caller name if not provided
|
||||||
|
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||||
|
var weapons = WeaponHelper.GetWeaponsByPartialName(weaponName);
|
||||||
|
|
||||||
|
switch (weapons.Count)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return;
|
||||||
|
case > 1:
|
||||||
|
{
|
||||||
|
var weaponList = string.Join(", ", weapons.Select(w => w.EnumMemberValue));
|
||||||
|
command?.ReplyToCommand($"Found weapons with a similar name: {weaponList}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give weapon to the player
|
||||||
|
player.GiveNamedItem(weapons.First().EnumValue);
|
||||||
|
|
||||||
|
// Log the command
|
||||||
|
if (command == null)
|
||||||
|
Helper.LogCommand(caller, $"css_giveweapon {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {weaponName}");
|
||||||
|
|
||||||
|
// Determine message keys and arguments for the weapon give notification
|
||||||
|
var (activityMessageKey, adminActivityArgs) =
|
||||||
|
("sa_admin_give_message",
|
||||||
|
new object[] { "CALLER", player.PlayerName, weaponName });
|
||||||
|
|
||||||
|
// Display admin activity message to other players
|
||||||
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
|
{
|
||||||
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gives a specific weapon to a player, with notifications and logging.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">Admin issuing the command.</param>
|
||||||
|
/// <param name="player">Target player.</param>
|
||||||
|
/// <param name="weapon">Weapon item object.</param>
|
||||||
|
/// <param name="callerName">Optional caller name for notifications.</param>
|
||||||
|
/// <param name="command">Optional command info.</param>
|
||||||
|
internal static void GiveWeapon(CCSPlayerController? caller, CCSPlayerController player, CsItem weapon, string? callerName = null, CommandInfo? command = null)
|
||||||
|
{
|
||||||
|
if (!caller.CanTarget(player)) return;
|
||||||
|
|
||||||
|
// Set default caller name if not provided
|
||||||
|
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||||
|
|
||||||
|
// Give weapon to the player
|
||||||
|
player.GiveNamedItem(weapon);
|
||||||
|
|
||||||
|
// Log the command
|
||||||
|
if (command == null)
|
||||||
|
Helper.LogCommand(caller, $"css_giveweapon {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {weapon.ToString()}");
|
||||||
|
|
||||||
|
// Determine message keys and arguments for the weapon give notification
|
||||||
|
var (activityMessageKey, adminActivityArgs) =
|
||||||
|
("sa_admin_give_message",
|
||||||
|
new object[] { "CALLER", player.PlayerName, weapon.ToString() });
|
||||||
|
|
||||||
|
// Display admin activity message to other players
|
||||||
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
|
{
|
||||||
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the 'strip' command, removing all weapons from targeted players.
|
||||||
|
/// Checks player validity and permissions.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">Player or console issuing the command.</param>
|
||||||
|
/// <param name="command">Command details including targets.</param>
|
||||||
|
[RequiresPermissions("@css/slay")]
|
||||||
|
[CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
|
public void OnStripCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
|
{
|
||||||
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
|
var 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();
|
||||||
|
|
||||||
|
playersToTarget.ForEach(player =>
|
||||||
|
{
|
||||||
|
if (caller!.CanTarget(player))
|
||||||
|
{
|
||||||
|
StripWeapons(caller, player, callerName, command);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Helper.LogCommand(caller, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all weapons from a player, with notifications and logging.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">Admin or console issuing the strip command.</param>
|
||||||
|
/// <param name="player">Target player.</param>
|
||||||
|
/// <param name="callerName">Optional caller name.</param>
|
||||||
|
/// <param name="command">Optional command info for logging.</param>
|
||||||
|
internal static void StripWeapons(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
||||||
|
{
|
||||||
|
if (!caller.CanTarget(player)) return;
|
||||||
|
|
||||||
|
// Set default caller name if not provided
|
||||||
|
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||||
|
|
||||||
|
// Check if player is valid, alive, and connected
|
||||||
|
if (!player.IsValid || player.PlayerPawn?.Value?.LifeState != (int)LifeState_t.LIFE_ALIVE || player.Connected != PlayerConnectedState.PlayerConnected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Strip weapons from the player
|
||||||
|
player.RemoveWeapons();
|
||||||
|
|
||||||
|
// Log the command
|
||||||
|
if (command == null)
|
||||||
|
Helper.LogCommand(caller, $"css_strip {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||||
|
|
||||||
|
// Determine message keys and arguments for the weapon strip notification
|
||||||
|
var (activityMessageKey, adminActivityArgs) =
|
||||||
|
("sa_admin_strip_message",
|
||||||
|
new object[] { "CALLER", player.PlayerName });
|
||||||
|
|
||||||
|
// Display admin activity message to other players
|
||||||
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
|
{
|
||||||
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets health value on targeted players.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">Admin or console issuing the command.</param>
|
||||||
|
/// <param name="command">Command details including targets and health value.</param>
|
||||||
|
[RequiresPermissions("@css/slay")]
|
||||||
|
[CommandHelper(minArgs: 1, usage: "<#userid or name> <health>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
|
public void OnHpCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
|
{
|
||||||
|
int.TryParse(command.GetArg(2), out var health);
|
||||||
|
|
||||||
|
var 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();
|
||||||
|
|
||||||
|
playersToTarget.ForEach(player =>
|
||||||
|
{
|
||||||
|
if (caller!.CanTarget(player))
|
||||||
|
{
|
||||||
|
SetHp(caller, player, health, command);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Helper.LogCommand(caller, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes health of a player and logs the action.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">Admin or console calling the method.</param>
|
||||||
|
/// <param name="player">Target player.</param>
|
||||||
|
/// <param name="health">Health value to set.</param>
|
||||||
|
/// <param name="command">Optional command info.</param>
|
||||||
|
internal static void SetHp(CCSPlayerController? caller, CCSPlayerController player, int health, CommandInfo? command = null)
|
||||||
|
{
|
||||||
|
if (!player.IsValid || player.IsHLTV) return;
|
||||||
|
if (!caller.CanTarget(player)) return;
|
||||||
|
|
||||||
|
// Set default caller name if not provided
|
||||||
|
var callerName = caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||||
|
|
||||||
|
// Set player's health
|
||||||
|
player.SetHp(health);
|
||||||
|
|
||||||
|
// Log the command
|
||||||
|
if (command == null)
|
||||||
|
Helper.LogCommand(caller, $"css_hp {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {health}");
|
||||||
|
|
||||||
|
// Determine message keys and arguments for the HP set notification
|
||||||
|
var (activityMessageKey, adminActivityArgs) =
|
||||||
|
("sa_admin_hp_message",
|
||||||
|
new object[] { "CALLER", player.PlayerName });
|
||||||
|
|
||||||
|
// Display admin activity message to other players
|
||||||
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
|
{
|
||||||
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets movement speed on targeted players.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">Admin or console issuing the command.</param>
|
||||||
|
/// <param name="command">Command details including targets and speed.</param>
|
||||||
|
[RequiresPermissions("@css/slay")]
|
||||||
|
[CommandHelper(minArgs: 1, usage: "<#userid or name> <speed>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
|
public void OnSpeedCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
|
{
|
||||||
|
float.TryParse(command.GetArg(2), NumberStyles.Float, CultureInfo.InvariantCulture, out var speed);
|
||||||
|
|
||||||
|
var 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();
|
||||||
|
|
||||||
|
playersToTarget.ForEach(player =>
|
||||||
|
{
|
||||||
|
if (player.Connected != PlayerConnectedState.PlayerConnected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (caller!.CanTarget(player))
|
||||||
|
{
|
||||||
|
SetSpeed(caller, player, speed, command);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Helper.LogCommand(caller, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes speed of a player and logs the action.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">Admin or console calling the method.</param>
|
||||||
|
/// <param name="player">Target player.</param>
|
||||||
|
/// <param name="speed">Speed value to set.</param>
|
||||||
|
/// <param name="command">Optional command info.</param>
|
||||||
|
internal static void SetSpeed(CCSPlayerController? caller, CCSPlayerController player, float speed, CommandInfo? command = null)
|
||||||
|
{
|
||||||
|
if (!caller.CanTarget(player)) return;
|
||||||
|
|
||||||
|
// Set default caller name if not provided
|
||||||
|
var callerName = caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||||
|
|
||||||
|
// Set player's speed
|
||||||
|
player.SetSpeed(speed);
|
||||||
|
|
||||||
|
if (speed == 1f)
|
||||||
|
SpeedPlayers.Remove(player);
|
||||||
|
else
|
||||||
|
SpeedPlayers[player] = speed;
|
||||||
|
|
||||||
|
// Log the command
|
||||||
|
if (command == null)
|
||||||
|
Helper.LogCommand(caller, $"css_speed {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {speed}");
|
||||||
|
|
||||||
|
// Determine message keys and arguments for the speed set notification
|
||||||
|
var (activityMessageKey, adminActivityArgs) =
|
||||||
|
("sa_admin_speed_message",
|
||||||
|
new object[] { "CALLER", player.PlayerName });
|
||||||
|
|
||||||
|
// Display admin activity message to other players
|
||||||
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
|
{
|
||||||
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets gravity on targeted players.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">Admin or console issuing the command.</param>
|
||||||
|
/// <param name="command">Command details including targets and gravity value.</param>
|
||||||
|
[RequiresPermissions("@css/slay")]
|
||||||
|
[CommandHelper(minArgs: 1, usage: "<#userid or name> <gravity>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
|
public void OnGravityCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
|
{
|
||||||
|
float.TryParse(command.GetArg(2), NumberStyles.Float, CultureInfo.InvariantCulture, out var gravity);
|
||||||
|
|
||||||
|
var 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();
|
||||||
|
|
||||||
|
playersToTarget.ForEach(player =>
|
||||||
|
{
|
||||||
|
if (player.Connected != PlayerConnectedState.PlayerConnected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (caller!.CanTarget(player))
|
||||||
|
{
|
||||||
|
SetGravity(caller, player, gravity, command);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Helper.LogCommand(caller, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes gravity of a player and logs the action.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">Admin or console calling the method.</param>
|
||||||
|
/// <param name="player">Target player.</param>
|
||||||
|
/// <param name="gravity">Gravity value to set.</param>
|
||||||
|
/// <param name="command">Optional command info.</param>
|
||||||
|
internal static void SetGravity(CCSPlayerController? caller, CCSPlayerController player, float gravity, CommandInfo? command = null)
|
||||||
|
{
|
||||||
|
if (!caller.CanTarget(player)) return;
|
||||||
|
|
||||||
|
// Set default caller name if not provided
|
||||||
|
var callerName = caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||||
|
|
||||||
|
// Set player's gravity
|
||||||
|
player.SetGravity(gravity);
|
||||||
|
|
||||||
|
if (gravity == 1f)
|
||||||
|
GravityPlayers.Remove(player);
|
||||||
|
else
|
||||||
|
GravityPlayers[player] = gravity;
|
||||||
|
|
||||||
|
// Log the command
|
||||||
|
if (command == null)
|
||||||
|
Helper.LogCommand(caller, $"css_gravity {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {gravity}");
|
||||||
|
|
||||||
|
// Determine message keys and arguments for the gravity set notification
|
||||||
|
var (activityMessageKey, adminActivityArgs) =
|
||||||
|
("sa_admin_gravity_message",
|
||||||
|
new object[] { "CALLER", player.PlayerName });
|
||||||
|
|
||||||
|
// Display admin activity message to other players
|
||||||
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
|
{
|
||||||
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the money amount for the targeted players.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">The player/admin executing the command.</param>
|
||||||
|
/// <param name="command">The command containing target player and money value.</param>
|
||||||
|
[RequiresPermissions("@css/slay")]
|
||||||
|
[CommandHelper(minArgs: 1, usage: "<#userid or name> <money>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
|
public void OnMoneyCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
|
{
|
||||||
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
|
int.TryParse(command.GetArg(2), out var money);
|
||||||
|
|
||||||
|
var 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();
|
||||||
|
|
||||||
|
playersToTarget.ForEach(player =>
|
||||||
|
{
|
||||||
|
if (player.Connected != PlayerConnectedState.PlayerConnected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (caller!.CanTarget(player))
|
||||||
|
{
|
||||||
|
SetMoney(caller, player, money, command);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Helper.LogCommand(caller, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies money value to a single targeted player and logs the operation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">The player/admin setting the money.</param>
|
||||||
|
/// <param name="player">The player whose money will be set.</param>
|
||||||
|
/// <param name="money">The value of money to set.</param>
|
||||||
|
/// <param name="command">Optional command info for logging.</param>
|
||||||
|
internal static void SetMoney(CCSPlayerController? caller, CCSPlayerController player, int money, CommandInfo? command = null)
|
||||||
|
{
|
||||||
|
if (!caller.CanTarget(player)) return;
|
||||||
|
|
||||||
|
// Set default caller name if not provided
|
||||||
|
var callerName = caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||||
|
|
||||||
|
// Set player's money
|
||||||
|
player.SetMoney(money);
|
||||||
|
|
||||||
|
// Log the command
|
||||||
|
if (command == null)
|
||||||
|
Helper.LogCommand(caller, $"css_money {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {money}");
|
||||||
|
|
||||||
|
// Determine message keys and arguments for the money set notification
|
||||||
|
var (activityMessageKey, adminActivityArgs) =
|
||||||
|
("sa_admin_money_message",
|
||||||
|
new object[] { "CALLER", player.PlayerName });
|
||||||
|
|
||||||
|
// Display admin activity message to other players
|
||||||
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
|
{
|
||||||
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies damage as a slap effect to the targeted players.
|
/// Applies damage as a slap effect to the targeted players.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -354,6 +802,75 @@ public partial class CS2_SimpleAdmin
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Respawns targeted players, restoring their state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">The admin or player issuing respawn.</param>
|
||||||
|
/// <param name="command">The command including target players.</param>
|
||||||
|
[CommandHelper(1, "<#userid or name>")]
|
||||||
|
[RequiresPermissions("@css/cheats")]
|
||||||
|
public void OnRespawnCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
|
{
|
||||||
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
|
|
||||||
|
var targets = GetTarget(command);
|
||||||
|
if (targets == null) return;
|
||||||
|
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||||
|
|
||||||
|
playersToTarget.ForEach(player =>
|
||||||
|
{
|
||||||
|
if (player.Connected != PlayerConnectedState.PlayerConnected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (caller!.CanTarget(player))
|
||||||
|
{
|
||||||
|
Respawn(caller, player, callerName, command);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Helper.LogCommand(caller, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Respawns a specified player and updates admin notifications.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">Admin or player executing respawn.</param>
|
||||||
|
/// <param name="player">Player to respawn.</param>
|
||||||
|
/// <param name="callerName">Optional admin name.</param>
|
||||||
|
/// <param name="command">Optional command info.</param>
|
||||||
|
internal static void Respawn(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
||||||
|
{
|
||||||
|
// Check if the caller can target the player
|
||||||
|
if (!caller.CanTarget(player)) return;
|
||||||
|
|
||||||
|
// Set default caller name if not provided
|
||||||
|
callerName ??= caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
|
|
||||||
|
// Ensure the player's pawn is valid before attempting to respawn
|
||||||
|
if (_cBasePlayerControllerSetPawnFunc == null || player.PlayerPawn.Value == null || !player.PlayerPawn.IsValid) return;
|
||||||
|
|
||||||
|
// Perform the respawn operation
|
||||||
|
var playerPawn = player.PlayerPawn.Value;
|
||||||
|
_cBasePlayerControllerSetPawnFunc.Invoke(player, playerPawn, true, false);
|
||||||
|
VirtualFunction.CreateVoid<CCSPlayerController>(player.Handle, GameData.GetOffset("CCSPlayerController_Respawn"))(player);
|
||||||
|
|
||||||
|
if (player.UserId.HasValue && PlayersInfo.TryGetValue(player.SteamID, out var value) && value.DiePosition != null)
|
||||||
|
playerPawn.Teleport(value.DiePosition?.Position, value.DiePosition?.Angle);
|
||||||
|
|
||||||
|
// Log the command
|
||||||
|
if (command == null)
|
||||||
|
Helper.LogCommand(caller, $"css_respawn {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||||
|
|
||||||
|
// Determine message key and arguments for the respawn notification
|
||||||
|
var activityMessageKey = "sa_admin_respawn_message";
|
||||||
|
var adminActivityArgs = new object[] { "CALLER", player.PlayerName };
|
||||||
|
|
||||||
|
// Display admin activity message to other players
|
||||||
|
if (caller != null && SilentPlayers.Contains(caller.Slot)) return;
|
||||||
|
|
||||||
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Teleports targeted player(s) to another player's location.
|
/// Teleports targeted player(s) to another player's location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -17,27 +17,16 @@ public class Migration(string migrationsPath)
|
|||||||
if (files.Count == 0) return;
|
if (files.Count == 0) return;
|
||||||
|
|
||||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||||
|
|
||||||
await using (var cmd = connection.CreateCommand())
|
await using (var cmd = connection.CreateCommand())
|
||||||
{
|
{
|
||||||
if (migrationsPath.Contains("sqlite", StringComparison.CurrentCultureIgnoreCase))
|
cmd.CommandText = """
|
||||||
{
|
CREATE TABLE IF NOT EXISTS sa_migrations (
|
||||||
cmd.CommandText = """
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
CREATE TABLE IF NOT EXISTS sa_migrations (
|
version TEXT NOT NULL
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
);
|
||||||
version TEXT NOT NULL
|
|
||||||
);
|
""";
|
||||||
|
|
||||||
""";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cmd.CommandText = """
|
|
||||||
CREATE TABLE IF NOT EXISTS sa_migrations (
|
|
||||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
||||||
version VARCHAR(128) NOT NULL
|
|
||||||
);
|
|
||||||
""";
|
|
||||||
}
|
|
||||||
|
|
||||||
await cmd.ExecuteNonQueryAsync();
|
await cmd.ExecuteNonQueryAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ CREATE TABLE IF NOT EXISTS `sa_groups_servers` (
|
|||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
ALTER TABLE `sa_admins` ADD `group_id` INT NULL AFTER `created`;
|
ALTER TABLE `sa_admins` ADD `group_id` INT NULL AFTER `created`;
|
||||||
|
|
||||||
ALTER TABLE `sa_groups_flags` ADD FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE CASCADE;
|
ALTER TABLE `sa_groups_flags` ADD FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE CASCADE;
|
||||||
ALTER TABLE `sa_groups_servers` ADD FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE CASCADE;
|
ALTER TABLE `sa_groups_servers` ADD FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE CASCADE;
|
||||||
ALTER TABLE `sa_admins` ADD FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE SET NULL;
|
ALTER TABLE `sa_admins` ADD FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE SET NULL;
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
CREATE TABLE IF NOT EXISTS `sa_players_ips` (
|
CREATE TABLE IF NOT EXISTS `sa_players_ips` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
`steamid` bigint(20) NOT NULL,
|
`steamid` bigint(20) NOT NULL,
|
||||||
`address` varchar(64) NOT NULL,
|
`address` varchar(64) NOT NULL,
|
||||||
`used_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
`used_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
PRIMARY KEY (`steamid`, `address`)
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `steamid` (`steamid`,`address`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
DELETE FROM `sa_players_ips`
|
||||||
|
WHERE `id` NOT IN (
|
||||||
|
SELECT * FROM (
|
||||||
|
SELECT MIN(`id`)
|
||||||
|
FROM `sa_players_ips`
|
||||||
|
GROUP BY `steamid`
|
||||||
|
) AS `keep_ids`
|
||||||
|
);
|
||||||
DELETE FROM sa_players_ips WHERE INET_ATON(address) IS NULL AND address IS NOT NULL;
|
DELETE FROM sa_players_ips WHERE INET_ATON(address) IS NULL AND address IS NOT NULL;
|
||||||
UPDATE `sa_players_ips` SET `address` = INET_ATON(address);
|
UPDATE `sa_players_ips` SET `address` = INET_ATON(address);
|
||||||
ALTER TABLE `sa_players_ips` CHANGE `address` `address` INT UNSIGNED NOT NULL;
|
ALTER TABLE `sa_players_ips` CHANGE `address` `address` INT UNSIGNED NOT NULL;
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
-- Migration 016: Optimize tables and indexes
|
|
||||||
-- Add proper indexes for all tables to improve query performance
|
|
||||||
|
|
||||||
-- Optimize sa_players_ips table indexes
|
|
||||||
-- Add index on used_at for efficient date-based queries
|
|
||||||
ALTER TABLE `sa_players_ips` ADD INDEX IF NOT EXISTS `idx_used_at` (`used_at` DESC);
|
|
||||||
|
|
||||||
-- Optimize sa_bans table indexes
|
|
||||||
-- Add composite indexes for common query patterns
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_bans_steamid_status` ON `sa_bans` (`player_steamid`, `status`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_bans_ip_status` ON `sa_bans` (`player_ip`, `status`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_bans_status_ends` ON `sa_bans` (`status`, `ends`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_bans_server_status` ON `sa_bans` (`server_id`, `status`, `ends`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_bans_created` ON `sa_bans` (`created` DESC);
|
|
||||||
|
|
||||||
-- Optimize sa_admins table indexes
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_admins_steamid` ON `sa_admins` (`player_steamid`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_admins_server_ends` ON `sa_admins` (`server_id`, `ends`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_admins_ends` ON `sa_admins` (`ends`);
|
|
||||||
|
|
||||||
-- Optimize sa_mutes table indexes (in addition to migration 014)
|
|
||||||
-- Add index for expire queries
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_mutes_status_ends` ON `sa_mutes` (`status`, `ends`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_mutes_server_status` ON `sa_mutes` (`server_id`, `status`, `ends`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_mutes_created` ON `sa_mutes` (`created` DESC);
|
|
||||||
|
|
||||||
-- Optimize sa_warns table indexes (if exists)
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_warns_steamid_status` ON `sa_warns` (`player_steamid`, `status`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_warns_status_ends` ON `sa_warns` (`status`, `ends`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_warns_server_status` ON `sa_warns` (`server_id`, `status`, `ends`);
|
|
||||||
|
|
||||||
-- Add index on sa_servers for faster lookups
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_servers_hostname` ON `sa_servers` (`hostname`);
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
CREATE TABLE IF NOT EXISTS `sa_players_ips` (
|
CREATE TABLE IF NOT EXISTS `sa_players_ips` (
|
||||||
|
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
`steamid` INTEGER NOT NULL,
|
`steamid` INTEGER NOT NULL,
|
||||||
`address` VARCHAR(64) NOT NULL,
|
`address` VARCHAR(64) NOT NULL,
|
||||||
`used_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
`used_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
PRIMARY KEY (`steamid`, `address`)
|
UNIQUE (`steamid`, `address`)
|
||||||
);
|
);
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
-- Migration 016: Optimize tables and indexes
|
|
||||||
-- Add proper indexes for all tables to improve query performance
|
|
||||||
|
|
||||||
-- Optimize sa_players_ips table indexes
|
|
||||||
-- Add index on used_at for efficient date-based queries
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_used_at` ON `sa_players_ips` (`used_at` DESC);
|
|
||||||
|
|
||||||
-- Optimize sa_bans table indexes
|
|
||||||
-- Add composite indexes for common query patterns
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_bans_steamid_status` ON `sa_bans` (`player_steamid`, `status`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_bans_ip_status` ON `sa_bans` (`player_ip`, `status`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_bans_status_ends` ON `sa_bans` (`status`, `ends`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_bans_server_status` ON `sa_bans` (`server_id`, `status`, `ends`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_bans_created` ON `sa_bans` (`created` DESC);
|
|
||||||
|
|
||||||
-- Optimize sa_admins table indexes
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_admins_steamid` ON `sa_admins` (`player_steamid`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_admins_server_ends` ON `sa_admins` (`server_id`, `ends`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_admins_ends` ON `sa_admins` (`ends`);
|
|
||||||
|
|
||||||
-- Optimize sa_mutes table indexes (in addition to migration 014)
|
|
||||||
-- Add index for expire queries
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_mutes_status_ends` ON `sa_mutes` (`status`, `ends`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_mutes_server_status` ON `sa_mutes` (`server_id`, `status`, `ends`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_mutes_created` ON `sa_mutes` (`created` DESC);
|
|
||||||
|
|
||||||
-- Optimize sa_warns table indexes (if exists)
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_warns_steamid_status` ON `sa_warns` (`player_steamid`, `status`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_warns_status_ends` ON `sa_warns` (`status`, `ends`);
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_warns_server_status` ON `sa_warns` (`server_id`, `status`, `ends`);
|
|
||||||
|
|
||||||
-- Add index on sa_servers for faster lookups
|
|
||||||
CREATE INDEX IF NOT EXISTS `idx_servers_hostname` ON `sa_servers` (`hostname`);
|
|
||||||
@@ -9,15 +9,6 @@ public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider
|
|||||||
{
|
{
|
||||||
var connection = new MySqlConnection(connectionString);
|
var connection = new MySqlConnection(connectionString);
|
||||||
await connection.OpenAsync();
|
await connection.OpenAsync();
|
||||||
|
|
||||||
await using var 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();
|
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,9 @@ public partial class CS2_SimpleAdmin
|
|||||||
CachedPlayers.Remove(player);
|
CachedPlayers.Remove(player);
|
||||||
|
|
||||||
SilentPlayers.Remove(player.Slot);
|
SilentPlayers.Remove(player.Slot);
|
||||||
|
GodPlayers.Remove(player.Slot);
|
||||||
|
SpeedPlayers.Remove(player);
|
||||||
|
GravityPlayers.Remove(player);
|
||||||
|
|
||||||
if (player.IsBot)
|
if (player.IsBot)
|
||||||
return HookResult.Continue;
|
return HookResult.Continue;
|
||||||
@@ -244,6 +247,10 @@ public partial class CS2_SimpleAdmin
|
|||||||
Logger.LogCritical("[OnRoundStart]");
|
Logger.LogCritical("[OnRoundStart]");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
GodPlayers.Clear();
|
||||||
|
SpeedPlayers.Clear();
|
||||||
|
GravityPlayers.Clear();
|
||||||
|
|
||||||
foreach (var player in PlayersInfo.Values)
|
foreach (var player in PlayersInfo.Values)
|
||||||
{
|
{
|
||||||
player.DiePosition = null;
|
player.DiePosition = null;
|
||||||
@@ -460,6 +467,8 @@ public partial class CS2_SimpleAdmin
|
|||||||
|
|
||||||
GodPlayers.Clear();
|
GodPlayers.Clear();
|
||||||
SilentPlayers.Clear();
|
SilentPlayers.Clear();
|
||||||
|
SpeedPlayers.Clear();
|
||||||
|
GravityPlayers.Clear();
|
||||||
|
|
||||||
PlayerPenaltyManager.RemoveAllPenalties();
|
PlayerPenaltyManager.RemoveAllPenalties();
|
||||||
}
|
}
|
||||||
@@ -485,9 +494,13 @@ public partial class CS2_SimpleAdmin
|
|||||||
{
|
{
|
||||||
var player = @event.Userid;
|
var player = @event.Userid;
|
||||||
|
|
||||||
if (player?.UserId == null || !player.IsValid || player.IsHLTV ||
|
if (player?.UserId == null || !player.IsValid || player.IsHLTV || player.Connected != PlayerConnectedState.PlayerConnected)
|
||||||
player.Connected != PlayerConnectedState.PlayerConnected || !PlayersInfo.ContainsKey(player.SteamID) ||
|
return HookResult.Continue;
|
||||||
@event.Attacker == null)
|
|
||||||
|
SpeedPlayers.Remove(player);
|
||||||
|
GravityPlayers.Remove(player);
|
||||||
|
|
||||||
|
if (!PlayersInfo.ContainsKey(player.SteamID) || @event.Attacker == null)
|
||||||
return HookResult.Continue;
|
return HookResult.Continue;
|
||||||
|
|
||||||
var playerPosition = player.PlayerPawn.Value?.AbsOrigin;
|
var playerPosition = player.PlayerPawn.Value?.AbsOrigin;
|
||||||
|
|||||||
@@ -335,10 +335,10 @@ public static class PlayerExtensions
|
|||||||
{
|
{
|
||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
sb.Append(localizer[messageKey, messageArgs]);
|
sb.Append(localizer[messageKey, messageArgs]);
|
||||||
|
|
||||||
foreach (var part in Helper.SeparateLines(sb.ToString()))
|
foreach (var part in Helper.SeparateLines(sb.ToString()))
|
||||||
{
|
{
|
||||||
var lineWithPrefix = (CS2_SimpleAdmin._localizer?["sa_prefix"] ?? "") + part.Trim();
|
var lineWithPrefix = localizer["sa_prefix"] + part.Trim();
|
||||||
controller.PrintToChat(lineWithPrefix);
|
controller.PrintToChat(lineWithPrefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -441,20 +441,20 @@ internal static class Helper
|
|||||||
public static void ShowAdminActivity(string messageKey, string? callerName = null, bool dontPublish = false, params object[] messageArgs)
|
public static void ShowAdminActivity(string messageKey, string? callerName = null, bool dontPublish = false, params object[] messageArgs)
|
||||||
{
|
{
|
||||||
string[] publishActions = ["ban", "gag", "silence", "mute"];
|
string[] publishActions = ["ban", "gag", "silence", "mute"];
|
||||||
|
|
||||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.ShowActivityType == 0) return;
|
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.ShowActivityType == 0) return;
|
||||||
if (CS2_SimpleAdmin._localizer == null) return;
|
if (CS2_SimpleAdmin._localizer == null) return;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(callerName))
|
if (string.IsNullOrWhiteSpace(callerName))
|
||||||
callerName = CS2_SimpleAdmin._localizer["sa_console"];
|
callerName = CS2_SimpleAdmin._localizer["sa_console"];
|
||||||
|
|
||||||
var formattedMessageArgs = messageArgs.Select(arg => arg.ToString() ?? string.Empty).ToArray();
|
var formattedMessageArgs = messageArgs.Select(arg => arg.ToString() ?? string.Empty).ToArray();
|
||||||
|
|
||||||
if (!dontPublish && publishActions.Any(messageKey.Contains))
|
if (dontPublish == false && publishActions.Any(messageKey.Contains))
|
||||||
{
|
{
|
||||||
CS2_SimpleAdmin.SimpleAdminApi?.OnAdminShowActivityEvent(messageKey, callerName, dontPublish, messageArgs);
|
CS2_SimpleAdmin.SimpleAdminApi?.OnAdminShowActivityEvent(messageKey, callerName, dontPublish, messageArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Replace placeholder based on showActivityType
|
// // Replace placeholder based on showActivityType
|
||||||
// for (var i = 0; i < formattedMessageArgs.Length; i++)
|
// for (var i = 0; i < formattedMessageArgs.Length; i++)
|
||||||
// {
|
// {
|
||||||
@@ -478,7 +478,7 @@ internal static class Helper
|
|||||||
AdminManager.PlayerHasPermissions(new SteamID(c.SteamID), "@css/kick") ||
|
AdminManager.PlayerHasPermissions(new SteamID(c.SteamID), "@css/kick") ||
|
||||||
AdminManager.PlayerHasPermissions(new SteamID(c.SteamID), "@css/ban"));
|
AdminManager.PlayerHasPermissions(new SteamID(c.SteamID), "@css/ban"));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var controller in validPlayers.ToList())
|
foreach (var controller in validPlayers.ToList())
|
||||||
{
|
{
|
||||||
var currentMessageArgs = (string[])formattedMessageArgs.Clone();
|
var currentMessageArgs = (string[])formattedMessageArgs.Clone();
|
||||||
@@ -499,89 +499,6 @@ internal static class Helper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Shows admin activity with a custom translated message (for modules with their own localizer).
|
|
||||||
/// </summary>
|
|
||||||
public static void ShowAdminActivityTranslated(string translatedMessage, string? callerName = null, bool dontPublish = false)
|
|
||||||
{
|
|
||||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.ShowActivityType == 0) return;
|
|
||||||
if (CS2_SimpleAdmin._localizer == null) return;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(callerName))
|
|
||||||
callerName = CS2_SimpleAdmin._localizer["sa_console"];
|
|
||||||
|
|
||||||
var validPlayers = GetValidPlayers().Where(c => c is { IsValid: true, IsBot: false });
|
|
||||||
|
|
||||||
if (!validPlayers.Any())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.ShowActivityType == 3)
|
|
||||||
{
|
|
||||||
validPlayers = validPlayers.Where(c =>
|
|
||||||
AdminManager.PlayerHasPermissions(new SteamID(c.SteamID), "@css/kick") ||
|
|
||||||
AdminManager.PlayerHasPermissions(new SteamID(c.SteamID), "@css/ban"));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var controller in validPlayers.ToList())
|
|
||||||
{
|
|
||||||
// Replace "CALLER" placeholder based on showActivityType
|
|
||||||
var message = CS2_SimpleAdmin.Instance.Config.OtherSettings.ShowActivityType switch
|
|
||||||
{
|
|
||||||
1 => translatedMessage.Replace("CALLER", AdminManager.PlayerHasPermissions(new SteamID(controller.SteamID), "@css/kick") || AdminManager.PlayerHasPermissions(new SteamID(controller.SteamID), "@css/ban") ? callerName : CS2_SimpleAdmin._localizer["sa_admin"]),
|
|
||||||
_ => translatedMessage.Replace("CALLER", callerName ?? CS2_SimpleAdmin._localizer["sa_console"]),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Send the pre-translated message to the player
|
|
||||||
controller.PrintToChat(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Shows admin activity using module's localizer for per-player language support.
|
|
||||||
/// Each player receives the message in their configured language using SendLocalizedMessage.
|
|
||||||
/// </summary>
|
|
||||||
public static void ShowAdminActivityLocalized(IStringLocalizer moduleLocalizer, string messageKey, string? callerName = null, bool dontPublish = false, params object[] messageArgs)
|
|
||||||
{
|
|
||||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.ShowActivityType == 0) return;
|
|
||||||
if (CS2_SimpleAdmin._localizer == null) return;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(callerName))
|
|
||||||
callerName = CS2_SimpleAdmin._localizer["sa_console"];
|
|
||||||
|
|
||||||
var formattedMessageArgs = messageArgs.Select(arg => arg.ToString() ?? string.Empty).ToArray();
|
|
||||||
|
|
||||||
var validPlayers = GetValidPlayers().Where(c => c is { IsValid: true, IsBot: false });
|
|
||||||
|
|
||||||
if (!validPlayers.Any())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.ShowActivityType == 3)
|
|
||||||
{
|
|
||||||
validPlayers = validPlayers.Where(c =>
|
|
||||||
AdminManager.PlayerHasPermissions(new SteamID(c.SteamID), "@css/kick") ||
|
|
||||||
AdminManager.PlayerHasPermissions(new SteamID(c.SteamID), "@css/ban"));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var controller in validPlayers.ToList())
|
|
||||||
{
|
|
||||||
var currentMessageArgs = (string[])formattedMessageArgs.Clone();
|
|
||||||
|
|
||||||
// Replace "CALLER" placeholder based on showActivityType
|
|
||||||
for (var i = 0; i < currentMessageArgs.Length; i++)
|
|
||||||
{
|
|
||||||
var arg = currentMessageArgs[i];
|
|
||||||
currentMessageArgs[i] = CS2_SimpleAdmin.Instance.Config.OtherSettings.ShowActivityType switch
|
|
||||||
{
|
|
||||||
1 => arg.Replace("CALLER", AdminManager.PlayerHasPermissions(new SteamID(controller.SteamID), "@css/kick") || AdminManager.PlayerHasPermissions(new SteamID(controller.SteamID), "@css/ban") ? callerName : CS2_SimpleAdmin._localizer["sa_admin"]),
|
|
||||||
_ => arg.Replace("CALLER", callerName ?? CS2_SimpleAdmin._localizer["sa_console"]),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the localized message to each player using their language
|
|
||||||
controller.SendLocalizedMessage(moduleLocalizer, messageKey, currentMessageArgs.Cast<object>().ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DisplayCenterMessage(
|
public static void DisplayCenterMessage(
|
||||||
CCSPlayerController player,
|
CCSPlayerController player,
|
||||||
string messageKey,
|
string messageKey,
|
||||||
@@ -877,7 +794,7 @@ internal static class Helper
|
|||||||
if (CS2_SimpleAdmin.DiscordWebhookClientLog == null || CS2_SimpleAdmin._localizer == null)
|
if (CS2_SimpleAdmin.DiscordWebhookClientLog == null || CS2_SimpleAdmin._localizer == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (caller != null && !caller.IsValid)
|
if (caller != null && caller.IsValid == false)
|
||||||
caller = null;
|
caller = null;
|
||||||
|
|
||||||
var callerName = caller == null ? CS2_SimpleAdmin._localizer["sa_console"] : caller.PlayerName;
|
var callerName = caller == null ? CS2_SimpleAdmin._localizer["sa_console"] : caller.PlayerName;
|
||||||
@@ -889,38 +806,32 @@ internal static class Helper
|
|||||||
commandString]));
|
commandString]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable CS8604
|
public static IMenu? CreateMenu(string title, Action<CCSPlayerController>? backAction = null)
|
||||||
public static IMenu? CreateMenu(string title, Action<CCSPlayerController>? backAction = null, Action<CCSPlayerController>? resetAction = null)
|
|
||||||
{
|
{
|
||||||
if (CS2_SimpleAdmin.MenuApi == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var menuType = CS2_SimpleAdmin.Instance.Config.MenuConfigs.MenuType.ToLower();
|
var menuType = CS2_SimpleAdmin.Instance.Config.MenuConfigs.MenuType.ToLower();
|
||||||
|
|
||||||
var menu = menuType switch
|
var menu = menuType switch
|
||||||
{
|
{
|
||||||
_ when menuType.Equals("selectable", StringComparison.CurrentCultureIgnoreCase) =>
|
_ when menuType.Equals("selectable", StringComparison.CurrentCultureIgnoreCase) =>
|
||||||
CS2_SimpleAdmin.MenuApi.GetMenu(title, backAction, resetAction),
|
CS2_SimpleAdmin.MenuApi?.GetMenu(title),
|
||||||
|
|
||||||
_ when menuType.Equals("dynamic", StringComparison.CurrentCultureIgnoreCase) =>
|
_ when menuType.Equals("dynamic", StringComparison.CurrentCultureIgnoreCase) =>
|
||||||
CS2_SimpleAdmin.MenuApi.GetMenuForcetype(title, MenuType.ButtonMenu, backAction, resetAction),
|
CS2_SimpleAdmin.MenuApi?.GetMenuForcetype(title, MenuType.ButtonMenu),
|
||||||
|
|
||||||
_ when menuType.Equals("center", StringComparison.CurrentCultureIgnoreCase) =>
|
_ when menuType.Equals("center", StringComparison.CurrentCultureIgnoreCase) =>
|
||||||
CS2_SimpleAdmin.MenuApi.GetMenuForcetype(title, MenuType.CenterMenu, backAction, resetAction),
|
CS2_SimpleAdmin.MenuApi?.GetMenuForcetype(title, MenuType.CenterMenu),
|
||||||
|
|
||||||
_ when menuType.Equals("chat", StringComparison.CurrentCultureIgnoreCase) =>
|
_ when menuType.Equals("chat", StringComparison.CurrentCultureIgnoreCase) =>
|
||||||
CS2_SimpleAdmin.MenuApi.GetMenuForcetype(title, MenuType.ChatMenu, backAction, resetAction),
|
CS2_SimpleAdmin.MenuApi?.GetMenuForcetype(title, MenuType.ChatMenu),
|
||||||
|
|
||||||
_ when menuType.Equals("console", StringComparison.CurrentCultureIgnoreCase) =>
|
_ when menuType.Equals("console", StringComparison.CurrentCultureIgnoreCase) =>
|
||||||
CS2_SimpleAdmin.MenuApi.GetMenuForcetype(title, MenuType.ConsoleMenu, backAction, resetAction),
|
CS2_SimpleAdmin.MenuApi?.GetMenuForcetype(title, MenuType.ConsoleMenu),
|
||||||
|
|
||||||
_ => CS2_SimpleAdmin.MenuApi.GetMenu(title, backAction, resetAction)
|
_ => CS2_SimpleAdmin.MenuApi?.GetMenu(title)
|
||||||
};
|
};
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
#pragma warning restore CS8604
|
|
||||||
|
|
||||||
internal static IPluginManager? GetPluginManager()
|
internal static IPluginManager? GetPluginManager()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ internal class BanManager(IDatabaseProvider? databaseProvider)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var sql = databaseProvider.GetAddBanQuery();
|
var sql = databaseProvider.GetAddBanQuery();
|
||||||
|
|
||||||
var banId = await connection.ExecuteScalarAsync<int?>(sql, new
|
var banId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||||
{
|
{
|
||||||
playerSteamid = player.SteamId.SteamId64,
|
playerSteamid = player.SteamId.SteamId64,
|
||||||
|
|||||||
@@ -70,44 +70,39 @@ internal class CacheManager: IDisposable
|
|||||||
|
|
||||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp)
|
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp)
|
||||||
{
|
{
|
||||||
// Optimization: Load IP history and build cache in single pass
|
var ipHistory =
|
||||||
var ipHistory = await connection.QueryAsync<(ulong steamid, string? name, uint address, DateTime used_at)>(
|
(await connection.QueryAsync<(ulong steamid, string? name, uint address, DateTime used_at)>(
|
||||||
"SELECT steamid, name, address, used_at FROM sa_players_ips ORDER BY steamid, address, used_at DESC");
|
"SELECT steamid, name, address, used_at FROM sa_players_ips ORDER BY used_at DESC")).ToList();
|
||||||
|
|
||||||
var unknownName = CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
foreach (var group in ipHistory.AsValueEnumerable().GroupBy(x => x.steamid))
|
||||||
var currentSteamId = 0UL;
|
|
||||||
var currentIpSet = new HashSet<IpRecord>(new IpRecordComparer());
|
|
||||||
var latestIpTimestamps = new Dictionary<uint, DateTime>();
|
|
||||||
|
|
||||||
foreach (var record in ipHistory)
|
|
||||||
{
|
{
|
||||||
// When we encounter a new steamid, save the previous one
|
var ipSet = group
|
||||||
if (record.steamid != currentSteamId && currentSteamId != 0)
|
.GroupBy(x => x.address)
|
||||||
{
|
.Select(g =>
|
||||||
_playerIpsCache[currentSteamId] = currentIpSet;
|
{
|
||||||
currentIpSet = new HashSet<IpRecord>(new IpRecordComparer());
|
var latest = g.MaxBy(x => x.used_at);
|
||||||
latestIpTimestamps.Clear();
|
return new IpRecord(
|
||||||
}
|
g.Key,
|
||||||
|
latest.used_at,
|
||||||
|
string.IsNullOrEmpty(latest.name)
|
||||||
|
? CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown"
|
||||||
|
: latest.name
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.ToHashSet(new IpRecordComparer());
|
||||||
|
|
||||||
currentSteamId = record.steamid;
|
_playerIpsCache.AddOrUpdate(
|
||||||
|
group.Key,
|
||||||
// Only keep the latest timestamp for each IP
|
_ => ipSet,
|
||||||
if (!latestIpTimestamps.TryGetValue(record.address, out var existingTimestamp) ||
|
(_, existingSet) =>
|
||||||
record.used_at > existingTimestamp)
|
{
|
||||||
{
|
foreach (var ip in ipSet)
|
||||||
latestIpTimestamps[record.address] = record.used_at;
|
{
|
||||||
currentIpSet.Add(new IpRecord(
|
existingSet.Remove(ip);
|
||||||
record.address,
|
existingSet.Add(ip);
|
||||||
record.used_at,
|
}
|
||||||
string.IsNullOrEmpty(record.name) ? unknownName : record.name
|
return existingSet;
|
||||||
));
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't forget the last steamid
|
|
||||||
if (currentSteamId != 0)
|
|
||||||
{
|
|
||||||
_playerIpsCache[currentSteamId] = currentIpSet;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,10 +150,8 @@ internal class CacheManager: IDisposable
|
|||||||
{
|
{
|
||||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||||
IEnumerable<BanRecord> updatedBans;
|
IEnumerable<BanRecord> updatedBans;
|
||||||
|
|
||||||
// Optimization: Only get IDs for comparison if we need to check for deletions
|
var allIds = (await connection.QueryAsync<int>("SELECT id FROM sa_bans")).ToHashSet();
|
||||||
// Most of the time bans are just added/updated, not deleted
|
|
||||||
HashSet<int>? allIds = null;
|
|
||||||
|
|
||||||
if (CS2_SimpleAdmin.Instance.Config.MultiServerMode)
|
if (CS2_SimpleAdmin.Instance.Config.MultiServerMode)
|
||||||
{
|
{
|
||||||
@@ -168,19 +161,11 @@ internal class CacheManager: IDisposable
|
|||||||
player_name AS PlayerName,
|
player_name AS PlayerName,
|
||||||
player_steamid AS PlayerSteamId,
|
player_steamid AS PlayerSteamId,
|
||||||
player_ip AS PlayerIp,
|
player_ip AS PlayerIp,
|
||||||
status AS Status
|
status AS Status
|
||||||
FROM `sa_bans` WHERE updated_at > @lastUpdate OR created > @lastUpdate ORDER BY updated_at DESC
|
FROM `sa_bans` WHERE updated_at > @lastUpdate OR created > @lastUpdate ORDER BY updated_at DESC
|
||||||
""",
|
""",
|
||||||
new { lastUpdate = _lastUpdateTime }
|
new { lastUpdate = _lastUpdateTime }
|
||||||
));
|
));
|
||||||
|
|
||||||
// Optimization: Only fetch all IDs if there were updates
|
|
||||||
var updatedList = updatedBans.ToList();
|
|
||||||
if (updatedList.Count > 0)
|
|
||||||
{
|
|
||||||
allIds = (await connection.QueryAsync<int>("SELECT id FROM sa_bans")).ToHashSet();
|
|
||||||
}
|
|
||||||
updatedBans = updatedList;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -190,47 +175,32 @@ internal class CacheManager: IDisposable
|
|||||||
player_name AS PlayerName,
|
player_name AS PlayerName,
|
||||||
player_steamid AS PlayerSteamId,
|
player_steamid AS PlayerSteamId,
|
||||||
player_ip AS PlayerIp,
|
player_ip AS PlayerIp,
|
||||||
status AS Status
|
status AS Status
|
||||||
FROM `sa_bans` WHERE (updated_at > @lastUpdate OR created > @lastUpdate) AND server_id = @serverId ORDER BY updated_at DESC
|
FROM `sa_bans` WHERE (updated_at > @lastUpdate OR created > @lastUpdate) AND server_id = @serverId ORDER BY updated_at DESC
|
||||||
""",
|
""",
|
||||||
new { lastUpdate = _lastUpdateTime, serverId = CS2_SimpleAdmin.ServerId }
|
new { lastUpdate = _lastUpdateTime, serverId = CS2_SimpleAdmin.ServerId }
|
||||||
));
|
));
|
||||||
|
|
||||||
// Optimization: Only fetch all IDs if there were updates
|
|
||||||
var updatedList = updatedBans.ToList();
|
|
||||||
if (updatedList.Count > 0)
|
|
||||||
{
|
|
||||||
allIds = (await connection.QueryAsync<int>(
|
|
||||||
"SELECT id FROM sa_bans WHERE server_id = @serverId",
|
|
||||||
new { serverId = CS2_SimpleAdmin.ServerId }
|
|
||||||
)).ToHashSet();
|
|
||||||
}
|
|
||||||
updatedBans = updatedList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimization: Only process deletions if we have the full ID list
|
foreach (var id in _banCache.Keys)
|
||||||
if (allIds != null)
|
|
||||||
{
|
{
|
||||||
foreach (var id in _banCache.Keys)
|
if (allIds.Contains(id) || !_banCache.TryRemove(id, out var ban)) continue;
|
||||||
|
|
||||||
|
if (ban.PlayerSteamId != null &&
|
||||||
|
_steamIdIndex.TryGetValue(ban.PlayerSteamId.Value, out var steamBans))
|
||||||
{
|
{
|
||||||
if (allIds.Contains(id) || !_banCache.TryRemove(id, out var ban)) continue;
|
steamBans.RemoveAll(b => b.Id == id);
|
||||||
|
if (steamBans.Count == 0)
|
||||||
|
_steamIdIndex.TryRemove(ban.PlayerSteamId.Value, out _);
|
||||||
|
}
|
||||||
|
|
||||||
if (ban.PlayerSteamId != null &&
|
if (string.IsNullOrWhiteSpace(ban.PlayerIp) ||
|
||||||
_steamIdIndex.TryGetValue(ban.PlayerSteamId.Value, out var steamBans))
|
!IpHelper.TryConvertIpToUint(ban.PlayerIp, out var ipUInt) ||
|
||||||
{
|
!_ipIndex.TryGetValue(ipUInt, out var ipBans)) continue;
|
||||||
steamBans.RemoveAll(b => b.Id == id);
|
{
|
||||||
if (steamBans.Count == 0)
|
ipBans.RemoveAll(b => b.Id == id);
|
||||||
_steamIdIndex.TryRemove(ban.PlayerSteamId.Value, out _);
|
if (ipBans.Count == 0)
|
||||||
}
|
_ipIndex.TryRemove(ipUInt, out _);
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(ban.PlayerIp) ||
|
|
||||||
!IpHelper.TryConvertIpToUint(ban.PlayerIp, out var ipUInt) ||
|
|
||||||
!_ipIndex.TryGetValue(ipUInt, out var ipBans)) continue;
|
|
||||||
{
|
|
||||||
ipBans.RemoveAll(b => b.Id == id);
|
|
||||||
if (ipBans.Count == 0)
|
|
||||||
_ipIndex.TryRemove(ipUInt, out _);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,21 +245,12 @@ internal class CacheManager: IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update cache with new/modified bans
|
|
||||||
var hasUpdates = false;
|
|
||||||
foreach (var ban in updatedBans)
|
foreach (var ban in updatedBans)
|
||||||
{
|
{
|
||||||
_banCache.AddOrUpdate(ban.Id, ban, (_, _) => ban);
|
_banCache.AddOrUpdate(ban.Id, ban, (_, _) => ban);
|
||||||
hasUpdates = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always rebuild indexes if there were any updates
|
RebuildIndexes();
|
||||||
// This ensures status changes (ACTIVE -> UNBANNED) are reflected
|
|
||||||
if (hasUpdates)
|
|
||||||
{
|
|
||||||
RebuildIndexes();
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastUpdateTime = Time.ActualDateTime().AddSeconds(-1);
|
_lastUpdateTime = Time.ActualDateTime().AddSeconds(-1);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
@@ -305,38 +266,38 @@ internal class CacheManager: IDisposable
|
|||||||
{
|
{
|
||||||
_steamIdIndex.Clear();
|
_steamIdIndex.Clear();
|
||||||
_ipIndex.Clear();
|
_ipIndex.Clear();
|
||||||
|
|
||||||
// Optimization: Cache config value to avoid repeated property access
|
foreach (var ban in _banCache.Values)
|
||||||
var banType = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType;
|
|
||||||
var 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);
|
|
||||||
|
|
||||||
foreach (var ban in activeBans)
|
|
||||||
{
|
{
|
||||||
// Index by Steam ID
|
if (ban.StatusEnum != BanStatus.ACTIVE)
|
||||||
if (ban.PlayerSteamId.HasValue)
|
continue;
|
||||||
{
|
|
||||||
var steamId = ban.PlayerSteamId.Value;
|
|
||||||
if (!_steamIdIndex.TryGetValue(steamId, out var steamList))
|
|
||||||
{
|
|
||||||
steamList = new List<BanRecord>();
|
|
||||||
_steamIdIndex[steamId] = steamList;
|
|
||||||
}
|
|
||||||
steamList.Add(ban);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Index by IP (only if IP bans are enabled)
|
if (ban.PlayerSteamId != null)
|
||||||
if (checkIpBans && !string.IsNullOrEmpty(ban.PlayerIp) &&
|
{
|
||||||
|
var steamId = ban.PlayerSteamId;
|
||||||
|
_steamIdIndex.AddOrUpdate(
|
||||||
|
steamId.Value,
|
||||||
|
key => [ban],
|
||||||
|
(key, list) =>
|
||||||
|
{
|
||||||
|
list.Add(ban);
|
||||||
|
return list;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0) continue;
|
||||||
|
|
||||||
|
if (ban.PlayerIp != null &&
|
||||||
IpHelper.TryConvertIpToUint(ban.PlayerIp, out var ipUInt))
|
IpHelper.TryConvertIpToUint(ban.PlayerIp, out var ipUInt))
|
||||||
{
|
{
|
||||||
if (!_ipIndex.TryGetValue(ipUInt, out var ipList))
|
_ipIndex.AddOrUpdate(
|
||||||
{
|
ipUInt,
|
||||||
ipList = new List<BanRecord>();
|
key => [ban],
|
||||||
_ipIndex[ipUInt] = ipList;
|
(key, list) =>
|
||||||
}
|
{
|
||||||
ipList.Add(ban);
|
list.Add(ban);
|
||||||
|
return list;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -371,16 +332,16 @@ internal class CacheManager: IDisposable
|
|||||||
{
|
{
|
||||||
var ipAsUint = IpHelper.IpToUint(ipAddress);
|
var ipAsUint = IpHelper.IpToUint(ipAddress);
|
||||||
var results = new List<(ulong, DateTime, string)>();
|
var results = new List<(ulong, DateTime, string)>();
|
||||||
|
var comparer = _playerIpsCache.Comparer;
|
||||||
// Optimization: Direct lookup using HashSet.Contains instead of TryGetValue
|
|
||||||
var searchRecord = new IpRecord(ipAsUint, default, null!);
|
|
||||||
|
|
||||||
foreach (var (steamId, ipSet) in _playerIpsCache)
|
foreach (var (steamId, ipSet) in _playerIpsCache)
|
||||||
{
|
{
|
||||||
// Optimization: Single pass through the set
|
if (!ipSet.TryGetValue(new IpRecord(ipAsUint, Time.ActualDateTime(), "Unknown"), out var actualEntry)) continue;
|
||||||
|
results.Add((steamId, actualEntry.UsedAt, actualEntry.PlayerName));
|
||||||
|
|
||||||
foreach (var entry in ipSet)
|
foreach (var entry in ipSet)
|
||||||
{
|
{
|
||||||
if (entry.Ip == ipAsUint)
|
if (entry.Ip == ipAsUint && !Equals(entry, actualEntry))
|
||||||
{
|
{
|
||||||
results.Add((steamId, entry.UsedAt, entry.PlayerName));
|
results.Add((steamId, entry.UsedAt, entry.PlayerName));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,36 +95,66 @@ internal class PlayerManager
|
|||||||
{
|
{
|
||||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||||
|
|
||||||
// Eliminates the need for SELECT COUNT and duplicate UPDATE queries
|
if (CS2_SimpleAdmin.Instance.CacheManager.HasIpForPlayer(
|
||||||
var steamId64 = CS2_SimpleAdmin.PlayersInfo[steamId].SteamId.SteamId64;
|
steamId, ipAddress))
|
||||||
var ipUint = IpHelper.IpToUint(ipAddress);
|
|
||||||
|
|
||||||
// MySQL: INSERT ... ON DUPLICATE KEY UPDATE pattern
|
|
||||||
const string upsertQuery = """
|
|
||||||
INSERT INTO `sa_players_ips` (steamid, name, address, used_at)
|
|
||||||
VALUES (@SteamID, @playerName, @IPAddress, CURRENT_TIMESTAMP)
|
|
||||||
ON DUPLICATE KEY UPDATE
|
|
||||||
used_at = CURRENT_TIMESTAMP,
|
|
||||||
name = @playerName;
|
|
||||||
""";
|
|
||||||
|
|
||||||
await connection.ExecuteAsync(upsertQuery, new
|
|
||||||
{
|
{
|
||||||
SteamID = steamId64,
|
const string updateQuery = """
|
||||||
playerName,
|
UPDATE `sa_players_ips`
|
||||||
IPAddress = ipUint
|
SET used_at = CURRENT_TIMESTAMP,
|
||||||
});
|
name = @playerName
|
||||||
|
WHERE steamid = @SteamID AND address = @IPAddress;
|
||||||
|
""";
|
||||||
|
await connection.ExecuteAsync(updateQuery, new
|
||||||
|
{
|
||||||
|
playerName,
|
||||||
|
SteamID = CS2_SimpleAdmin.PlayersInfo[steamId].SteamId.SteamId64,
|
||||||
|
IPAddress = IpHelper.IpToUint(ipAddress)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const string selectQuery =
|
||||||
|
"SELECT COUNT(*) FROM `sa_players_ips` WHERE steamid = @SteamID AND address = @IPAddress;";
|
||||||
|
var recordExists = await connection.ExecuteScalarAsync<int>(selectQuery, new
|
||||||
|
{
|
||||||
|
SteamID = CS2_SimpleAdmin.PlayersInfo[steamId].SteamId.SteamId64,
|
||||||
|
IPAddress = IpHelper.IpToUint(ipAddress)
|
||||||
|
});
|
||||||
|
|
||||||
// // Cache will be updated on next refresh cycle
|
if (recordExists > 0)
|
||||||
// if (!CS2_SimpleAdmin.Instance.CacheManager.HasIpForPlayer(steamId, ipAddress))
|
{
|
||||||
// {
|
const string updateQuery = """
|
||||||
// // IP association will be reflected after cache refresh
|
UPDATE `sa_players_ips`
|
||||||
// }
|
SET used_at = CURRENT_TIMESTAMP,
|
||||||
|
name = @playerName
|
||||||
|
WHERE steamid = @SteamID AND address = @IPAddress;
|
||||||
|
""";
|
||||||
|
await connection.ExecuteAsync(updateQuery, new
|
||||||
|
{
|
||||||
|
playerName,
|
||||||
|
SteamID = CS2_SimpleAdmin.PlayersInfo[steamId].SteamId.SteamId64,
|
||||||
|
IPAddress = IpHelper.IpToUint(ipAddress)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const string insertQuery = """
|
||||||
|
INSERT INTO `sa_players_ips` (steamid, name, address, used_at)
|
||||||
|
VALUES (@SteamID, @playerName, @IPAddress, CURRENT_TIMESTAMP);
|
||||||
|
""";
|
||||||
|
await connection.ExecuteAsync(insertQuery, new
|
||||||
|
{
|
||||||
|
SteamID = CS2_SimpleAdmin.PlayersInfo[steamId].SteamId.SteamId64,
|
||||||
|
playerName,
|
||||||
|
IPAddress = IpHelper.IpToUint(ipAddress)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
CS2_SimpleAdmin._logger?.LogError(
|
CS2_SimpleAdmin._logger?.LogError(
|
||||||
$"Unable to save ip address for {playerInfo.Name} ({ipAddress}): {ex.Message}");
|
$"Unable to save ip address for {playerInfo.Name} ({ipAddress}) {ex.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
playerInfo.AccountsAssociated =
|
playerInfo.AccountsAssociated =
|
||||||
@@ -283,6 +313,31 @@ internal class PlayerManager
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public void CheckPlayersTimer()
|
public void CheckPlayersTimer()
|
||||||
{
|
{
|
||||||
|
CS2_SimpleAdmin.Instance.AddTimer(0.12f, () =>
|
||||||
|
{
|
||||||
|
if (CS2_SimpleAdmin.SpeedPlayers.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var (player, speed) in CS2_SimpleAdmin.SpeedPlayers)
|
||||||
|
{
|
||||||
|
if (player is { IsValid: true, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE })
|
||||||
|
{
|
||||||
|
player.SetSpeed(speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CS2_SimpleAdmin.GravityPlayers.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var (player, gravity) in CS2_SimpleAdmin.GravityPlayers)
|
||||||
|
{
|
||||||
|
if (player is { IsValid: true, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE })
|
||||||
|
{
|
||||||
|
player.SetGravity(gravity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, TimerFlags.REPEAT);
|
||||||
|
|
||||||
CS2_SimpleAdmin.Instance.PlayersTimer = CS2_SimpleAdmin.Instance.AddTimer(61.0f, () =>
|
CS2_SimpleAdmin.Instance.PlayersTimer = CS2_SimpleAdmin.Instance.AddTimer(61.0f, () =>
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
@@ -291,26 +346,18 @@ internal class PlayerManager
|
|||||||
if (CS2_SimpleAdmin.DatabaseProvider == null)
|
if (CS2_SimpleAdmin.DatabaseProvider == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Optimization: Get players once and avoid allocating anonymous types
|
var tempPlayers = Helper.GetValidPlayers()
|
||||||
var validPlayers = Helper.GetValidPlayers();
|
.Select(p => new
|
||||||
if (validPlayers.Count == 0)
|
{
|
||||||
return;
|
p.PlayerName, p.SteamID, p.IpAddress, p.UserId, p.Slot,
|
||||||
|
})
|
||||||
// Use ValueTuple instead of anonymous type - better performance and less allocations
|
.ToList();
|
||||||
var tempPlayers = new List<(string PlayerName, ulong SteamID, string? IpAddress, int? UserId, int Slot)>(validPlayers.Count);
|
|
||||||
foreach (var p in validPlayers)
|
|
||||||
{
|
|
||||||
tempPlayers.Add((p.PlayerName, p.SteamID, p.IpAddress, p.UserId, p.Slot));
|
|
||||||
}
|
|
||||||
|
|
||||||
var pluginInstance = CS2_SimpleAdmin.Instance;
|
var pluginInstance = CS2_SimpleAdmin.Instance;
|
||||||
var config = _config.OtherSettings; // Cache config access
|
|
||||||
|
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Run all expire tasks in parallel
|
|
||||||
var expireTasks = new[]
|
var expireTasks = new[]
|
||||||
{
|
{
|
||||||
pluginInstance.BanManager.ExpireOldBans(),
|
pluginInstance.BanManager.ExpireOldBans(),
|
||||||
@@ -337,33 +384,22 @@ internal class PlayerManager
|
|||||||
|
|
||||||
if (pluginInstance.CacheManager == null)
|
if (pluginInstance.CacheManager == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Optimization: Cache ban type and multi-account check to avoid repeated config access
|
var bannedPlayers = tempPlayers.AsValueEnumerable()
|
||||||
var banType = config.BanType;
|
.Where(player =>
|
||||||
var checkMultiAccounts = config.CheckMultiAccountsByIp;
|
|
||||||
|
|
||||||
var 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)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName;
|
|
||||||
var steamId = player.SteamID;
|
|
||||||
var ip = player.IpAddress?.Split(':')[0];
|
|
||||||
|
|
||||||
bool isBanned = banType switch
|
|
||||||
{
|
{
|
||||||
0 => pluginInstance.CacheManager.IsPlayerBanned(playerName, steamId, null),
|
var playerName = player.PlayerName;
|
||||||
_ => checkMultiAccounts
|
var steamId = player.SteamID;
|
||||||
? pluginInstance.CacheManager.IsPlayerOrAnyIpBanned(playerName, steamId, ip)
|
var ip = player.IpAddress?.Split(':')[0];
|
||||||
: pluginInstance.CacheManager.IsPlayerBanned(playerName, steamId, ip)
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isBanned)
|
return CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType switch
|
||||||
{
|
{
|
||||||
bannedPlayers.Add(player);
|
0 => pluginInstance.CacheManager.IsPlayerBanned(playerName, steamId, null),
|
||||||
}
|
_ => CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp
|
||||||
}
|
? pluginInstance.CacheManager.IsPlayerOrAnyIpBanned(playerName, steamId, ip)
|
||||||
|
: pluginInstance.CacheManager.IsPlayerBanned(playerName, steamId, ip)
|
||||||
|
};
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
if (bannedPlayers.Count > 0)
|
if (bannedPlayers.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -374,38 +410,32 @@ internal class PlayerManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.TimeMode == 0)
|
if (_config.OtherSettings.TimeMode == 0)
|
||||||
{
|
{
|
||||||
// Optimization: Manual projection instead of LINQ
|
var onlinePlayers = tempPlayers.AsValueEnumerable().Select(player => (player.SteamID, player.UserId, player.Slot)).ToList();
|
||||||
var onlinePlayers = new List<(ulong, int?, int)>(tempPlayers.Count);
|
if (tempPlayers.Count == 0 || onlinePlayers.Count == 0) return;
|
||||||
foreach (var player in tempPlayers)
|
|
||||||
{
|
|
||||||
onlinePlayers.Add((player.SteamID, player.UserId, player.Slot));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onlinePlayers.Count > 0)
|
|
||||||
{
|
|
||||||
await pluginInstance.MuteManager.CheckOnlineModeMutes(onlinePlayers);
|
await pluginInstance.MuteManager.CheckOnlineModeMutes(onlinePlayers);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Optimization: Process penalties without LINQ allocations
|
|
||||||
var players = Helper.GetValidPlayers();
|
var players = Helper.GetValidPlayers();
|
||||||
foreach (var player in players)
|
var penalizedSlots = players
|
||||||
|
.Where(player => PlayerPenaltyManager.IsSlotInPenalties(player.Slot))
|
||||||
|
.Select(player => new
|
||||||
|
{
|
||||||
|
Player = player,
|
||||||
|
IsMuted = PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Mute, out _),
|
||||||
|
IsSilenced = PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence, out _),
|
||||||
|
IsGagged = PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag, out _)
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var entry in penalizedSlots)
|
||||||
{
|
{
|
||||||
if (!PlayerPenaltyManager.IsSlotInPenalties(player.Slot))
|
if (!entry.IsMuted && !entry.IsSilenced)
|
||||||
continue;
|
|
||||||
|
|
||||||
var isMuted = PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Mute, out _);
|
|
||||||
var isSilenced = PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence, out _);
|
|
||||||
|
|
||||||
// Only reset voice flags if not muted or silenced
|
|
||||||
if (!isMuted && !isSilenced)
|
|
||||||
{
|
{
|
||||||
player.VoiceFlags = VoiceFlags.Normal;
|
entry.Player.VoiceFlags = VoiceFlags.Normal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,43 +35,29 @@ public class ServerManager
|
|||||||
{
|
{
|
||||||
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
|
if (_getIpTryCount > 32 && Helper.GetServerIp().StartsWith("0.0.0.0") || string.IsNullOrEmpty(Helper.GetServerIp()))
|
||||||
var serverIp = Helper.GetServerIp();
|
|
||||||
var isInvalidIp = string.IsNullOrEmpty(serverIp) || serverIp.StartsWith("0.0.0");
|
|
||||||
|
|
||||||
// Check if we've exceeded retry limit with invalid IP
|
|
||||||
if (_getIpTryCount > 32 && isInvalidIp)
|
|
||||||
{
|
{
|
||||||
CS2_SimpleAdmin._logger?.LogError("Unable to load server data - can't fetch ip address!");
|
CS2_SimpleAdmin._logger?.LogError("Unable to load server data - can't fetch ip address!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimization: Cache ConVar lookups
|
var ipAddress = ConVar.Find("ip")?.StringValue;
|
||||||
var ipConVar = ConVar.Find("ip");
|
|
||||||
var ipAddress = ipConVar?.StringValue;
|
|
||||||
|
|
||||||
// Use Helper IP if ConVar IP is invalid
|
|
||||||
if (string.IsNullOrEmpty(ipAddress) || ipAddress.StartsWith("0.0.0"))
|
if (string.IsNullOrEmpty(ipAddress) || ipAddress.StartsWith("0.0.0"))
|
||||||
{
|
{
|
||||||
ipAddress = serverIp;
|
ipAddress = Helper.GetServerIp();
|
||||||
|
|
||||||
// Retry if still invalid and under retry limit
|
if (_getIpTryCount <= 32 && (string.IsNullOrEmpty(ipAddress) || ipAddress.StartsWith("0.0.0")))
|
||||||
if (_getIpTryCount <= 32 && isInvalidIp)
|
|
||||||
{
|
{
|
||||||
_getIpTryCount++;
|
_getIpTryCount++;
|
||||||
|
|
||||||
LoadServerData();
|
LoadServerData();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimization: Cache remaining ConVar lookups
|
var address = $"{ipAddress}:{ConVar.Find("hostport")?.GetPrimitiveValue<int>()}";
|
||||||
var hostportConVar = ConVar.Find("hostport");
|
var hostname = ConVar.Find("hostname")?.StringValue ?? CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
||||||
var hostnameConVar = ConVar.Find("hostname");
|
var rconPassword = ConVar.Find("rcon_password")?.StringValue ?? "";
|
||||||
var 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 ?? "";
|
|
||||||
CS2_SimpleAdmin.IpAddress = address;
|
CS2_SimpleAdmin.IpAddress = address;
|
||||||
|
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
@@ -132,8 +118,6 @@ public class ServerManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
CS2_SimpleAdmin.SimpleAdminApi?.OnSimpleAdminReadyEvent();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,11 +7,6 @@ namespace CS2_SimpleAdmin.Menus;
|
|||||||
|
|
||||||
public static class AdminMenu
|
public static class AdminMenu
|
||||||
{
|
{
|
||||||
public static void OpenMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
MenuManager.Instance.OpenMainMenu(admin);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IMenu? CreateMenu(string title, Action<CCSPlayerController>? backAction = null)
|
public static IMenu? CreateMenu(string title, Action<CCSPlayerController>? backAction = null)
|
||||||
{
|
{
|
||||||
return Helper.CreateMenu(title, backAction);
|
return Helper.CreateMenu(title, backAction);
|
||||||
@@ -32,44 +27,44 @@ public static class AdminMenu
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// public static void OpenMenu(CCSPlayerController admin)
|
public static void OpenMenu(CCSPlayerController admin)
|
||||||
// {
|
{
|
||||||
// if (admin.IsValid == false)
|
if (admin.IsValid == false)
|
||||||
// return;
|
return;
|
||||||
//
|
|
||||||
// var localizer = CS2_SimpleAdmin._localizer;
|
var localizer = CS2_SimpleAdmin._localizer;
|
||||||
// if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic") == false)
|
if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic") == false)
|
||||||
// {
|
{
|
||||||
// admin.PrintToChat(localizer?["sa_prefix"] ??
|
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||||
// "[SimpleAdmin] " +
|
"[SimpleAdmin] " +
|
||||||
// (localizer?["sa_no_permission"] ?? "You do not have permissions to use this command")
|
(localizer?["sa_no_permission"] ?? "You do not have permissions to use this command")
|
||||||
// );
|
);
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// var menu = CreateMenu(localizer?["sa_title"] ?? "SimpleAdmin");
|
var menu = CreateMenu(localizer?["sa_title"] ?? "SimpleAdmin");
|
||||||
// List<ChatMenuOptionData> options =
|
List<ChatMenuOptionData> options =
|
||||||
// [
|
[
|
||||||
// new(localizer?["sa_menu_players_manage"] ?? "Players Manage", () => ManagePlayersMenu.OpenMenu(admin)),
|
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_server_manage"] ?? "Server Manage", () => ManageServerMenu.OpenMenu(admin)),
|
||||||
// new(localizer?["sa_menu_fun_commands"] ?? "Fun Commands", () => FunActionsMenu.OpenMenu(admin)),
|
new(localizer?["sa_menu_fun_commands"] ?? "Fun Commands", () => FunActionsMenu.OpenMenu(admin)),
|
||||||
// ];
|
];
|
||||||
//
|
|
||||||
// var customCommands = CS2_SimpleAdmin.Instance.Config.CustomServerCommands;
|
var customCommands = CS2_SimpleAdmin.Instance.Config.CustomServerCommands;
|
||||||
// if (customCommands.Count > 0)
|
if (customCommands.Count > 0)
|
||||||
// {
|
{
|
||||||
// options.Add(new ChatMenuOptionData(localizer?["sa_menu_custom_commands"] ?? "Custom Commands", () => CustomCommandsMenu.OpenMenu(admin)));
|
options.Add(new ChatMenuOptionData(localizer?["sa_menu_custom_commands"] ?? "Custom Commands", () => CustomCommandsMenu.OpenMenu(admin)));
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/root"))
|
if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/root"))
|
||||||
// options.Add(new ChatMenuOptionData(localizer?["sa_menu_admins_manage"] ?? "Admins Manage", () => ManageAdminsMenu.OpenMenu(admin)));
|
options.Add(new ChatMenuOptionData(localizer?["sa_menu_admins_manage"] ?? "Admins Manage", () => ManageAdminsMenu.OpenMenu(admin)));
|
||||||
//
|
|
||||||
// foreach (var menuOptionData in options)
|
foreach (var menuOptionData in options)
|
||||||
// {
|
{
|
||||||
// var menuName = menuOptionData.Name;
|
var menuName = menuOptionData.Name;
|
||||||
// menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// if (menu != null) OpenMenu(admin, menu);
|
if (menu != null) OpenMenu(admin, menu);
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
@@ -1,602 +0,0 @@
|
|||||||
using CounterStrikeSharp.API;
|
|
||||||
using CounterStrikeSharp.API.Core;
|
|
||||||
using CounterStrikeSharp.API.Modules.Admin;
|
|
||||||
using CounterStrikeSharp.API.Modules.Entities;
|
|
||||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
|
||||||
using CS2_SimpleAdminApi;
|
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin.Menus;
|
|
||||||
|
|
||||||
public abstract class BasicMenu
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes all menus in the system by registering them with the MenuManager.
|
|
||||||
/// </summary>
|
|
||||||
public static void Initialize()
|
|
||||||
{
|
|
||||||
var manager = MenuManager.Instance;
|
|
||||||
|
|
||||||
// Players category menus
|
|
||||||
manager.RegisterMenu("players", "slap", "Slap Player", CreateSlapMenu, "@css/slay");
|
|
||||||
manager.RegisterMenu("players", "slay", "Slay Player", CreateSlayMenu, "@css/slay");
|
|
||||||
manager.RegisterMenu("players", "kick", "Kick Player", CreateKickMenu, "@css/kick");
|
|
||||||
manager.RegisterMenu("players", "warn", "Warn Player", CreateWarnMenu, "@css/kick");
|
|
||||||
manager.RegisterMenu("players", "ban", "Ban Player", CreateBanMenu, "@css/ban");
|
|
||||||
manager.RegisterMenu("players", "gag", "Gag Player", CreateGagMenu, "@css/chat");
|
|
||||||
manager.RegisterMenu("players", "mute", "Mute Player", CreateMuteMenu, "@css/chat");
|
|
||||||
manager.RegisterMenu("players", "silence", "Silence Player", CreateSilenceMenu, "@css/chat");
|
|
||||||
manager.RegisterMenu("players", "team", "Force Team", CreateForceTeamMenu, "@css/kick");
|
|
||||||
|
|
||||||
// Server category menus
|
|
||||||
manager.RegisterMenu("server", "plugins", "Manage Plugins", CreatePluginsMenu, "@css/root");
|
|
||||||
manager.RegisterMenu("server", "changemap", "Change Map", CreateChangeMapMenu, "@css/changemap");
|
|
||||||
manager.RegisterMenu("server", "restart", "Restart Game", CreateRestartGameMenu, "@css/generic");
|
|
||||||
manager.RegisterMenu("server", "custom", "Custom Commands", CreateCustomCommandsMenu, "@css/generic");
|
|
||||||
|
|
||||||
// Admin category menus
|
|
||||||
manager.RegisterMenu("admin", "add", "Add Admin", CreateAddAdminMenu, "@css/root");
|
|
||||||
manager.RegisterMenu("admin", "remove", "Remove Admin", CreateRemoveAdminMenu, "@css/root");
|
|
||||||
manager.RegisterMenu("admin", "reload", "Reload Admins", CreateReloadAdminsMenu, "@css/root");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for slapping players with selectable damage amounts.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the slap menu.</returns>
|
|
||||||
|
|
||||||
private static MenuBuilder CreateSlapMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var slapMenu = new MenuBuilder(localizer?["sa_slap"] ?? "Slap Player");
|
|
||||||
|
|
||||||
var players = Helper.GetValidPlayers().Where(admin.CanTarget);
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
slapMenu.AddSubMenu(playerName, () => CreateSlapDamageMenu(admin, player));
|
|
||||||
}
|
|
||||||
|
|
||||||
return slapMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates damage selection submenu for slapping a specific player.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player executing the slap.</param>
|
|
||||||
/// <param name="target">The target player to be slapped.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the slap damage menu.</returns>
|
|
||||||
private static MenuBuilder CreateSlapDamageMenu(CCSPlayerController admin, CCSPlayerController target)
|
|
||||||
{
|
|
||||||
var slapDamageMenu = new MenuBuilder($"Slap: {target.PlayerName}");
|
|
||||||
var damages = new[] { 0, 1, 5, 10, 50, 100 };
|
|
||||||
|
|
||||||
foreach (var damage in damages)
|
|
||||||
{
|
|
||||||
slapDamageMenu.AddOption($"{damage} HP", _ =>
|
|
||||||
{
|
|
||||||
if (target.IsValid)
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.Slap(admin, target, damage);
|
|
||||||
// Keep menu open for consecutive slaps
|
|
||||||
CreateSlapDamageMenu(admin, target).OpenMenu(admin);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return slapDamageMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for slaying (killing) players.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the slay menu.</returns>
|
|
||||||
private static MenuBuilder CreateSlayMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var slayMenu = new MenuBuilder(localizer?["sa_slay"] ?? "Slay Player");
|
|
||||||
|
|
||||||
var players = Helper.GetValidPlayers().Where(admin.CanTarget);
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
slayMenu.AddOption(playerName, _ =>
|
|
||||||
{
|
|
||||||
if (player.IsValid)
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.Slay(admin, player);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return slayMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for kicking players with reason selection.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the kick menu.</returns>
|
|
||||||
private static MenuBuilder CreateKickMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var kickMenu = new MenuBuilder(localizer?["sa_kick"] ?? "Kick Player");
|
|
||||||
|
|
||||||
var players = Helper.GetValidPlayers().Where(p => !p.IsBot && admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
kickMenu.AddSubMenu(playerName, () => CreateReasonMenu(admin, player, "Kick", PenaltyType.Kick,
|
|
||||||
(_, _, reason) =>
|
|
||||||
{
|
|
||||||
if (player.IsValid)
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.Instance.Kick(admin, player, reason, admin.PlayerName);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
return kickMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for warning players with duration and reason selection.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the warn menu.</returns>
|
|
||||||
private static MenuBuilder CreateWarnMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var warnMenu = new MenuBuilder(localizer?["sa_warn"] ?? "Warn Player");
|
|
||||||
|
|
||||||
var players = Helper.GetValidPlayers().Where(p => !p.IsBot && admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
warnMenu.AddSubMenu(playerName, () => CreateDurationMenu(admin, player, "Warn",
|
|
||||||
(_, _, duration) => CreateReasonMenu(admin, player, "Warn", PenaltyType.Warn,
|
|
||||||
(_, _, reason) =>
|
|
||||||
{
|
|
||||||
if (player.IsValid)
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.Instance.Warn(admin, player, duration, reason, admin.PlayerName);
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
|
|
||||||
return warnMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for banning players with duration and reason selection.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the ban menu.</returns>
|
|
||||||
private static MenuBuilder CreateBanMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var banMenu = new MenuBuilder(localizer?["sa_ban"] ?? "Ban Player");
|
|
||||||
|
|
||||||
var players = Helper.GetValidPlayers().Where(p => !p.IsBot && admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
banMenu.AddSubMenu(playerName, () => CreateDurationMenu(admin, player, "Ban",
|
|
||||||
(_, _, duration) => CreateReasonMenu(admin, player, "Ban", PenaltyType.Ban,
|
|
||||||
(_, _, reason) =>
|
|
||||||
{
|
|
||||||
if (player.IsValid)
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.Instance.Ban(admin, player, duration, reason, admin.PlayerName);
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
|
|
||||||
return banMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for gagging (text chat muting) players with duration and reason selection.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the gag menu.</returns>
|
|
||||||
private static MenuBuilder CreateGagMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var gagMenu = new MenuBuilder(localizer?["sa_gag"] ?? "Gag Player");
|
|
||||||
|
|
||||||
var players = Helper.GetValidPlayers().Where(p => !p.IsBot && admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
gagMenu.AddSubMenu(playerName, () => CreateDurationMenu(admin, player, "Gag",
|
|
||||||
(_, _, duration) => CreateReasonMenu(admin, player, "Gag", PenaltyType.Gag,
|
|
||||||
(_, _, reason) =>
|
|
||||||
{
|
|
||||||
if (player.IsValid)
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.Instance.Gag(admin, player, duration, reason);
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
|
|
||||||
return gagMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for muting (voice chat muting) players with duration and reason selection.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the mute menu.</returns>
|
|
||||||
private static MenuBuilder CreateMuteMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var muteMenu = new MenuBuilder(localizer?["sa_mute"] ?? "Mute Player");
|
|
||||||
|
|
||||||
var players = Helper.GetValidPlayers().Where(p => !p.IsBot && admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
muteMenu.AddSubMenu(playerName, () => CreateDurationMenu(admin, player, "Mute",
|
|
||||||
(_, _, duration) => CreateReasonMenu(admin, player, "Mute", PenaltyType.Mute,
|
|
||||||
(_, _, reason) =>
|
|
||||||
{
|
|
||||||
if (player.IsValid)
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.Instance.Mute(admin, player, duration, reason);
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
|
|
||||||
return muteMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for silencing (both text and voice chat muting) players with duration and reason selection.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the silence menu.</returns>
|
|
||||||
private static MenuBuilder CreateSilenceMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var silenceMenu = new MenuBuilder(localizer?["sa_silence"] ?? "Silence Player");
|
|
||||||
|
|
||||||
var players = Helper.GetValidPlayers().Where(p => !p.IsBot && admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
silenceMenu.AddSubMenu(playerName, () => CreateDurationMenu(admin, player, "Silence",
|
|
||||||
(_, _, duration) => CreateReasonMenu(admin, player, "Silence", PenaltyType.Silence,
|
|
||||||
(_, _, reason) =>
|
|
||||||
{
|
|
||||||
if (player.IsValid)
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.Instance.Silence(admin, player, duration, reason);
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
|
|
||||||
return silenceMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for forcing players to switch teams.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the force team menu.</returns>
|
|
||||||
private static MenuBuilder CreateForceTeamMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var teamMenu = new MenuBuilder(localizer?["sa_team_force"] ?? "Force Team");
|
|
||||||
|
|
||||||
var players = Helper.GetValidPlayers().Where(p => admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
teamMenu.AddSubMenu(playerName, () => CreateTeamSelectionMenu(admin, player));
|
|
||||||
}
|
|
||||||
|
|
||||||
return teamMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates team selection submenu for forcing a specific player to a team.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player executing the team change.</param>
|
|
||||||
/// <param name="target">The target player to be moved.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the team selection menu.</returns>
|
|
||||||
private static MenuBuilder CreateTeamSelectionMenu(CCSPlayerController admin, CCSPlayerController target)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var teamSelectionMenu = new MenuBuilder($"Force Team: {target.PlayerName}");
|
|
||||||
|
|
||||||
var teams = new[]
|
|
||||||
{
|
|
||||||
(localizer?["sa_team_ct"] ?? "CT", "ct", CsTeam.CounterTerrorist),
|
|
||||||
(localizer?["sa_team_t"] ?? "T", "t", CsTeam.Terrorist),
|
|
||||||
(localizer?["sa_team_swap"] ?? "Swap", "swap", CsTeam.Spectator),
|
|
||||||
(localizer?["sa_team_spec"] ?? "Spec", "spec", CsTeam.Spectator)
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var (name, teamName, teamNum) in teams)
|
|
||||||
{
|
|
||||||
teamSelectionMenu.AddOption(name, _ =>
|
|
||||||
{
|
|
||||||
if (target.IsValid)
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.ChangeTeam(admin, target, teamName, teamNum, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return teamSelectionMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for managing server plugins.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the plugins menu.</returns>
|
|
||||||
private static MenuBuilder CreatePluginsMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var pluginsMenu = new MenuBuilder(localizer?["sa_menu_pluginsmanager_title"] ?? "Manage Plugins");
|
|
||||||
|
|
||||||
pluginsMenu.AddOption("Open Plugins Manager", _ =>
|
|
||||||
{
|
|
||||||
admin.ExecuteClientCommandFromServer("css_pluginsmanager");
|
|
||||||
});
|
|
||||||
|
|
||||||
return pluginsMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for changing the current map (includes default and workshop maps).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the change map menu.</returns>
|
|
||||||
private static MenuBuilder CreateChangeMapMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var mapMenu = new MenuBuilder(localizer?["sa_changemap"] ?? "Change Map");
|
|
||||||
|
|
||||||
// Add default maps
|
|
||||||
var maps = CS2_SimpleAdmin.Instance.Config.DefaultMaps;
|
|
||||||
foreach (var map in maps)
|
|
||||||
{
|
|
||||||
mapMenu.AddOption(map, _ =>
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.Instance.ChangeMap(admin, map);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add workshop maps
|
|
||||||
var wsMaps = CS2_SimpleAdmin.Instance.Config.WorkshopMaps;
|
|
||||||
foreach (var wsMap in wsMaps)
|
|
||||||
{
|
|
||||||
mapMenu.AddOption($"{wsMap.Key} (WS)", _ =>
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.Instance.ChangeWorkshopMap(admin, wsMap.Value?.ToString() ?? wsMap.Key);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return mapMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for restarting the current game/round.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the restart game menu.</returns>
|
|
||||||
private static MenuBuilder CreateRestartGameMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var restartMenu = new MenuBuilder(localizer?["sa_restart_game"] ?? "Restart Game");
|
|
||||||
|
|
||||||
restartMenu.AddOption("Restart Round", _ =>
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.RestartGame(admin);
|
|
||||||
});
|
|
||||||
|
|
||||||
return restartMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for executing custom server commands defined in configuration.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the custom commands menu.</returns>
|
|
||||||
private static MenuBuilder CreateCustomCommandsMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var customMenu = new MenuBuilder(localizer?["sa_menu_custom_commands"] ?? "Custom Commands");
|
|
||||||
|
|
||||||
var customCommands = CS2_SimpleAdmin.Instance.Config.CustomServerCommands;
|
|
||||||
|
|
||||||
foreach (var customCommand in customCommands)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(customCommand.DisplayName) || string.IsNullOrEmpty(customCommand.Command))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var steamId = new SteamID(admin.SteamID);
|
|
||||||
if (!AdminManager.PlayerHasPermissions(steamId, customCommand.Flag))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
customMenu.AddOption(customCommand.DisplayName, _ =>
|
|
||||||
{
|
|
||||||
Helper.TryLogCommandOnDiscord(admin, customCommand.Command);
|
|
||||||
|
|
||||||
if (customCommand.ExecuteOnClient)
|
|
||||||
admin.ExecuteClientCommandFromServer(customCommand.Command);
|
|
||||||
else
|
|
||||||
Server.ExecuteCommand(customCommand.Command);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return customMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for adding admin privileges to players.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the add admin menu.</returns>
|
|
||||||
private static MenuBuilder CreateAddAdminMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var addAdminMenu = new MenuBuilder(localizer?["sa_admin_add"] ?? "Add Admin");
|
|
||||||
|
|
||||||
var players = Helper.GetValidPlayers().Where(p => !p.IsBot && admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
addAdminMenu.AddSubMenu(playerName, () => CreateAdminFlagsMenu(admin, player));
|
|
||||||
}
|
|
||||||
|
|
||||||
return addAdminMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates admin flags selection submenu for granting specific permissions to a player.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player granting permissions.</param>
|
|
||||||
/// <param name="target">The target player to receive admin privileges.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the admin flags menu.</returns>
|
|
||||||
private static MenuBuilder CreateAdminFlagsMenu(CCSPlayerController admin, CCSPlayerController target)
|
|
||||||
{
|
|
||||||
var flagsMenu = new MenuBuilder($"Add Admin: {target.PlayerName}");
|
|
||||||
|
|
||||||
foreach (var adminFlag in CS2_SimpleAdmin.Instance.Config.MenuConfigs.AdminFlags)
|
|
||||||
{
|
|
||||||
var hasFlag = AdminManager.PlayerHasPermissions(target, adminFlag.Flag);
|
|
||||||
flagsMenu.AddOption(adminFlag.Name, _ =>
|
|
||||||
{
|
|
||||||
if (target.IsValid)
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.AddAdmin(admin, target.SteamID.ToString(), target.PlayerName, adminFlag.Flag, 10);
|
|
||||||
}
|
|
||||||
}, hasFlag); // Disabled if player already has this flag
|
|
||||||
}
|
|
||||||
|
|
||||||
return flagsMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for removing admin privileges from players.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the remove admin menu.</returns>
|
|
||||||
private static MenuBuilder CreateRemoveAdminMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var removeAdminMenu = new MenuBuilder(localizer?["sa_admin_remove"] ?? "Remove Admin");
|
|
||||||
|
|
||||||
var adminPlayers = Helper.GetValidPlayers().Where(p =>
|
|
||||||
AdminManager.GetPlayerAdminData(p)?.Flags.Count > 0 &&
|
|
||||||
p != admin &&
|
|
||||||
admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in adminPlayers)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
removeAdminMenu.AddOption(playerName, _ =>
|
|
||||||
{
|
|
||||||
if (player.IsValid)
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.Instance.RemoveAdmin(admin, player.SteamID.ToString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return removeAdminMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates menu for reloading admin list from database.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player opening the menu.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the reload admins menu.</returns>
|
|
||||||
private static MenuBuilder CreateReloadAdminsMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var reloadMenu = new MenuBuilder(localizer?["sa_admin_reload"] ?? "Reload Admins");
|
|
||||||
|
|
||||||
reloadMenu.AddOption("Reload Admins", _ =>
|
|
||||||
{
|
|
||||||
CS2_SimpleAdmin.Instance.ReloadAdmins(admin);
|
|
||||||
});
|
|
||||||
|
|
||||||
return reloadMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates duration selection submenu for time-based penalties (ban, mute, gag, etc.).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player selecting duration.</param>
|
|
||||||
/// <param name="player">The target player for the penalty.</param>
|
|
||||||
/// <param name="actionName">The name of the penalty action.</param>
|
|
||||||
/// <param name="onSelectAction">Callback action executed when duration is selected.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the duration menu.</returns>
|
|
||||||
private static MenuBuilder CreateDurationMenu(CCSPlayerController admin, CCSPlayerController player, string actionName,
|
|
||||||
Action<CCSPlayerController, CCSPlayerController, int> onSelectAction)
|
|
||||||
{
|
|
||||||
var durationMenu = new MenuBuilder($"{actionName} Duration: {player.PlayerName}");
|
|
||||||
|
|
||||||
foreach (var durationItem in CS2_SimpleAdmin.Instance.Config.MenuConfigs.Durations)
|
|
||||||
{
|
|
||||||
durationMenu.AddOption(durationItem.Name, _ =>
|
|
||||||
{
|
|
||||||
onSelectAction(admin, player, durationItem.Duration);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return durationMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates reason selection submenu for penalties with predefined reasons from configuration.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="admin">The admin player selecting reason.</param>
|
|
||||||
/// <param name="player">The target player for the penalty.</param>
|
|
||||||
/// <param name="actionName">The name of the penalty action.</param>
|
|
||||||
/// <param name="penaltyType">The type of penalty to determine which reason list to use.</param>
|
|
||||||
/// <param name="onSelectAction">Callback action executed when reason is selected.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the reason menu.</returns>
|
|
||||||
private static MenuBuilder CreateReasonMenu(CCSPlayerController admin, CCSPlayerController player, string actionName,
|
|
||||||
PenaltyType penaltyType, Action<CCSPlayerController, CCSPlayerController, string> onSelectAction)
|
|
||||||
{
|
|
||||||
var reasonMenu = new MenuBuilder($"{actionName} Reason: {player.PlayerName}");
|
|
||||||
|
|
||||||
var reasons = penaltyType switch
|
|
||||||
{
|
|
||||||
PenaltyType.Ban => CS2_SimpleAdmin.Instance.Config.MenuConfigs.BanReasons,
|
|
||||||
PenaltyType.Kick => CS2_SimpleAdmin.Instance.Config.MenuConfigs.KickReasons,
|
|
||||||
PenaltyType.Mute => CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons,
|
|
||||||
PenaltyType.Warn => CS2_SimpleAdmin.Instance.Config.MenuConfigs.WarnReasons,
|
|
||||||
PenaltyType.Gag or PenaltyType.Silence => CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons,
|
|
||||||
_ => CS2_SimpleAdmin.Instance.Config.MenuConfigs.BanReasons
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var reason in reasons)
|
|
||||||
{
|
|
||||||
reasonMenu.AddOption(reason, _ =>
|
|
||||||
{
|
|
||||||
onSelectAction(admin, player, reason);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return reasonMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,11 +9,11 @@ public static class CustomCommandsMenu
|
|||||||
{
|
{
|
||||||
public static void OpenMenu(CCSPlayerController admin)
|
public static void OpenMenu(CCSPlayerController admin)
|
||||||
{
|
{
|
||||||
if (!admin.IsValid)
|
if (admin.IsValid == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
var localizer = CS2_SimpleAdmin._localizer;
|
||||||
if (!AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic"))
|
if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic") == false)
|
||||||
{
|
{
|
||||||
admin.PrintToChat(localizer?["sa_prefix"] ??
|
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||||
"[SimpleAdmin] " +
|
"[SimpleAdmin] " +
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ public static class DurationMenu
|
|||||||
public static void OpenMenu(CCSPlayerController admin, string menuName, CCSPlayerController player, Action<CCSPlayerController, CCSPlayerController, int> onSelectAction)
|
public static void OpenMenu(CCSPlayerController admin, string menuName, CCSPlayerController player, Action<CCSPlayerController, CCSPlayerController, int> onSelectAction)
|
||||||
{
|
{
|
||||||
var menu = AdminMenu.CreateMenu(menuName);
|
var menu = AdminMenu.CreateMenu(menuName);
|
||||||
|
|
||||||
foreach (var durationItem in CS2_SimpleAdmin.Instance.Config.MenuConfigs.Durations)
|
foreach (var durationItem in CS2_SimpleAdmin.Instance.Config.MenuConfigs.Durations)
|
||||||
{
|
{
|
||||||
menu?.AddMenuOption(durationItem.Name, (_, _) => { onSelectAction(admin, player, durationItem.Duration); });
|
menu?.AddMenuOption(durationItem.Name, (_, _) => { onSelectAction(admin, player, durationItem.Duration); });
|
||||||
@@ -19,6 +20,7 @@ public static class DurationMenu
|
|||||||
public static void OpenMenu(CCSPlayerController admin, string menuName, DisconnectedPlayer player, Action<CCSPlayerController, DisconnectedPlayer, int> onSelectAction)
|
public static void OpenMenu(CCSPlayerController admin, string menuName, DisconnectedPlayer player, Action<CCSPlayerController, DisconnectedPlayer, int> onSelectAction)
|
||||||
{
|
{
|
||||||
var menu = AdminMenu.CreateMenu(menuName);
|
var menu = AdminMenu.CreateMenu(menuName);
|
||||||
|
|
||||||
foreach (var durationItem in CS2_SimpleAdmin.Instance.Config.MenuConfigs.Durations)
|
foreach (var durationItem in CS2_SimpleAdmin.Instance.Config.MenuConfigs.Durations)
|
||||||
{
|
{
|
||||||
menu?.AddMenuOption(durationItem.Name, (_, _) => { onSelectAction(admin, player, durationItem.Duration); });
|
menu?.AddMenuOption(durationItem.Name, (_, _) => { onSelectAction(admin, player, durationItem.Duration); });
|
||||||
|
|||||||
@@ -1,267 +1,267 @@
|
|||||||
// using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
// using CounterStrikeSharp.API.Modules.Admin;
|
using CounterStrikeSharp.API.Modules.Admin;
|
||||||
// using CounterStrikeSharp.API.Modules.Entities;
|
using CounterStrikeSharp.API.Modules.Entities;
|
||||||
// using CounterStrikeSharp.API.Modules.Entities.Constants;
|
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||||
//
|
|
||||||
// namespace CS2_SimpleAdmin.Menus;
|
namespace CS2_SimpleAdmin.Menus;
|
||||||
//
|
|
||||||
// public static class FunActionsMenu
|
public static class FunActionsMenu
|
||||||
// {
|
{
|
||||||
// private static Dictionary<int, CsItem>? _weaponsCache;
|
private static Dictionary<int, CsItem>? _weaponsCache;
|
||||||
//
|
|
||||||
// private static Dictionary<int, CsItem> GetWeaponsCache
|
private static Dictionary<int, CsItem> GetWeaponsCache
|
||||||
// {
|
{
|
||||||
// get
|
get
|
||||||
// {
|
{
|
||||||
// if (_weaponsCache != null) return _weaponsCache;
|
if (_weaponsCache != null) return _weaponsCache;
|
||||||
//
|
|
||||||
// var weaponsArray = Enum.GetValues(typeof(CsItem));
|
var weaponsArray = Enum.GetValues(typeof(CsItem));
|
||||||
//
|
|
||||||
// // avoid duplicates in the menu
|
// avoid duplicates in the menu
|
||||||
// _weaponsCache = new Dictionary<int, CsItem>();
|
_weaponsCache = new Dictionary<int, CsItem>();
|
||||||
// foreach (CsItem item in weaponsArray)
|
foreach (CsItem item in weaponsArray)
|
||||||
// {
|
{
|
||||||
// if (item == CsItem.Tablet)
|
if (item == CsItem.Tablet)
|
||||||
// continue;
|
continue;
|
||||||
//
|
|
||||||
// _weaponsCache[(int)item] = item;
|
_weaponsCache[(int)item] = item;
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// return _weaponsCache;
|
return _weaponsCache;
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// public static void OpenMenu(CCSPlayerController admin)
|
public static void OpenMenu(CCSPlayerController admin)
|
||||||
// {
|
{
|
||||||
// if (!admin.IsValid)
|
if (admin.IsValid == false)
|
||||||
// return;
|
return;
|
||||||
//
|
|
||||||
// var localizer = CS2_SimpleAdmin._localizer;
|
var localizer = CS2_SimpleAdmin._localizer;
|
||||||
// if (!AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic"))
|
if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic") == false)
|
||||||
// {
|
{
|
||||||
// admin.PrintToChat(localizer?["sa_prefix"] ??
|
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||||
// "[SimpleAdmin] " +
|
"[SimpleAdmin] " +
|
||||||
// (localizer?["sa_no_permission"] ?? "You do not have permissions to use this command")
|
(localizer?["sa_no_permission"] ?? "You do not have permissions to use this command")
|
||||||
// );
|
);
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// var menu = AdminMenu.CreateMenu(localizer?["sa_menu_fun_commands"] ?? "Fun Commands");
|
var menu = AdminMenu.CreateMenu(localizer?["sa_menu_fun_commands"] ?? "Fun Commands");
|
||||||
// List<ChatMenuOptionData> options = [];
|
List<ChatMenuOptionData> options = [];
|
||||||
//
|
|
||||||
// //var hasCheats = AdminManager.PlayerHasPermissions(admin, "@css/cheats");
|
//var hasCheats = AdminManager.PlayerHasPermissions(admin, "@css/cheats");
|
||||||
// //var hasSlay = AdminManager.PlayerHasPermissions(admin, "@css/slay");
|
//var hasSlay = AdminManager.PlayerHasPermissions(admin, "@css/slay");
|
||||||
//
|
|
||||||
// // options added in order
|
// options added in order
|
||||||
//
|
|
||||||
// if (AdminManager.CommandIsOverriden("css_god")
|
if (AdminManager.CommandIsOverriden("css_god")
|
||||||
// ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_god"))
|
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_god"))
|
||||||
// : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/cheats"))
|
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/cheats"))
|
||||||
// options.Add(new ChatMenuOptionData(localizer?["sa_godmode"] ?? "God Mode", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_godmode"] ?? "God Mode", GodMode)));
|
options.Add(new ChatMenuOptionData(localizer?["sa_godmode"] ?? "God Mode", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_godmode"] ?? "God Mode", GodMode)));
|
||||||
// if (AdminManager.CommandIsOverriden("css_noclip")
|
if (AdminManager.CommandIsOverriden("css_noclip")
|
||||||
// ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_noclip"))
|
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_noclip"))
|
||||||
// : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/cheats"))
|
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/cheats"))
|
||||||
// options.Add(new ChatMenuOptionData(localizer?["sa_noclip"] ?? "No Clip", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_noclip"] ?? "No Clip", NoClip)));
|
options.Add(new ChatMenuOptionData(localizer?["sa_noclip"] ?? "No Clip", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_noclip"] ?? "No Clip", NoClip)));
|
||||||
// if (AdminManager.CommandIsOverriden("css_respawn")
|
if (AdminManager.CommandIsOverriden("css_respawn")
|
||||||
// ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_respawn"))
|
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_respawn"))
|
||||||
// : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/cheats"))
|
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/cheats"))
|
||||||
// options.Add(new ChatMenuOptionData(localizer?["sa_respawn"] ?? "Respawn", () => PlayersMenu.OpenDeadMenu(admin, localizer?["sa_respawn"] ?? "Respawn", Respawn)));
|
options.Add(new ChatMenuOptionData(localizer?["sa_respawn"] ?? "Respawn", () => PlayersMenu.OpenDeadMenu(admin, localizer?["sa_respawn"] ?? "Respawn", Respawn)));
|
||||||
// if (AdminManager.CommandIsOverriden("css_give")
|
if (AdminManager.CommandIsOverriden("css_give")
|
||||||
// ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_give"))
|
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_give"))
|
||||||
// : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/cheats"))
|
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/cheats"))
|
||||||
// options.Add(new ChatMenuOptionData(localizer?["sa_give_weapon"] ?? "Give Weapon", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_give_weapon"] ?? "Give Weapon", GiveWeaponMenu)));
|
options.Add(new ChatMenuOptionData(localizer?["sa_give_weapon"] ?? "Give Weapon", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_give_weapon"] ?? "Give Weapon", GiveWeaponMenu)));
|
||||||
//
|
|
||||||
// if (AdminManager.CommandIsOverriden("css_strip")
|
if (AdminManager.CommandIsOverriden("css_strip")
|
||||||
// ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_strip"))
|
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_strip"))
|
||||||
// : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
||||||
// options.Add(new ChatMenuOptionData(localizer?["sa_strip_weapons"] ?? "Strip Weapons", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_strip_weapons"] ?? "Strip Weapons", StripWeapons)));
|
options.Add(new ChatMenuOptionData(localizer?["sa_strip_weapons"] ?? "Strip Weapons", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_strip_weapons"] ?? "Strip Weapons", StripWeapons)));
|
||||||
// if (AdminManager.CommandIsOverriden("css_freeze")
|
if (AdminManager.CommandIsOverriden("css_freeze")
|
||||||
// ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_freeze"))
|
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_freeze"))
|
||||||
// : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
||||||
// options.Add(new ChatMenuOptionData(localizer?["sa_freeze"] ?? "Freeze", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_freeze"] ?? "Freeze", Freeze)));
|
options.Add(new ChatMenuOptionData(localizer?["sa_freeze"] ?? "Freeze", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_freeze"] ?? "Freeze", Freeze)));
|
||||||
// if (AdminManager.CommandIsOverriden("css_hp")
|
if (AdminManager.CommandIsOverriden("css_hp")
|
||||||
// ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_hp"))
|
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_hp"))
|
||||||
// : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
||||||
// options.Add(new ChatMenuOptionData(localizer?["sa_set_hp"] ?? "Set Hp", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_set_hp"] ?? "Set Hp", SetHpMenu)));
|
options.Add(new ChatMenuOptionData(localizer?["sa_set_hp"] ?? "Set Hp", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_set_hp"] ?? "Set Hp", SetHpMenu)));
|
||||||
// if (AdminManager.CommandIsOverriden("css_speed")
|
if (AdminManager.CommandIsOverriden("css_speed")
|
||||||
// ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_speed"))
|
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_speed"))
|
||||||
// : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
||||||
// options.Add(new ChatMenuOptionData(localizer?["sa_set_speed"] ?? "Set Speed", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_set_speed"] ?? "Set Speed", SetSpeedMenu)));
|
options.Add(new ChatMenuOptionData(localizer?["sa_set_speed"] ?? "Set Speed", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_set_speed"] ?? "Set Speed", SetSpeedMenu)));
|
||||||
// if (AdminManager.CommandIsOverriden("css_gravity")
|
if (AdminManager.CommandIsOverriden("css_gravity")
|
||||||
// ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_gravity"))
|
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_gravity"))
|
||||||
// : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
||||||
// options.Add(new ChatMenuOptionData(localizer?["sa_set_gravity"] ?? "Set Gravity", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_set_gravity"] ?? "Set Gravity", SetGravityMenu)));
|
options.Add(new ChatMenuOptionData(localizer?["sa_set_gravity"] ?? "Set Gravity", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_set_gravity"] ?? "Set Gravity", SetGravityMenu)));
|
||||||
// if (AdminManager.CommandIsOverriden("css_money")
|
if (AdminManager.CommandIsOverriden("css_money")
|
||||||
// ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_money"))
|
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_money"))
|
||||||
// : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
||||||
// options.Add(new ChatMenuOptionData(localizer?["sa_set_money"] ?? "Set Money", () => PlayersMenu.OpenMenu(admin, localizer?["sa_set_money"] ?? "Set Money", SetMoneyMenu)));
|
options.Add(new ChatMenuOptionData(localizer?["sa_set_money"] ?? "Set Money", () => PlayersMenu.OpenMenu(admin, localizer?["sa_set_money"] ?? "Set Money", SetMoneyMenu)));
|
||||||
//
|
|
||||||
// foreach (var menuOptionData in options)
|
foreach (var menuOptionData in options)
|
||||||
// {
|
{
|
||||||
// var menuName = menuOptionData.Name;
|
var menuName = menuOptionData.Name;
|
||||||
// menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action(); }, menuOptionData.Disabled);
|
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action(); }, menuOptionData.Disabled);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private static void GodMode(CCSPlayerController admin, CCSPlayerController player)
|
private static void GodMode(CCSPlayerController admin, CCSPlayerController player)
|
||||||
// {
|
{
|
||||||
// CS2_SimpleAdmin.God(admin, player);
|
CS2_SimpleAdmin.God(admin, player);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private static void NoClip(CCSPlayerController admin, CCSPlayerController player)
|
private static void NoClip(CCSPlayerController admin, CCSPlayerController player)
|
||||||
// {
|
{
|
||||||
// CS2_SimpleAdmin.NoClip(admin, player);
|
CS2_SimpleAdmin.NoClip(admin, player);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private static void Respawn(CCSPlayerController? admin, CCSPlayerController player)
|
private static void Respawn(CCSPlayerController? admin, CCSPlayerController player)
|
||||||
// {
|
{
|
||||||
// CS2_SimpleAdmin.Respawn(admin, player);
|
CS2_SimpleAdmin.Respawn(admin, player);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private static void GiveWeaponMenu(CCSPlayerController admin, CCSPlayerController player)
|
private static void GiveWeaponMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||||
// {
|
{
|
||||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_give_weapon"] ?? "Give Weapon"}: {player.PlayerName}");
|
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_give_weapon"] ?? "Give Weapon"}: {player.PlayerName}");
|
||||||
//
|
|
||||||
// foreach (var weapon in GetWeaponsCache)
|
foreach (var weapon in GetWeaponsCache)
|
||||||
// {
|
{
|
||||||
// menu?.AddMenuOption(weapon.Value.ToString(), (_, _) => { GiveWeapon(admin, player, weapon.Value); });
|
menu?.AddMenuOption(weapon.Value.ToString(), (_, _) => { GiveWeapon(admin, player, weapon.Value); });
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private static void GiveWeapon(CCSPlayerController admin, CCSPlayerController player, CsItem weaponValue)
|
private static void GiveWeapon(CCSPlayerController admin, CCSPlayerController player, CsItem weaponValue)
|
||||||
// {
|
{
|
||||||
// CS2_SimpleAdmin.GiveWeapon(admin, player, weaponValue);
|
CS2_SimpleAdmin.GiveWeapon(admin, player, weaponValue);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private static void StripWeapons(CCSPlayerController admin, CCSPlayerController player)
|
private static void StripWeapons(CCSPlayerController admin, CCSPlayerController player)
|
||||||
// {
|
{
|
||||||
// CS2_SimpleAdmin.StripWeapons(admin, player);
|
CS2_SimpleAdmin.StripWeapons(admin, player);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private static void Freeze(CCSPlayerController admin, CCSPlayerController player)
|
private static void Freeze(CCSPlayerController admin, CCSPlayerController player)
|
||||||
// {
|
{
|
||||||
// if (!(player.PlayerPawn.Value?.IsValid ?? false))
|
if (!(player.PlayerPawn.Value?.IsValid ?? false))
|
||||||
// return;
|
return;
|
||||||
//
|
|
||||||
// if (player.PlayerPawn.Value.MoveType != MoveType_t.MOVETYPE_INVALID)
|
if (player.PlayerPawn.Value.MoveType != MoveType_t.MOVETYPE_INVALID)
|
||||||
// CS2_SimpleAdmin.Freeze(admin, player, -1);
|
CS2_SimpleAdmin.Freeze(admin, player, -1);
|
||||||
// else
|
else
|
||||||
// CS2_SimpleAdmin.Unfreeze(admin, player);
|
CS2_SimpleAdmin.Unfreeze(admin, player);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private static void SetHpMenu(CCSPlayerController admin, CCSPlayerController player)
|
private static void SetHpMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||||
// {
|
{
|
||||||
// var hpArray = new[]
|
var hpArray = new[]
|
||||||
// {
|
{
|
||||||
// new Tuple<string, int>("1", 1),
|
new Tuple<string, int>("1", 1),
|
||||||
// new Tuple<string, int>("10", 10),
|
new Tuple<string, int>("10", 10),
|
||||||
// new Tuple<string, int>("25", 25),
|
new Tuple<string, int>("25", 25),
|
||||||
// new Tuple<string, int>("50", 50),
|
new Tuple<string, int>("50", 50),
|
||||||
// new Tuple<string, int>("100", 100),
|
new Tuple<string, int>("100", 100),
|
||||||
// new Tuple<string, int>("200", 200),
|
new Tuple<string, int>("200", 200),
|
||||||
// new Tuple<string, int>("500", 500),
|
new Tuple<string, int>("500", 500),
|
||||||
// new Tuple<string, int>("999", 999)
|
new Tuple<string, int>("999", 999)
|
||||||
// };
|
};
|
||||||
//
|
|
||||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_set_hp"] ?? "Set Hp"}: {player.PlayerName}");
|
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_set_hp"] ?? "Set Hp"}: {player.PlayerName}");
|
||||||
//
|
|
||||||
// foreach (var (optionName, value) in hpArray)
|
foreach (var (optionName, value) in hpArray)
|
||||||
// {
|
{
|
||||||
// menu?.AddMenuOption(optionName, (_, _) => { SetHp(admin, player, value); });
|
menu?.AddMenuOption(optionName, (_, _) => { SetHp(admin, player, value); });
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private static void SetHp(CCSPlayerController admin, CCSPlayerController player, int hp)
|
private static void SetHp(CCSPlayerController admin, CCSPlayerController player, int hp)
|
||||||
// {
|
{
|
||||||
// CS2_SimpleAdmin.SetHp(admin, player, hp);
|
CS2_SimpleAdmin.SetHp(admin, player, hp);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private static void SetSpeedMenu(CCSPlayerController admin, CCSPlayerController player)
|
private static void SetSpeedMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||||
// {
|
{
|
||||||
// var speedArray = new[]
|
var speedArray = new[]
|
||||||
// {
|
{
|
||||||
// new Tuple<string, float>("0.1", .1f),
|
new Tuple<string, float>("0.1", .1f),
|
||||||
// new Tuple<string, float>("0.25", .25f),
|
new Tuple<string, float>("0.25", .25f),
|
||||||
// new Tuple<string, float>("0.5", .5f),
|
new Tuple<string, float>("0.5", .5f),
|
||||||
// new Tuple<string, float>("0.75", .75f),
|
new Tuple<string, float>("0.75", .75f),
|
||||||
// new Tuple<string, float>("1", 1),
|
new Tuple<string, float>("1", 1),
|
||||||
// new Tuple<string, float>("2", 2),
|
new Tuple<string, float>("2", 2),
|
||||||
// new Tuple<string, float>("3", 3),
|
new Tuple<string, float>("3", 3),
|
||||||
// new Tuple<string, float>("4", 4)
|
new Tuple<string, float>("4", 4)
|
||||||
// };
|
};
|
||||||
//
|
|
||||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_set_speed"] ?? "Set Speed"}: {player.PlayerName}");
|
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_set_speed"] ?? "Set Speed"}: {player.PlayerName}");
|
||||||
//
|
|
||||||
// foreach (var (optionName, value) in speedArray)
|
foreach (var (optionName, value) in speedArray)
|
||||||
// {
|
{
|
||||||
// menu?.AddMenuOption(optionName, (_, _) => { SetSpeed(admin, player, value); });
|
menu?.AddMenuOption(optionName, (_, _) => { SetSpeed(admin, player, value); });
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private static void SetSpeed(CCSPlayerController admin, CCSPlayerController player, float speed)
|
private static void SetSpeed(CCSPlayerController admin, CCSPlayerController player, float speed)
|
||||||
// {
|
{
|
||||||
// CS2_SimpleAdmin.SetSpeed(admin, player, speed);
|
CS2_SimpleAdmin.SetSpeed(admin, player, speed);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private static void SetGravityMenu(CCSPlayerController admin, CCSPlayerController player)
|
private static void SetGravityMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||||
// {
|
{
|
||||||
// var gravityArray = new[]
|
var gravityArray = new[]
|
||||||
// {
|
{
|
||||||
// new Tuple<string, float>("0.1", .1f),
|
new Tuple<string, float>("0.1", .1f),
|
||||||
// new Tuple<string, float>("0.25", .25f),
|
new Tuple<string, float>("0.25", .25f),
|
||||||
// new Tuple<string, float>("0.5", .5f),
|
new Tuple<string, float>("0.5", .5f),
|
||||||
// new Tuple<string, float>("0.75", .75f),
|
new Tuple<string, float>("0.75", .75f),
|
||||||
// new Tuple<string, float>("1", 1),
|
new Tuple<string, float>("1", 1),
|
||||||
// new Tuple<string, float>("2", 2)
|
new Tuple<string, float>("2", 2)
|
||||||
// };
|
};
|
||||||
//
|
|
||||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_set_gravity"] ?? "Set Gravity"}: {player.PlayerName}");
|
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_set_gravity"] ?? "Set Gravity"}: {player.PlayerName}");
|
||||||
//
|
|
||||||
// foreach (var (optionName, value) in gravityArray)
|
foreach (var (optionName, value) in gravityArray)
|
||||||
// {
|
{
|
||||||
// menu?.AddMenuOption(optionName, (_, _) => { SetGravity(admin, player, value); });
|
menu?.AddMenuOption(optionName, (_, _) => { SetGravity(admin, player, value); });
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private static void SetGravity(CCSPlayerController admin, CCSPlayerController player, float gravity)
|
private static void SetGravity(CCSPlayerController admin, CCSPlayerController player, float gravity)
|
||||||
// {
|
{
|
||||||
// CS2_SimpleAdmin.SetGravity(admin, player, gravity);
|
CS2_SimpleAdmin.SetGravity(admin, player, gravity);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private static void SetMoneyMenu(CCSPlayerController admin, CCSPlayerController player)
|
private static void SetMoneyMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||||
// {
|
{
|
||||||
// var moneyArray = new[]
|
var moneyArray = new[]
|
||||||
// {
|
{
|
||||||
// new Tuple<string, int>("$0", 0),
|
new Tuple<string, int>("$0", 0),
|
||||||
// new Tuple<string, int>("$1000", 1000),
|
new Tuple<string, int>("$1000", 1000),
|
||||||
// new Tuple<string, int>("$2500", 2500),
|
new Tuple<string, int>("$2500", 2500),
|
||||||
// new Tuple<string, int>("$5000", 5000),
|
new Tuple<string, int>("$5000", 5000),
|
||||||
// new Tuple<string, int>("$10000", 10000),
|
new Tuple<string, int>("$10000", 10000),
|
||||||
// new Tuple<string, int>("$16000", 16000)
|
new Tuple<string, int>("$16000", 16000)
|
||||||
// };
|
};
|
||||||
//
|
|
||||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_set_money"] ?? "Set Money"}: {player.PlayerName}");
|
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_set_money"] ?? "Set Money"}: {player.PlayerName}");
|
||||||
//
|
|
||||||
// foreach (var (optionName, value) in moneyArray)
|
foreach (var (optionName, value) in moneyArray)
|
||||||
// {
|
{
|
||||||
// menu?.AddMenuOption(optionName, (_, _) => { SetMoney(admin, player, value); });
|
menu?.AddMenuOption(optionName, (_, _) => { SetMoney(admin, player, value); });
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private static void SetMoney(CCSPlayerController admin, CCSPlayerController player, int money)
|
private static void SetMoney(CCSPlayerController admin, CCSPlayerController player, int money)
|
||||||
// {
|
{
|
||||||
// CS2_SimpleAdmin.SetMoney(admin, player, money);
|
CS2_SimpleAdmin.SetMoney(admin, player, money);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
@@ -8,11 +8,11 @@ public static class ManageAdminsMenu
|
|||||||
{
|
{
|
||||||
public static void OpenMenu(CCSPlayerController admin)
|
public static void OpenMenu(CCSPlayerController admin)
|
||||||
{
|
{
|
||||||
if (!admin.IsValid)
|
if (admin.IsValid == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
var localizer = CS2_SimpleAdmin._localizer;
|
||||||
if (!AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/root"))
|
if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/root") == false)
|
||||||
{
|
{
|
||||||
admin.PrintToChat(localizer?["sa_prefix"] ??
|
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||||
"[SimpleAdmin] " +
|
"[SimpleAdmin] " +
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ public static class ManagePlayersMenu
|
|||||||
{
|
{
|
||||||
public static void OpenMenu(CCSPlayerController admin)
|
public static void OpenMenu(CCSPlayerController admin)
|
||||||
{
|
{
|
||||||
if (!admin.IsValid)
|
if (admin.IsValid == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
var localizer = CS2_SimpleAdmin._localizer;
|
||||||
if (!AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic"))
|
if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic") == false)
|
||||||
{
|
{
|
||||||
admin.PrintToChat(localizer?["sa_prefix"] ??
|
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||||
"[SimpleAdmin] " +
|
"[SimpleAdmin] " +
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ public static class ManageServerMenu
|
|||||||
{
|
{
|
||||||
public static void OpenMenu(CCSPlayerController admin)
|
public static void OpenMenu(CCSPlayerController admin)
|
||||||
{
|
{
|
||||||
if (!admin.IsValid)
|
if (admin.IsValid == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
var localizer = CS2_SimpleAdmin._localizer;
|
||||||
if (!AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic"))
|
if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic") == false)
|
||||||
{
|
{
|
||||||
admin.PrintToChat(localizer?["sa_prefix"] ??
|
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||||
"[SimpleAdmin] " +
|
"[SimpleAdmin] " +
|
||||||
|
|||||||
@@ -1,170 +0,0 @@
|
|||||||
using CounterStrikeSharp.API.Core;
|
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin.Menus;
|
|
||||||
public class MenuBuilder(string title)
|
|
||||||
{
|
|
||||||
private readonly List<MenuOption> _options = [];
|
|
||||||
private MenuBuilder? _parentMenu;
|
|
||||||
private Action<CCSPlayerController>? _backAction;
|
|
||||||
private Action<CCSPlayerController>? _resetAction;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a menu option with an action.
|
|
||||||
/// </summary>
|
|
||||||
public MenuBuilder AddOption(string name, Action<CCSPlayerController> action, bool disabled = false, string? permission = null)
|
|
||||||
{
|
|
||||||
_options.Add(new MenuOption
|
|
||||||
{
|
|
||||||
Name = name,
|
|
||||||
Action = action,
|
|
||||||
Disabled = disabled,
|
|
||||||
Permission = permission
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a menu option that opens a submenu (with player parameter in factory).
|
|
||||||
/// </summary>
|
|
||||||
public MenuBuilder AddSubMenu(string name, Func<CCSPlayerController, MenuBuilder> subMenuFactory, bool disabled = false, string? permission = null)
|
|
||||||
{
|
|
||||||
_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);
|
|
||||||
},
|
|
||||||
Disabled = disabled,
|
|
||||||
Permission = permission
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a back button to return to the previous menu.
|
|
||||||
/// </summary>
|
|
||||||
public MenuBuilder WithBackButton()
|
|
||||||
{
|
|
||||||
if (_parentMenu != null)
|
|
||||||
{
|
|
||||||
_backAction = player => _parentMenu.OpenMenu(player);
|
|
||||||
|
|
||||||
// Add back option at the end of menu
|
|
||||||
// AddOption(backButtonText, _backAction);
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
|
|
||||||
// Use MenuManager dependency
|
|
||||||
var menu = Helper.CreateMenu(title, _backAction);
|
|
||||||
if (menu == null) return;
|
|
||||||
|
|
||||||
foreach (var option in _options)
|
|
||||||
{
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
menu.AddMenuOption(option.Name, (menuPlayer, menuOption) =>
|
|
||||||
{
|
|
||||||
option.Action?.Invoke(menuPlayer);
|
|
||||||
}, option.Disabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
menu.Open(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clears all menu options.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>This MenuBuilder instance for chaining.</returns>
|
|
||||||
public MenuBuilder Clear()
|
|
||||||
{
|
|
||||||
_options.Clear();
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
_resetAction = resetAction;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets a custom back action for the 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)
|
|
||||||
{
|
|
||||||
_backAction = backAction;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents an option within a menu.
|
|
||||||
/// </summary>
|
|
||||||
public class MenuOption
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public Action<CCSPlayerController>? Action { get; set; }
|
|
||||||
public bool Disabled { get; set; }
|
|
||||||
public string? Permission { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
using CounterStrikeSharp.API.Core;
|
|
||||||
using CounterStrikeSharp.API.Modules.Admin;
|
|
||||||
using CounterStrikeSharp.API.Modules.Entities;
|
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin.Menus;
|
|
||||||
|
|
||||||
public class MenuManager
|
|
||||||
{
|
|
||||||
private static MenuManager? _instance;
|
|
||||||
public static MenuManager Instance => _instance ??= new MenuManager();
|
|
||||||
|
|
||||||
private readonly Dictionary<string, Func<CCSPlayerController, MenuBuilder>> _menuFactories = [];
|
|
||||||
private readonly Dictionary<string, MenuCategory> _menuCategories = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides public access to menu categories (for API usage).
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Dictionary of menu categories keyed by category ID.</returns>
|
|
||||||
public Dictionary<string, MenuCategory> GetMenuCategories()
|
|
||||||
{
|
|
||||||
return _menuCategories;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers a new menu category with specified permissions.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="categoryId">Unique identifier for the category.</param>
|
|
||||||
/// <param name="categoryName">Display name of the category.</param>
|
|
||||||
/// <param name="permission">Required permission to access this category (default: @css/generic).</param>
|
|
||||||
public void RegisterCategory(string categoryId, string categoryName, string permission = "@css/generic")
|
|
||||||
{
|
|
||||||
_menuCategories[categoryId] = new MenuCategory
|
|
||||||
{
|
|
||||||
Id = categoryId,
|
|
||||||
Name = categoryName,
|
|
||||||
Permission = permission,
|
|
||||||
MenuFactories = new Dictionary<string, Func<CCSPlayerController, MenuBuilder>>()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers a menu within a category (API for other plugins).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="categoryId">The category to add this menu to.</param>
|
|
||||||
/// <param name="menuId">Unique identifier for the menu.</param>
|
|
||||||
/// <param name="menuName">Display name of the menu.</param>
|
|
||||||
/// <param name="menuFactory">Factory function that creates the menu for a player.</param>
|
|
||||||
/// <param name="permission">Required permission to access this menu (optional).</param>
|
|
||||||
public void RegisterMenu(string categoryId, string menuId, string menuName, Func<CCSPlayerController, MenuBuilder> menuFactory, string? permission = null)
|
|
||||||
{
|
|
||||||
if (!_menuCategories.ContainsKey(categoryId))
|
|
||||||
{
|
|
||||||
RegisterCategory(categoryId, categoryId); // Auto-create category if it doesn't exist
|
|
||||||
}
|
|
||||||
|
|
||||||
_menuCategories[categoryId].MenuFactories[menuId] = menuFactory;
|
|
||||||
_menuCategories[categoryId].MenuNames[menuId] = menuName;
|
|
||||||
if (permission != null)
|
|
||||||
{
|
|
||||||
_menuCategories[categoryId].MenuPermissions[menuId] = permission;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unregisters a menu from a category.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="categoryId">The category containing the menu.</param>
|
|
||||||
/// <param name="menuId">The menu to unregister.</param>
|
|
||||||
public void UnregisterMenu(string categoryId, string menuId)
|
|
||||||
{
|
|
||||||
if (!_menuCategories.TryGetValue(categoryId, out var category)) return;
|
|
||||||
category.MenuFactories.Remove(menuId);
|
|
||||||
_menuCategories[categoryId].MenuNames.Remove(menuId);
|
|
||||||
_menuCategories[categoryId].MenuPermissions.Remove(menuId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates the main admin menu for a player with accessible categories.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="player">The player to create the menu for.</param>
|
|
||||||
/// <returns>A MenuBuilder instance for the main menu.</returns>
|
|
||||||
public MenuBuilder CreateMainMenu(CCSPlayerController player)
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
var mainMenu = new MenuBuilder(localizer?["sa_title"] ?? "SimpleAdmin");
|
|
||||||
|
|
||||||
foreach (var 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))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Pass player to CreateCategoryMenu
|
|
||||||
mainMenu.AddSubMenu(category.Name, () => CreateCategoryMenu(category, player),
|
|
||||||
permission: category.Permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mainMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a category submenu containing all registered menus in that category.
|
|
||||||
/// </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>
|
|
||||||
private MenuBuilder CreateCategoryMenu(MenuCategory category, CCSPlayerController player)
|
|
||||||
{
|
|
||||||
var categoryMenu = new MenuBuilder(category.Name);
|
|
||||||
|
|
||||||
foreach (var 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;
|
|
||||||
|
|
||||||
// Check permissions
|
|
||||||
if (!string.IsNullOrEmpty(permission))
|
|
||||||
{
|
|
||||||
var steamId = new SteamID(player.SteamID);
|
|
||||||
if (!AdminManager.PlayerHasPermissions(steamId, permission))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call the actual factory with player parameter
|
|
||||||
categoryMenu.AddSubMenu(menuName, () => menuFactory(player), permission: permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
return categoryMenu.WithBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Opens the main admin menu for a player.
|
|
||||||
/// </summary>
|
|
||||||
/// <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);
|
|
||||||
if (!AdminManager.PlayerHasPermissions(steamId, "@css/generic"))
|
|
||||||
{
|
|
||||||
player.PrintToChat(localizer?["sa_prefix"] ?? "[SimpleAdmin] " +
|
|
||||||
(localizer?["sa_no_permission"] ?? "You do not have permissions to use this command"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateMainMenu(player).OpenMenu(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes default menu categories (Players, Server, Admin).
|
|
||||||
/// </summary>
|
|
||||||
public void InitializeDefaultCategories()
|
|
||||||
{
|
|
||||||
var localizer = CS2_SimpleAdmin._localizer;
|
|
||||||
|
|
||||||
RegisterCategory("players", localizer?["sa_menu_players_manage"] ?? "Manage Players", "@css/generic");
|
|
||||||
RegisterCategory("server", localizer?["sa_menu_server_manage"] ?? "Server Management", "@css/generic");
|
|
||||||
// RegisterCategory("fun", localizer?["sa_menu_fun_commands"] ?? "Fun Commands", "@css/generic");
|
|
||||||
RegisterCategory("admin", localizer?["sa_menu_admins_manage"] ?? "Admin Management", "@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>
|
|
||||||
/// Represents a menu category containing multiple menus.
|
|
||||||
/// </summary>
|
|
||||||
public class MenuCategory
|
|
||||||
{
|
|
||||||
public string Id { get; set; } = string.Empty;
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public string Permission { get; set; } = "@css/generic";
|
|
||||||
public Dictionary<string, Func<CCSPlayerController, MenuBuilder>> MenuFactories { get; set; } = [];
|
|
||||||
public Dictionary<string, string> MenuNames { get; set; } = [];
|
|
||||||
public Dictionary<string, string> MenuPermissions { get; set; } = [];
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@ public static class PlayersMenu
|
|||||||
{
|
{
|
||||||
public static void OpenRealPlayersMenu(CCSPlayerController admin, string menuName, Action<CCSPlayerController, CCSPlayerController> onSelectAction, Func<CCSPlayerController, bool>? enableFilter = null)
|
public static void OpenRealPlayersMenu(CCSPlayerController admin, string menuName, Action<CCSPlayerController, CCSPlayerController> onSelectAction, Func<CCSPlayerController, bool>? enableFilter = null)
|
||||||
{
|
{
|
||||||
OpenMenu(admin, menuName, onSelectAction, p => !p.IsBot);
|
OpenMenu(admin, menuName, onSelectAction, p => p.IsBot == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OpenAdminPlayersMenu(CCSPlayerController admin, string menuName, Action<CCSPlayerController, CCSPlayerController> onSelectAction, Func<CCSPlayerController?, bool>? enableFilter = null)
|
public static void OpenAdminPlayersMenu(CCSPlayerController admin, string menuName, Action<CCSPlayerController, CCSPlayerController> onSelectAction, Func<CCSPlayerController?, bool>? enableFilter = null)
|
||||||
@@ -37,7 +37,7 @@ public static class PlayersMenu
|
|||||||
var playerName = player != null && player.PlayerName.Length > 26 ? player.PlayerName[..26] : player?.PlayerName;
|
var playerName = player != null && player.PlayerName.Length > 26 ? player.PlayerName[..26] : player?.PlayerName;
|
||||||
|
|
||||||
var optionName = HttpUtility.HtmlEncode(playerName);
|
var optionName = HttpUtility.HtmlEncode(playerName);
|
||||||
if (player != null && enableFilter != null && !enableFilter(player))
|
if (player != null && enableFilter != null && enableFilter(player) == false)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var enabled = admin.CanTarget(player);
|
var enabled = admin.CanTarget(player);
|
||||||
@@ -47,7 +47,7 @@ public static class PlayersMenu
|
|||||||
{
|
{
|
||||||
if (player != null) onSelectAction.Invoke(admin, player);
|
if (player != null) onSelectAction.Invoke(admin, player);
|
||||||
},
|
},
|
||||||
!enabled);
|
enabled == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
1.7.8-beta-1
|
1.7.7-alpha-10
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Core.Capabilities;
|
using CounterStrikeSharp.API.Core.Capabilities;
|
||||||
using CounterStrikeSharp.API.Modules.Commands;
|
using CounterStrikeSharp.API.Modules.Commands;
|
||||||
using CounterStrikeSharp.API.Modules.Commands.Targeting;
|
|
||||||
using CounterStrikeSharp.API.Modules.Entities;
|
using CounterStrikeSharp.API.Modules.Entities;
|
||||||
|
|
||||||
namespace CS2_SimpleAdminApi;
|
namespace CS2_SimpleAdminApi;
|
||||||
@@ -9,8 +8,6 @@ namespace CS2_SimpleAdminApi;
|
|||||||
public interface ICS2_SimpleAdminApi
|
public interface ICS2_SimpleAdminApi
|
||||||
{
|
{
|
||||||
public static readonly PluginCapability<ICS2_SimpleAdminApi?> PluginCapability = new("simpleadmin:api");
|
public static readonly PluginCapability<ICS2_SimpleAdminApi?> PluginCapability = new("simpleadmin:api");
|
||||||
|
|
||||||
public event Action? OnSimpleAdminReady;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets player information associated with the specified player controller.
|
/// Gets player information associated with the specified player controller.
|
||||||
@@ -86,25 +83,6 @@ public interface ICS2_SimpleAdminApi
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void ShowAdminActivity(string messageKey, string? callerName = null, bool dontPublish = false, params object[] messageArgs);
|
public void ShowAdminActivity(string messageKey, string? callerName = null, bool dontPublish = false, params object[] messageArgs);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Shows an admin activity message with a custom translated message (for modules with their own localizer).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="translatedMessage">Already translated message to display to players.</param>
|
|
||||||
/// <param name="callerName">Name of the admin executing the action (optional).</param>
|
|
||||||
/// <param name="dontPublish">If true, won't trigger publish events.</param>
|
|
||||||
public void ShowAdminActivityTranslated(string translatedMessage, string? callerName = null, bool dontPublish = false);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Shows an admin activity message using module's localizer for per-player language support.
|
|
||||||
/// This method sends messages in each player's configured language.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="moduleLocalizer">The module's IStringLocalizer instance.</param>
|
|
||||||
/// <param name="messageKey">The translation key from the module's lang files.</param>
|
|
||||||
/// <param name="callerName">Name of the admin executing the action (optional).</param>
|
|
||||||
/// <param name="dontPublish">If true, won't trigger publish events.</param>
|
|
||||||
/// <param name="messageArgs">Arguments to format the localized message.</param>
|
|
||||||
public void ShowAdminActivityLocalized(object moduleLocalizer, string messageKey, string? callerName = null, bool dontPublish = false, params object[] messageArgs);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if the specified admin player is in silent mode (not broadcasting activity).
|
/// Returns true if the specified admin player is in silent mode (not broadcasting activity).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -124,54 +102,4 @@ public interface ICS2_SimpleAdminApi
|
|||||||
/// Unregisters an existing command by its name.
|
/// Unregisters an existing command by its name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void UnRegisterCommand(string name);
|
public void UnRegisterCommand(string name);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets target players from command
|
|
||||||
/// </summary>
|
|
||||||
TargetResult? GetTarget(CommandInfo command);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the list of current valid players, available to call from other plugins.
|
|
||||||
/// </summary>
|
|
||||||
List<CCSPlayerController> GetValidPlayers();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers a menu category.
|
|
||||||
/// </summary>
|
|
||||||
void RegisterMenuCategory(string categoryId, string categoryName, string permission = "@css/generic");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers a menu in a category.
|
|
||||||
/// </summary>
|
|
||||||
void RegisterMenu(string categoryId, string menuId, string menuName, Func<CCSPlayerController, object> menuFactory, string? permission = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unregisters a menu from a category.
|
|
||||||
/// </summary>
|
|
||||||
void UnregisterMenu(string categoryId, string menuId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a menu with an automatic back button.
|
|
||||||
/// </summary>
|
|
||||||
object CreateMenuWithBack(string title, string categoryId, CCSPlayerController player);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a menu with a list of players with filter and action.
|
|
||||||
/// </summary>
|
|
||||||
object CreateMenuWithPlayers(string title, string categoryId, CCSPlayerController admin, Func<CCSPlayerController, bool> filter, Action<CCSPlayerController, CCSPlayerController> onSelect);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds an option to the menu (extension method helper).
|
|
||||||
/// </summary>
|
|
||||||
void AddMenuOption(object menu, string name, Action<CCSPlayerController> action, bool disabled = false, string? permission = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a submenu to the menu (extension method helper).
|
|
||||||
/// </summary>
|
|
||||||
void AddSubMenu(object menu, string name, Func<CCSPlayerController, object> subMenuFactory, bool disabled = false, string? permission = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Opens a menu for a player.
|
|
||||||
/// </summary>
|
|
||||||
void OpenMenu(object menu, CCSPlayerController player);
|
|
||||||
}
|
}
|
||||||
Binary file not shown.
@@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CS2-SimpleAdmin_FunCommands", "CS2-SimpleAdmin_FunCommands\CS2-SimpleAdmin_FunCommands.csproj", "{72713A40-688F-401F-8211-3D28B068C791}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{72713A40-688F-401F-8211-3D28B068C791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{72713A40-688F-401F-8211-3D28B068C791}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{72713A40-688F-401F-8211-3D28B068C791}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{72713A40-688F-401F-8211-3D28B068C791}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,237 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using CounterStrikeSharp.API;
|
|
||||||
using CounterStrikeSharp.API.Core;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin_FunCommands;
|
|
||||||
|
|
||||||
public partial class CS2_SimpleAdmin_FunCommands
|
|
||||||
{
|
|
||||||
private void God(CCSPlayerController? caller, CCSPlayerController player)
|
|
||||||
{
|
|
||||||
if (!caller.CanTarget(player)) return;
|
|
||||||
|
|
||||||
var callerName = caller?.PlayerName ?? "Console";
|
|
||||||
|
|
||||||
// Toggle god mode for the player (like in main plugin)
|
|
||||||
if (!GodPlayers.Add(player.Slot))
|
|
||||||
{
|
|
||||||
GodPlayers.Remove(player.Slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show admin activity using module's own localizer with per-player language support
|
|
||||||
var activityArgs = new object[] { "CALLER", player.PlayerName };
|
|
||||||
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
|
|
||||||
{
|
|
||||||
if (Localizer != null)
|
|
||||||
{
|
|
||||||
_sharedApi!.ShowAdminActivityLocalized(Localizer, "fun_admin_god_message", callerName, false,
|
|
||||||
activityArgs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_sharedApi!.ShowAdminActivity("fun_admin_god_message", callerName, false, activityArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log command using API
|
|
||||||
_sharedApi!.LogCommand(caller,
|
|
||||||
$"css_god {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void NoClip(CCSPlayerController? caller, CCSPlayerController player)
|
|
||||||
{
|
|
||||||
if (!player.IsValid) return;
|
|
||||||
if (!caller.CanTarget(player)) return;
|
|
||||||
|
|
||||||
var callerName = caller?.PlayerName ?? "Console";
|
|
||||||
|
|
||||||
// Toggle no-clip mode using PlayerExtensions
|
|
||||||
player.Pawn.Value?.ToggleNoclip();
|
|
||||||
|
|
||||||
// Show admin activity using module's own localizer with per-player language support
|
|
||||||
var activityArgs = new object[] { "CALLER", player.PlayerName };
|
|
||||||
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
|
|
||||||
{
|
|
||||||
if (Localizer != null)
|
|
||||||
{
|
|
||||||
_sharedApi!.ShowAdminActivityLocalized(Localizer, "fun_admin_noclip_message", callerName, false,
|
|
||||||
activityArgs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_sharedApi!.ShowAdminActivity("fun_admin_noclip_message", callerName, false, activityArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log command using API
|
|
||||||
_sharedApi!.LogCommand(caller,
|
|
||||||
$"css_noclip {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Freeze(CCSPlayerController? caller, CCSPlayerController player, int time)
|
|
||||||
{
|
|
||||||
if (!player.IsValid) return;
|
|
||||||
if (!caller.CanTarget(player)) return;
|
|
||||||
|
|
||||||
var callerName = caller?.PlayerName ?? "Console";
|
|
||||||
|
|
||||||
// Freeze player using PlayerExtensions
|
|
||||||
player.Pawn.Value?.Freeze();
|
|
||||||
|
|
||||||
// Show admin activity using module's own localizer with per-player language support
|
|
||||||
var activityArgs = new object[] { "CALLER", player.PlayerName };
|
|
||||||
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
|
|
||||||
{
|
|
||||||
if (Localizer != null)
|
|
||||||
{
|
|
||||||
_sharedApi!.ShowAdminActivityLocalized(Localizer, "fun_admin_freeze_message", callerName, false,
|
|
||||||
activityArgs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_sharedApi!.ShowAdminActivity("fun_admin_freeze_message", callerName, false, activityArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-unfreeze after duration
|
|
||||||
if (time > 0)
|
|
||||||
{
|
|
||||||
AddTimer(time, () => player.Pawn.Value?.Unfreeze(),
|
|
||||||
CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log command using API
|
|
||||||
_sharedApi!.LogCommand(caller,
|
|
||||||
$"css_freeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Unfreeze(CCSPlayerController? caller, CCSPlayerController player)
|
|
||||||
{
|
|
||||||
if (!player.IsValid) return;
|
|
||||||
if (!caller.CanTarget(player)) return;
|
|
||||||
|
|
||||||
var callerName = caller?.PlayerName ?? "Console";
|
|
||||||
|
|
||||||
// Unfreeze player using PlayerExtensions
|
|
||||||
player.Pawn.Value?.Unfreeze();
|
|
||||||
|
|
||||||
// Show admin activity using module's own localizer with per-player language support
|
|
||||||
var activityArgs = new object[] { "CALLER", player.PlayerName };
|
|
||||||
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
|
|
||||||
{
|
|
||||||
if (Localizer != null)
|
|
||||||
{
|
|
||||||
_sharedApi!.ShowAdminActivityLocalized(Localizer, "fun_admin_unfreeze_message", callerName, false,
|
|
||||||
activityArgs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_sharedApi!.ShowAdminActivity("fun_admin_unfreeze_message", callerName, false, activityArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log command using API
|
|
||||||
_sharedApi!.LogCommand(caller,
|
|
||||||
$"css_unfreeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Respawns a player and teleports them to their last death position if available.
|
|
||||||
/// This demonstrates using the GetPlayerInfo API to access player data.
|
|
||||||
/// </summary>
|
|
||||||
private void Respawn(CCSPlayerController? caller, CCSPlayerController player)
|
|
||||||
{
|
|
||||||
if (!player.IsValid) return;
|
|
||||||
if (!caller.CanTarget(player)) return;
|
|
||||||
|
|
||||||
var callerName = caller?.PlayerName ?? "Console";
|
|
||||||
|
|
||||||
// Respawn the player
|
|
||||||
player.Respawn();
|
|
||||||
|
|
||||||
// Get death position from API and teleport player to it
|
|
||||||
// BEST PRACTICE: Use API to access player data like death position
|
|
||||||
if (_sharedApi != null && player.UserId.HasValue)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var playerInfo = _sharedApi.GetPlayerInfo(player);
|
|
||||||
|
|
||||||
// Teleport to death position if available
|
|
||||||
if (playerInfo?.DiePosition != null && player.PlayerPawn?.Value != null)
|
|
||||||
{
|
|
||||||
player.PlayerPawn.Value.Teleport(
|
|
||||||
playerInfo.DiePosition.Position,
|
|
||||||
playerInfo.DiePosition.Angle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogWarning($"Failed to get player info for respawn: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show admin activity using module's own localizer with per-player language support
|
|
||||||
var activityArgs = new object[] { "CALLER", player.PlayerName };
|
|
||||||
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
|
|
||||||
{
|
|
||||||
if (Localizer != null)
|
|
||||||
{
|
|
||||||
_sharedApi!.ShowAdminActivityLocalized(Localizer, "fun_admin_respawn_message", callerName, false,
|
|
||||||
activityArgs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_sharedApi!.ShowAdminActivity("fun_admin_respawn_message", callerName, false, activityArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log command using API
|
|
||||||
_sharedApi!.LogCommand(caller,
|
|
||||||
$"css_respawn {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resizes a player's model to the specified scale.
|
|
||||||
/// </summary>
|
|
||||||
private void Resize(CCSPlayerController? caller, CCSPlayerController player, float size)
|
|
||||||
{
|
|
||||||
if (!player.IsValid) return;
|
|
||||||
if (!caller.CanTarget(player)) return;
|
|
||||||
|
|
||||||
var callerName = caller?.PlayerName ?? "Console";
|
|
||||||
|
|
||||||
// Resize the player
|
|
||||||
var sceneNode = player.PlayerPawn.Value!.CBodyComponent?.SceneNode;
|
|
||||||
if (sceneNode != null)
|
|
||||||
{
|
|
||||||
sceneNode.GetSkeletonInstance().Scale = size;
|
|
||||||
player.PlayerPawn.Value.AcceptInput("SetScale", null, null, size.ToString(CultureInfo.InvariantCulture));
|
|
||||||
|
|
||||||
Server.NextWorldUpdate(() =>
|
|
||||||
{
|
|
||||||
Utilities.SetStateChanged(player.PlayerPawn.Value, "CBaseEntity", "m_CBodyComponent");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show admin activity using module's own localizer with per-player language support
|
|
||||||
var activityArgs = new object[] { "CALLER", player.PlayerName, size.ToString(CultureInfo.InvariantCulture) };
|
|
||||||
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
|
|
||||||
{
|
|
||||||
if (Localizer != null)
|
|
||||||
{
|
|
||||||
_sharedApi!.ShowAdminActivityLocalized(Localizer, "fun_admin_resize_message", callerName, false,
|
|
||||||
activityArgs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_sharedApi!.ShowAdminActivity("fun_admin_resize_message", callerName, false, activityArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log command using API
|
|
||||||
_sharedApi!.LogCommand(caller,
|
|
||||||
$"css_resize {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {size}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,492 +0,0 @@
|
|||||||
using CounterStrikeSharp.API;
|
|
||||||
using CounterStrikeSharp.API.Core;
|
|
||||||
using CounterStrikeSharp.API.Core.Capabilities;
|
|
||||||
using CounterStrikeSharp.API.Modules.Admin;
|
|
||||||
using CounterStrikeSharp.API.Modules.Commands;
|
|
||||||
using CounterStrikeSharp.API.Modules.Entities;
|
|
||||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
|
||||||
using CS2_SimpleAdminApi;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Globalization;
|
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin_FunCommands;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// CS2-SimpleAdmin Fun Commands Module
|
|
||||||
///
|
|
||||||
/// This module serves as a REFERENCE IMPLEMENTATION for creating CS2-SimpleAdmin modules.
|
|
||||||
/// Study this code to learn best practices for:
|
|
||||||
/// - Command registration from configuration
|
|
||||||
/// - Menu creation with SimpleAdmin API
|
|
||||||
/// - Per-player translation support
|
|
||||||
/// - Proper cleanup on module unload
|
|
||||||
/// - Code organization using partial classes
|
|
||||||
///
|
|
||||||
/// File Structure:
|
|
||||||
/// - CS2-SimpleAdmin_FunCommands.cs (this file) - Plugin initialization and registration
|
|
||||||
/// - Commands.cs - Command handlers
|
|
||||||
/// - Actions.cs - Action methods (God, NoClip, Freeze, etc.)
|
|
||||||
/// - Menus.cs - Menu creation
|
|
||||||
/// - Config.cs - Configuration with command lists
|
|
||||||
/// - lang/ - Translation files (13 languages)
|
|
||||||
///
|
|
||||||
/// See README.md for detailed explanation of all patterns demonstrated here.
|
|
||||||
/// </summary>
|
|
||||||
public partial class CS2_SimpleAdmin_FunCommands : BasePlugin, IPluginConfig<Config>
|
|
||||||
{
|
|
||||||
public Config Config { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// BEST PRACTICE: Cache expensive operations
|
|
||||||
/// Weapons enum values don't change, so we cache them on first access
|
|
||||||
/// </summary>
|
|
||||||
private static Dictionary<int, CsItem>? _weaponsCache;
|
|
||||||
private static Dictionary<int, CsItem> GetWeaponsCache()
|
|
||||||
{
|
|
||||||
if (_weaponsCache != null) return _weaponsCache;
|
|
||||||
|
|
||||||
var weaponsArray = Enum.GetValues(typeof(CsItem));
|
|
||||||
_weaponsCache = new Dictionary<int, CsItem>();
|
|
||||||
|
|
||||||
foreach (CsItem item in weaponsArray)
|
|
||||||
{
|
|
||||||
if (item == CsItem.Tablet) continue; // Skip tablet (invalid weapon)
|
|
||||||
_weaponsCache[(int)item] = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _weaponsCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Track players with god mode enabled
|
|
||||||
/// HashSet for O(1) lookup performance
|
|
||||||
/// </summary>
|
|
||||||
private static readonly HashSet<int> GodPlayers = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Track players with modified speed
|
|
||||||
/// Dictionary for storing speed values per player
|
|
||||||
/// </summary>
|
|
||||||
private static readonly Dictionary<CCSPlayerController, float> SpeedPlayers = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Track players with modified gravity
|
|
||||||
/// Dictionary for storing gravity values per player
|
|
||||||
/// </summary>
|
|
||||||
private static readonly Dictionary<CCSPlayerController, float> GravityPlayers = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// BEST PRACTICE: Use capability system to get SimpleAdmin API
|
|
||||||
/// This ensures your module works even if SimpleAdmin loads after your module
|
|
||||||
/// </summary>
|
|
||||||
private ICS2_SimpleAdminApi? _sharedApi;
|
|
||||||
private readonly PluginCapability<ICS2_SimpleAdminApi> _pluginCapability = new("simpleadmin:api");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// BEST PRACTICE: Track menu registration state to prevent duplicate registrations
|
|
||||||
/// </summary>
|
|
||||||
private bool _menusRegistered = false;
|
|
||||||
|
|
||||||
// Plugin metadata
|
|
||||||
public override string ModuleName => "CS2-SimpleAdmin Fun Commands";
|
|
||||||
public override string ModuleVersion => "1.0.0";
|
|
||||||
public override string ModuleAuthor => "Your Name";
|
|
||||||
public override string ModuleDescription => "Fun commands extension for CS2-SimpleAdmin";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// BEST PRACTICE: Initialize plugin after all plugins are loaded
|
|
||||||
/// This ensures SimpleAdmin API is available
|
|
||||||
/// </summary>
|
|
||||||
public override void OnAllPluginsLoaded(bool hotReload)
|
|
||||||
{
|
|
||||||
// STEP 1: Get SimpleAdmin API using capability system
|
|
||||||
_sharedApi = _pluginCapability.Get();
|
|
||||||
if (_sharedApi == null)
|
|
||||||
{
|
|
||||||
Logger.LogError("CS2-SimpleAdmin API not found - make sure CS2-SimpleAdmin is loaded!");
|
|
||||||
Unload(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// STEP 2: Register commands (can be done immediately)
|
|
||||||
RegisterFunCommands();
|
|
||||||
|
|
||||||
// STEP 3: Register menus (wait for SimpleAdmin to be ready)
|
|
||||||
// BEST PRACTICE: Use event + fallback to handle both normal load and hot reload
|
|
||||||
_sharedApi.OnSimpleAdminReady += RegisterFunMenus;
|
|
||||||
RegisterFunMenus(); // Fallback for hot reload case
|
|
||||||
|
|
||||||
// STEP 4: Start timer to maintain speed and gravity modifications
|
|
||||||
StartSpeedGravityTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Unload(bool hotReload)
|
|
||||||
{
|
|
||||||
if (_sharedApi == null) return;
|
|
||||||
|
|
||||||
// Unregister commands
|
|
||||||
if (Config.NoclipCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.NoclipCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.UnRegisterCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.GodCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.GodCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.UnRegisterCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.FreezeCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.FreezeCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.UnRegisterCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.UnfreezeCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.UnfreezeCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.UnRegisterCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.RespawnCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.RespawnCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.UnRegisterCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.GiveCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.GiveCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.UnRegisterCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.StripCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.StripCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.UnRegisterCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.HpCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.HpCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.UnRegisterCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.SpeedCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.SpeedCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.UnRegisterCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.GravityCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.GravityCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.UnRegisterCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.MoneyCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.MoneyCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.UnRegisterCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.ResizeCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.ResizeCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.UnRegisterCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unregister menus
|
|
||||||
if (Config.NoclipCommands.Count > 0)
|
|
||||||
_sharedApi.UnregisterMenu("fun", "noclip");
|
|
||||||
|
|
||||||
if (Config.GodCommands.Count > 0)
|
|
||||||
_sharedApi.UnregisterMenu("fun", "god");
|
|
||||||
|
|
||||||
if (Config.RespawnCommands.Count > 0)
|
|
||||||
_sharedApi.UnregisterMenu("fun", "respawn");
|
|
||||||
|
|
||||||
if (Config.GiveCommands.Count > 0)
|
|
||||||
_sharedApi.UnregisterMenu("fun", "give");
|
|
||||||
|
|
||||||
if (Config.StripCommands.Count > 0)
|
|
||||||
_sharedApi.UnregisterMenu("fun", "strip");
|
|
||||||
|
|
||||||
if (Config.FreezeCommands.Count > 0)
|
|
||||||
_sharedApi.UnregisterMenu("fun", "freeze");
|
|
||||||
|
|
||||||
if (Config.HpCommands.Count > 0)
|
|
||||||
_sharedApi.UnregisterMenu("fun", "hp");
|
|
||||||
|
|
||||||
if (Config.SpeedCommands.Count > 0)
|
|
||||||
_sharedApi.UnregisterMenu("fun", "speed");
|
|
||||||
|
|
||||||
if (Config.GravityCommands.Count > 0)
|
|
||||||
_sharedApi.UnregisterMenu("fun", "gravity");
|
|
||||||
|
|
||||||
if (Config.MoneyCommands.Count > 0)
|
|
||||||
_sharedApi.UnregisterMenu("fun", "money");
|
|
||||||
|
|
||||||
if (Config.ResizeCommands.Count > 0)
|
|
||||||
_sharedApi.UnregisterMenu("fun", "resize");
|
|
||||||
|
|
||||||
_sharedApi.OnSimpleAdminReady -= RegisterFunMenus;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RegisterFunCommands()
|
|
||||||
{
|
|
||||||
if (_sharedApi == null) return;
|
|
||||||
|
|
||||||
if (Config.NoclipCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.NoclipCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.RegisterCommand(command, "Enable noclip", OnNoclipCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.GodCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.GodCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.RegisterCommand(command, "Enable god mode", OnGodCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.FreezeCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.FreezeCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.RegisterCommand(command, "Freeze player", OnFreezeCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.UnfreezeCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.UnfreezeCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.RegisterCommand(command, "Unfreeze player", OnUnfreezeCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.RespawnCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.RespawnCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.RegisterCommand(command, "Respawn player", OnRespawnCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.GiveCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.GiveCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.RegisterCommand(command, "Give weapon", OnGiveWeaponCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.StripCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.StripCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.RegisterCommand(command, "Strip weapons", OnStripWeaponsCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.HpCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.HpCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.RegisterCommand(command, "Set HP", OnSetHpCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.SpeedCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.SpeedCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.RegisterCommand(command, "Set speed", OnSetSpeedCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.GravityCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.GravityCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.RegisterCommand(command, "Set gravity", OnSetGravityCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.MoneyCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.MoneyCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.RegisterCommand(command, "Set money", OnSetMoneyCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.ResizeCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.ResizeCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.RegisterCommand(command, "Resize player", OnSetResizeCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RegisterFunMenus()
|
|
||||||
{
|
|
||||||
if (_sharedApi == null || _menusRegistered) return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_sharedApi.RegisterMenuCategory("fun", Localizer?["fun_category_name"] ?? "Fun Commands", "@css/generic");
|
|
||||||
|
|
||||||
if (Config.GodCommands.Count > 0)
|
|
||||||
_sharedApi.RegisterMenu("fun", "god",
|
|
||||||
Localizer?["fun_menu_god"] ?? "God Mode",
|
|
||||||
CreateGodModeMenu, "@css/cheats");
|
|
||||||
|
|
||||||
if (Config.NoclipCommands.Count > 0)
|
|
||||||
_sharedApi.RegisterMenu("fun", "noclip",
|
|
||||||
Localizer?["fun_menu_noclip"] ?? "No Clip",
|
|
||||||
CreateNoClipMenu, "@css/cheats");
|
|
||||||
|
|
||||||
if (Config.RespawnCommands.Count > 0)
|
|
||||||
_sharedApi.RegisterMenu("fun", "respawn",
|
|
||||||
Localizer?["fun_menu_respawn"] ?? "Respawn",
|
|
||||||
CreateRespawnMenu, "@css/cheats");
|
|
||||||
|
|
||||||
if (Config.GiveCommands.Count > 0)
|
|
||||||
_sharedApi.RegisterMenu("fun", "give",
|
|
||||||
Localizer?["fun_menu_give"] ?? "Give Weapon",
|
|
||||||
CreateGiveWeaponMenu, "@css/cheats");
|
|
||||||
|
|
||||||
if (Config.StripCommands.Count > 0)
|
|
||||||
_sharedApi.RegisterMenu("fun", "strip",
|
|
||||||
Localizer?["fun_menu_strip"] ?? "Strip Weapons",
|
|
||||||
CreateStripWeaponsMenu, "@css/slay");
|
|
||||||
|
|
||||||
if (Config.FreezeCommands.Count > 0)
|
|
||||||
_sharedApi.RegisterMenu("fun", "freeze",
|
|
||||||
Localizer?["fun_menu_freeze"] ?? "Freeze",
|
|
||||||
CreateFreezeMenu, "@css/slay");
|
|
||||||
|
|
||||||
if (Config.HpCommands.Count > 0)
|
|
||||||
_sharedApi.RegisterMenu("fun", "hp",
|
|
||||||
Localizer?["fun_menu_hp"] ?? "Set HP",
|
|
||||||
CreateSetHpMenu, "@css/slay");
|
|
||||||
|
|
||||||
if (Config.SpeedCommands.Count > 0)
|
|
||||||
_sharedApi.RegisterMenu("fun", "speed",
|
|
||||||
Localizer?["fun_menu_speed"] ?? "Set Speed",
|
|
||||||
CreateSetSpeedMenu, "@css/slay");
|
|
||||||
|
|
||||||
if (Config.GravityCommands.Count > 0)
|
|
||||||
_sharedApi.RegisterMenu("fun", "gravity",
|
|
||||||
Localizer?["fun_menu_gravity"] ?? "Set Gravity",
|
|
||||||
CreateSetGravityMenu, "@css/slay");
|
|
||||||
|
|
||||||
if (Config.MoneyCommands.Count > 0)
|
|
||||||
_sharedApi.RegisterMenu("fun", "money",
|
|
||||||
Localizer?["fun_menu_money"] ?? "Set Money",
|
|
||||||
CreateSetMoneyMenu, "@css/slay");
|
|
||||||
|
|
||||||
if (Config.ResizeCommands.Count > 0)
|
|
||||||
_sharedApi.RegisterMenu("fun", "resize",
|
|
||||||
Localizer?["fun_menu_resize"] ?? "Resize Player",
|
|
||||||
CreateSetResizeMenu, "@css/slay");
|
|
||||||
|
|
||||||
_menusRegistered = true;
|
|
||||||
Logger.LogInformation("Fun menus registered successfully!");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError($"Failed to register Fun menus: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnConfigParsed(Config config)
|
|
||||||
{
|
|
||||||
Config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Starts a repeating timer to maintain speed and gravity modifications for players.
|
|
||||||
/// This ensures that speed/gravity changes persist even after respawns or round changes.
|
|
||||||
/// </summary>
|
|
||||||
private void StartSpeedGravityTimer()
|
|
||||||
{
|
|
||||||
AddTimer(0.12f, () =>
|
|
||||||
{
|
|
||||||
// Early exit if no players have modified speed or gravity
|
|
||||||
var hasSpeedPlayers = SpeedPlayers.Count > 0;
|
|
||||||
var hasGravityPlayers = GravityPlayers.Count > 0;
|
|
||||||
|
|
||||||
if (!hasSpeedPlayers && !hasGravityPlayers)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (hasSpeedPlayers)
|
|
||||||
{
|
|
||||||
// Iterate through players with modified speed
|
|
||||||
foreach (var kvp in SpeedPlayers)
|
|
||||||
{
|
|
||||||
var player = kvp.Key;
|
|
||||||
// Early validation check - avoid accessing PlayerPawn if player is invalid
|
|
||||||
if (player.IsValid && player.Connected == PlayerConnectedState.PlayerConnected)
|
|
||||||
{
|
|
||||||
var pawn = player.PlayerPawn?.Value;
|
|
||||||
if (pawn != null && pawn.LifeState == (int)LifeState_t.LIFE_ALIVE)
|
|
||||||
{
|
|
||||||
player.SetSpeed(kvp.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasGravityPlayers)
|
|
||||||
{
|
|
||||||
// Iterate through players with modified gravity
|
|
||||||
foreach (var kvp in GravityPlayers)
|
|
||||||
{
|
|
||||||
var player = kvp.Key;
|
|
||||||
if (player.IsValid && player.Connected == PlayerConnectedState.PlayerConnected)
|
|
||||||
{
|
|
||||||
var pawn = player.PlayerPawn?.Value;
|
|
||||||
if (pawn != null && pawn.LifeState == (int)LifeState_t.LIFE_ALIVE)
|
|
||||||
{
|
|
||||||
player.SetGravity(kvp.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.REPEAT);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<RootNamespace>CS2_SimpleAdmin_FunCommands</RootNamespace>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="CS2-SimpleAdminApi">
|
|
||||||
<HintPath>..\CS2-SimpleAdminApi.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.340" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Update="lang\**\*.json" CopyToOutputDirectory="PreserveNewest" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,320 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using CounterStrikeSharp.API.Core;
|
|
||||||
using CounterStrikeSharp.API.Modules.Admin;
|
|
||||||
using CounterStrikeSharp.API.Modules.Commands;
|
|
||||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin_FunCommands;
|
|
||||||
|
|
||||||
public partial class CS2_SimpleAdmin_FunCommands
|
|
||||||
{
|
|
||||||
// =================================
|
|
||||||
// COMMAND HANDLERS
|
|
||||||
// =================================
|
|
||||||
|
|
||||||
[CommandHelper(1, "<#userid or name>")]
|
|
||||||
[RequiresPermissions("@css/cheats")]
|
|
||||||
private void OnNoclipCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
var targets = _sharedApi!.GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player =>
|
|
||||||
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
NoClip(caller, player);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[RequiresPermissions("@css/cheats")]
|
|
||||||
[CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
|
||||||
private void OnGodCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
var targets = _sharedApi!.GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player =>
|
|
||||||
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
God(caller, player);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHelper(1, "<#userid or name> [duration]")]
|
|
||||||
[RequiresPermissions("@css/slay")]
|
|
||||||
private void OnFreezeCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
int.TryParse(command.GetArg(2), out var time);
|
|
||||||
var targets = _sharedApi!.GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player =>
|
|
||||||
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
Freeze(caller, player, time);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHelper(1, "<#userid or name>")]
|
|
||||||
[RequiresPermissions("@css/slay")]
|
|
||||||
private void OnUnfreezeCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
var targets = _sharedApi!.GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player =>
|
|
||||||
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
Unfreeze(caller, player);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHelper(1, "<#userid or name>")]
|
|
||||||
[RequiresPermissions("@css/cheats")]
|
|
||||||
private void OnRespawnCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
var targets = _sharedApi!.GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player =>
|
|
||||||
player is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
Respawn(caller, player);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHelper(2, "<#userid or name> <weapon>")]
|
|
||||||
[RequiresPermissions("@css/cheats")]
|
|
||||||
private void OnGiveWeaponCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
var weaponName = command.GetArg(2);
|
|
||||||
if (Enum.TryParse(weaponName, true, out CsItem weapon))
|
|
||||||
{
|
|
||||||
var targets = _sharedApi!.GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player =>
|
|
||||||
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
player.GiveNamedItem(weapon);
|
|
||||||
LogAndShowActivity(caller, player, "fun_admin_give_message", "css_give", weapon.ToString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHelper(1, "<#userid or name>")]
|
|
||||||
[RequiresPermissions("@css/slay")]
|
|
||||||
private void OnStripWeaponsCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
var targets = _sharedApi!.GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player =>
|
|
||||||
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
player.RemoveWeapons();
|
|
||||||
LogAndShowActivity(caller, player, "fun_admin_strip_message", "css_strip");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHelper(2, "<#userid or name> <hp>")]
|
|
||||||
[RequiresPermissions("@css/slay")]
|
|
||||||
private void OnSetHpCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
if (int.TryParse(command.GetArg(2), out var hp))
|
|
||||||
{
|
|
||||||
var targets = _sharedApi!.GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player =>
|
|
||||||
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
player.SetHp(hp);
|
|
||||||
LogAndShowActivity(caller, player, "fun_admin_hp_message", "css_hp", hp.ToString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHelper(2, "<#userid or name> <speed>")]
|
|
||||||
[RequiresPermissions("@css/slay")]
|
|
||||||
private void OnSetSpeedCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
if (float.TryParse(command.GetArg(2), NumberStyles.Float, CultureInfo.InvariantCulture, out var speed))
|
|
||||||
{
|
|
||||||
var targets = _sharedApi!.GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player =>
|
|
||||||
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
player.SetSpeed(speed);
|
|
||||||
|
|
||||||
// Track speed modification for timer
|
|
||||||
if (speed == 1f)
|
|
||||||
SpeedPlayers.Remove(player);
|
|
||||||
else
|
|
||||||
SpeedPlayers[player] = speed;
|
|
||||||
|
|
||||||
LogAndShowActivity(caller, player, "fun_admin_speed_message", "css_speed", speed.ToString(CultureInfo.InvariantCulture));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHelper(2, "<#userid or name> <gravity>")]
|
|
||||||
[RequiresPermissions("@css/slay")]
|
|
||||||
private void OnSetGravityCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
if (float.TryParse(command.GetArg(2), NumberStyles.Float, CultureInfo.InvariantCulture, out var gravity))
|
|
||||||
{
|
|
||||||
var targets = _sharedApi!.GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player =>
|
|
||||||
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
player.SetGravity(gravity);
|
|
||||||
|
|
||||||
// Track gravity modification for timer
|
|
||||||
if (gravity == 1f)
|
|
||||||
GravityPlayers.Remove(player);
|
|
||||||
else
|
|
||||||
GravityPlayers[player] = gravity;
|
|
||||||
|
|
||||||
LogAndShowActivity(caller, player, "fun_admin_gravity_message", "css_gravity", gravity.ToString(CultureInfo.InvariantCulture));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHelper(2, "<#userid or name> <money>")]
|
|
||||||
[RequiresPermissions("@css/slay")]
|
|
||||||
private void OnSetMoneyCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
if (int.TryParse(command.GetArg(2), out var money))
|
|
||||||
{
|
|
||||||
var targets = _sharedApi!.GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player =>
|
|
||||||
player is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
player.SetMoney(money);
|
|
||||||
LogAndShowActivity(caller, player, "fun_admin_money_message", "css_money", money.ToString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHelper(2, "<#userid or name> <size>")]
|
|
||||||
[RequiresPermissions("@css/slay")]
|
|
||||||
private void OnSetResizeCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
if (float.TryParse(command.GetArg(2), NumberStyles.Float, CultureInfo.InvariantCulture, out var size))
|
|
||||||
{
|
|
||||||
var targets = _sharedApi!.GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player =>
|
|
||||||
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
Resize(caller, player, size);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// =================================
|
|
||||||
// HELPER METHOD FOR ACTIVITIES WITH INDIVIDUAL COMMAND LOGGING
|
|
||||||
// =================================
|
|
||||||
|
|
||||||
private void LogAndShowActivity(CCSPlayerController? caller, CCSPlayerController target, string messageKey, string baseCommand, params string[] extraArgs)
|
|
||||||
{
|
|
||||||
var callerName = caller?.PlayerName ?? "Console";
|
|
||||||
|
|
||||||
// Build activity args
|
|
||||||
var args = new List<object> { "CALLER", target.PlayerName };
|
|
||||||
args.AddRange(extraArgs);
|
|
||||||
|
|
||||||
// Show admin activity using module's own localizer with per-player language support
|
|
||||||
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
|
|
||||||
{
|
|
||||||
// Use module's own translations with automatic per-player language support
|
|
||||||
if (Localizer != null)
|
|
||||||
{
|
|
||||||
// This will send the message in each player's configured language
|
|
||||||
_sharedApi!.ShowAdminActivityLocalized(Localizer, messageKey, callerName, false, args.ToArray());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Fallback to old method if localizer is not available
|
|
||||||
_sharedApi!.ShowAdminActivity(messageKey, callerName, false, args.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build and log command using API string method
|
|
||||||
var logCommand = $"{baseCommand} {(string.IsNullOrEmpty(target.PlayerName) ? target.SteamID.ToString() : target.PlayerName)}";
|
|
||||||
if (extraArgs.Length > 0)
|
|
||||||
{
|
|
||||||
logCommand += $" {string.Join(" ", extraArgs)}";
|
|
||||||
}
|
|
||||||
|
|
||||||
_sharedApi!.LogCommand(caller, logCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using CounterStrikeSharp.API.Core;
|
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin_FunCommands;
|
|
||||||
|
|
||||||
public class Config : IBasePluginConfig
|
|
||||||
{
|
|
||||||
public int Version { get; set; } = 1;
|
|
||||||
|
|
||||||
public List<string> NoclipCommands { get; set; } = ["css_noclip"];
|
|
||||||
public List<string> GodCommands { get; set; } = ["css_god"];
|
|
||||||
public List<string> FreezeCommands { get; set; } = ["css_freeze"];
|
|
||||||
public List<string> UnfreezeCommands { get; set; } = ["css_unfreeze"];
|
|
||||||
public List<string> RespawnCommands { get; set; } = ["css_respawn"];
|
|
||||||
public List<string> GiveCommands { get; set; } = ["css_give"];
|
|
||||||
public List<string> StripCommands { get; set; } = ["css_strip"];
|
|
||||||
public List<string> HpCommands { get; set; } = ["css_hp"];
|
|
||||||
public List<string> SpeedCommands { get; set; } = ["css_speed"];
|
|
||||||
public List<string> GravityCommands { get; set; } = ["css_gravity"];
|
|
||||||
public List<string> MoneyCommands { get; set; } = ["css_money"];
|
|
||||||
public List<string> ResizeCommands { get; set; } = ["css_resize"];
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
using CounterStrikeSharp.API;
|
|
||||||
using CounterStrikeSharp.API.Core;
|
|
||||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin_FunCommands;
|
|
||||||
|
|
||||||
public partial class CS2_SimpleAdmin_FunCommands
|
|
||||||
{
|
|
||||||
[GameEventHandler]
|
|
||||||
public HookResult OnPlayerHurt(EventPlayerHurt @event, GameEventInfo info)
|
|
||||||
{
|
|
||||||
var player = @event.Userid;
|
|
||||||
if (player == null || !player.IsValid) return HookResult.Continue;
|
|
||||||
|
|
||||||
// Check if player has god mode (similar to main plugin)
|
|
||||||
if (!GodPlayers.Contains(player.Slot)) return HookResult.Continue;
|
|
||||||
|
|
||||||
// Cancel damage
|
|
||||||
@event.DmgHealth = 0;
|
|
||||||
@event.DmgArmor = 0;
|
|
||||||
|
|
||||||
// Reset health to full
|
|
||||||
if (player.PlayerPawn?.Value == null) return HookResult.Continue;
|
|
||||||
|
|
||||||
player.PlayerPawn.Value.Health = player.PlayerPawn.Value.MaxHealth;
|
|
||||||
Utilities.SetStateChanged(player.PlayerPawn.Value, "CBaseEntity", "m_iHealth");
|
|
||||||
|
|
||||||
return HookResult.Continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
[GameEventHandler]
|
|
||||||
public HookResult OnPlayerDeath(EventPlayerDeath @event, GameEventInfo info)
|
|
||||||
{
|
|
||||||
var player = @event.Userid;
|
|
||||||
if (player == null || !player.IsValid) return HookResult.Continue;
|
|
||||||
|
|
||||||
// Remove player from god mode, speed, and gravity tracking on death
|
|
||||||
GodPlayers.Remove(player.Slot);
|
|
||||||
SpeedPlayers.Remove(player);
|
|
||||||
GravityPlayers.Remove(player);
|
|
||||||
|
|
||||||
return HookResult.Continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
[GameEventHandler]
|
|
||||||
public HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info)
|
|
||||||
{
|
|
||||||
// Clear all fun command modifications at round start
|
|
||||||
GodPlayers.Clear();
|
|
||||||
SpeedPlayers.Clear();
|
|
||||||
GravityPlayers.Clear();
|
|
||||||
|
|
||||||
return HookResult.Continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
[GameEventHandler]
|
|
||||||
public HookResult OnPlayerDisconnect(EventPlayerDisconnect @event, GameEventInfo info)
|
|
||||||
{
|
|
||||||
var player = @event.Userid;
|
|
||||||
if (player == null || !player.IsValid) return HookResult.Continue;
|
|
||||||
|
|
||||||
// Clean up player from all tracking when they disconnect
|
|
||||||
GodPlayers.Remove(player.Slot);
|
|
||||||
SpeedPlayers.Remove(player);
|
|
||||||
GravityPlayers.Remove(player);
|
|
||||||
|
|
||||||
return HookResult.Continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,391 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using CounterStrikeSharp.API;
|
|
||||||
using CounterStrikeSharp.API.Core;
|
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin_FunCommands;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Menu creation methods for Fun Commands module.
|
|
||||||
/// This file demonstrates different menu patterns using SimpleAdmin API.
|
|
||||||
/// </summary>
|
|
||||||
public partial class CS2_SimpleAdmin_FunCommands
|
|
||||||
{
|
|
||||||
// =================================
|
|
||||||
// SIMPLE PLAYER SELECTION MENUS
|
|
||||||
// =================================
|
|
||||||
// Pattern: Direct player selection with immediate action
|
|
||||||
// Use CreateMenuWithPlayers when you just need to select a player and execute an action
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a simple player selection menu for god mode.
|
|
||||||
/// PATTERN: CreateMenuWithPlayers with method reference
|
|
||||||
/// </summary>
|
|
||||||
private object CreateGodModeMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
return _sharedApi!.CreateMenuWithPlayers(
|
|
||||||
Localizer?["fun_menu_god"] ?? "God Mode", // Menu title from translation
|
|
||||||
"fun", // Category ID (for back button navigation)
|
|
||||||
admin, // Admin opening the menu
|
|
||||||
player => player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(player), // Filter: only alive, targetable players
|
|
||||||
God); // Action to execute (method reference)
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateNoClipMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
return _sharedApi!.CreateMenuWithPlayers(
|
|
||||||
Localizer?["fun_menu_noclip"] ?? "No Clip",
|
|
||||||
"fun",
|
|
||||||
admin,
|
|
||||||
player => player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(player),
|
|
||||||
NoClip);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a player selection menu for respawn command.
|
|
||||||
/// PATTERN: CreateMenuWithPlayers with method reference
|
|
||||||
/// </summary>
|
|
||||||
private object CreateRespawnMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
return _sharedApi!.CreateMenuWithPlayers(
|
|
||||||
Localizer?["fun_menu_respawn"] ?? "Respawn", // Menu title from translation
|
|
||||||
"fun", // Category ID
|
|
||||||
admin, // Admin
|
|
||||||
admin.CanTarget, // Filter: only targetable players (no LifeState check - can respawn dead players)
|
|
||||||
Respawn); // Use the Respawn method which includes death position teleport
|
|
||||||
}
|
|
||||||
|
|
||||||
// =================================
|
|
||||||
// NESTED MENUS - PLAYER → VALUE SELECTION
|
|
||||||
// =================================
|
|
||||||
// Pattern: First select player, then select a value/option for that player
|
|
||||||
// Use CreateMenuWithBack + AddSubMenu for multi-level menus
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a nested menu: Player selection → Weapon selection.
|
|
||||||
/// PATTERN: CreateMenuWithBack + foreach + AddSubMenu
|
|
||||||
/// </summary>
|
|
||||||
private object CreateGiveWeaponMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var menu = _sharedApi!.CreateMenuWithBack(
|
|
||||||
Localizer?["fun_menu_give"] ?? "Give Weapon",
|
|
||||||
"fun",
|
|
||||||
admin);
|
|
||||||
var players = _sharedApi.GetValidPlayers().Where(p =>
|
|
||||||
p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
|
|
||||||
// AddSubMenu automatically adds a "Back" button to the submenu
|
|
||||||
// The lambda receives 'p' but we use captured 'player' variable (closure)
|
|
||||||
_sharedApi.AddSubMenu(menu, playerName, p => CreateWeaponSelectionMenu(admin, player));
|
|
||||||
}
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates weapon selection submenu for a specific player.
|
|
||||||
/// PATTERN: CreateMenuWithBack + foreach + AddMenuOption
|
|
||||||
/// </summary>
|
|
||||||
private object CreateWeaponSelectionMenu(CCSPlayerController admin, CCSPlayerController target)
|
|
||||||
{
|
|
||||||
var weaponMenu = _sharedApi!.CreateMenuWithBack(
|
|
||||||
Localizer?["fun_menu_give_player", target.PlayerName] ?? $"Give Weapon: {target.PlayerName}",
|
|
||||||
"fun",
|
|
||||||
admin);
|
|
||||||
|
|
||||||
// Loop through cached weapons (performance optimization)
|
|
||||||
foreach (var weapon in GetWeaponsCache())
|
|
||||||
{
|
|
||||||
// AddMenuOption for each selectable option
|
|
||||||
// IMPORTANT: Always validate target.IsValid before executing action
|
|
||||||
_sharedApi.AddMenuOption(weaponMenu, weapon.Value.ToString(), _ =>
|
|
||||||
{
|
|
||||||
if (target.IsValid) // Player might disconnect before selection
|
|
||||||
{
|
|
||||||
target.GiveNamedItem(weapon.Value);
|
|
||||||
LogAndShowActivity(admin, target, "fun_admin_give_message", $"css_give", weapon.Value.ToString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return weaponMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateStripWeaponsMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
return _sharedApi!.CreateMenuWithPlayers(
|
|
||||||
Localizer?["fun_menu_strip"] ?? "Strip Weapons",
|
|
||||||
"fun",
|
|
||||||
admin,
|
|
||||||
player => player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(player),
|
|
||||||
(adminPlayer, targetPlayer) =>
|
|
||||||
{
|
|
||||||
targetPlayer.RemoveWeapons();
|
|
||||||
LogAndShowActivity(adminPlayer, targetPlayer, "fun_admin_strip_message", "css_strip");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateFreezeMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
return _sharedApi!.CreateMenuWithPlayers(
|
|
||||||
Localizer?["fun_menu_freeze"] ?? "Freeze",
|
|
||||||
"fun",
|
|
||||||
admin,
|
|
||||||
player => player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(player),
|
|
||||||
(adminPlayer, targetPlayer) => { Freeze(adminPlayer, targetPlayer, -1); });
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a nested menu for setting player HP with predefined values.
|
|
||||||
/// PATTERN: Same as Give Weapon (player selection → value selection)
|
|
||||||
/// This is a common pattern you'll use frequently!
|
|
||||||
/// </summary>
|
|
||||||
private object CreateSetHpMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var menu = _sharedApi!.CreateMenuWithBack(
|
|
||||||
Localizer?["fun_menu_hp"] ?? "Set HP",
|
|
||||||
"fun",
|
|
||||||
admin);
|
|
||||||
var players = _sharedApi.GetValidPlayers().Where(p =>
|
|
||||||
p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
_sharedApi.AddSubMenu(menu, playerName, p => CreateHpSelectionMenu(admin, player));
|
|
||||||
}
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates HP value selection submenu.
|
|
||||||
/// TIP: Use arrays for predefined values - easy to modify and maintain
|
|
||||||
/// </summary>
|
|
||||||
private object CreateHpSelectionMenu(CCSPlayerController admin, CCSPlayerController target)
|
|
||||||
{
|
|
||||||
var hpSelectionMenu = _sharedApi!.CreateMenuWithBack(
|
|
||||||
Localizer?["fun_menu_hp_player", target.PlayerName] ?? $"Set HP: {target.PlayerName}",
|
|
||||||
"fun",
|
|
||||||
admin);
|
|
||||||
|
|
||||||
// Predefined HP values - easy to customize
|
|
||||||
var hpValues = new[] { 1, 10, 25, 50, 100, 200, 500, 999 };
|
|
||||||
|
|
||||||
foreach (var hp in hpValues)
|
|
||||||
{
|
|
||||||
_sharedApi.AddMenuOption(hpSelectionMenu,
|
|
||||||
Localizer?["fun_menu_hp_value", hp] ?? $"{hp} HP",
|
|
||||||
_ =>
|
|
||||||
{
|
|
||||||
if (target.IsValid)
|
|
||||||
{
|
|
||||||
target.SetHp(hp);
|
|
||||||
LogAndShowActivity(admin, target, "fun_admin_hp_message", "css_hp", hp.ToString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return hpSelectionMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateSetSpeedMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var menu = _sharedApi!.CreateMenuWithBack(
|
|
||||||
Localizer?["fun_menu_speed"] ?? "Set Speed",
|
|
||||||
"fun",
|
|
||||||
admin);
|
|
||||||
var players = _sharedApi.GetValidPlayers().Where(p =>
|
|
||||||
p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
_sharedApi.AddSubMenu(menu, playerName, p => CreateSpeedSelectionMenu(admin, player));
|
|
||||||
}
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates speed value selection submenu.
|
|
||||||
/// TIP: Use tuples (value, display) when you need different internal value vs display text
|
|
||||||
/// Example: (0.5f, "0.5") - float value for code, string for display
|
|
||||||
/// </summary>
|
|
||||||
private object CreateSpeedSelectionMenu(CCSPlayerController admin, CCSPlayerController target)
|
|
||||||
{
|
|
||||||
var speedSelectionMenu = _sharedApi!.CreateMenuWithBack(
|
|
||||||
Localizer?["fun_menu_speed_player", target.PlayerName] ?? $"Set Speed: {target.PlayerName}",
|
|
||||||
"fun",
|
|
||||||
admin);
|
|
||||||
|
|
||||||
// Tuple pattern: (actualValue, displayText)
|
|
||||||
// Useful when display text differs from actual value
|
|
||||||
var speedValues = new[]
|
|
||||||
{
|
|
||||||
(0.1f, "0.1"), (0.25f, "0.25"), (0.5f, "0.5"), (0.75f, "0.75"), (1f, "1"), (2f, "2"), (3f, "3"), (4f, "4")
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var (speed, display) in speedValues)
|
|
||||||
{
|
|
||||||
_sharedApi.AddMenuOption(speedSelectionMenu,
|
|
||||||
Localizer?["fun_menu_speed_value", display] ?? $"Speed {display}",
|
|
||||||
_ =>
|
|
||||||
{
|
|
||||||
if (target.IsValid)
|
|
||||||
{
|
|
||||||
target.SetSpeed(speed);
|
|
||||||
|
|
||||||
// Track speed modification for timer
|
|
||||||
if (speed == 1f)
|
|
||||||
SpeedPlayers.Remove(target);
|
|
||||||
else
|
|
||||||
SpeedPlayers[target] = speed;
|
|
||||||
|
|
||||||
LogAndShowActivity(admin, target, "fun_admin_speed_message", "css_speed", speed.ToString(CultureInfo.InvariantCulture));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return speedSelectionMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateSetGravityMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var menu = _sharedApi!.CreateMenuWithBack(
|
|
||||||
Localizer?["fun_menu_gravity"] ?? "Set Gravity",
|
|
||||||
"fun",
|
|
||||||
admin);
|
|
||||||
var players = _sharedApi.GetValidPlayers().Where(p =>
|
|
||||||
p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
_sharedApi.AddSubMenu(menu, playerName, p => CreateGravitySelectionMenu(admin, player));
|
|
||||||
}
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateGravitySelectionMenu(CCSPlayerController admin, CCSPlayerController target)
|
|
||||||
{
|
|
||||||
var gravitySelectionMenu = _sharedApi!.CreateMenuWithBack(
|
|
||||||
Localizer?["fun_menu_gravity_player", target.PlayerName] ?? $"Set Gravity: {target.PlayerName}",
|
|
||||||
"fun",
|
|
||||||
admin);
|
|
||||||
var gravityValues = new[]
|
|
||||||
{ (0.1f, "0.1"), (0.25f, "0.25"), (0.5f, "0.5"), (0.75f, "0.75"), (1f, "1"), (2f, "2") };
|
|
||||||
|
|
||||||
foreach (var (gravity, display) in gravityValues)
|
|
||||||
{
|
|
||||||
_sharedApi.AddMenuOption(gravitySelectionMenu,
|
|
||||||
Localizer?["fun_menu_gravity_value", display] ?? $"Gravity {display}",
|
|
||||||
_ =>
|
|
||||||
{
|
|
||||||
if (target.IsValid)
|
|
||||||
{
|
|
||||||
target.SetGravity(Convert.ToSingle(gravity, CultureInfo.InvariantCulture));
|
|
||||||
|
|
||||||
// Track gravity modification for timer
|
|
||||||
if (gravity == 1f)
|
|
||||||
GravityPlayers.Remove(target);
|
|
||||||
else
|
|
||||||
GravityPlayers[target] = gravity;
|
|
||||||
|
|
||||||
LogAndShowActivity(admin, target, "fun_admin_gravity_message", "css_gravity", gravity.ToString(CultureInfo.InvariantCulture));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return gravitySelectionMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateSetMoneyMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var menu = _sharedApi!.CreateMenuWithBack(
|
|
||||||
Localizer?["fun_menu_money"] ?? "Set Money",
|
|
||||||
"fun",
|
|
||||||
admin);
|
|
||||||
var players = _sharedApi.GetValidPlayers().Where(p => admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
_sharedApi.AddSubMenu(menu, playerName, p => CreateMoneySelectionMenu(admin, player));
|
|
||||||
}
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateMoneySelectionMenu(CCSPlayerController admin, CCSPlayerController target)
|
|
||||||
{
|
|
||||||
var moneySelectionMenu = _sharedApi!.CreateMenuWithBack(
|
|
||||||
Localizer?["fun_menu_money_player", target.PlayerName] ?? $"Set Money: {target.PlayerName}",
|
|
||||||
"fun",
|
|
||||||
admin);
|
|
||||||
var moneyValues = new[] { 0, 1000, 2500, 5000, 10000, 16000 };
|
|
||||||
|
|
||||||
foreach (var money in moneyValues)
|
|
||||||
{
|
|
||||||
_sharedApi.AddMenuOption(moneySelectionMenu,
|
|
||||||
Localizer?["fun_menu_money_value", money] ?? $"${money}",
|
|
||||||
_ =>
|
|
||||||
{
|
|
||||||
if (target.IsValid)
|
|
||||||
{
|
|
||||||
target.SetMoney(money);
|
|
||||||
LogAndShowActivity(admin, target, "fun_admin_money_message", "css_money", money.ToString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return moneySelectionMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateSetResizeMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var menu = _sharedApi!.CreateMenuWithBack(
|
|
||||||
Localizer?["fun_menu_resize"] ?? "Resize Player",
|
|
||||||
"fun",
|
|
||||||
admin);
|
|
||||||
var players = _sharedApi.GetValidPlayers().Where(p =>
|
|
||||||
p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
|
||||||
_sharedApi.AddSubMenu(menu, playerName, p => CreateResizeSelectionMenu(admin, player));
|
|
||||||
}
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateResizeSelectionMenu(CCSPlayerController admin, CCSPlayerController target)
|
|
||||||
{
|
|
||||||
var resizeSelectionMenu = _sharedApi!.CreateMenuWithBack(
|
|
||||||
Localizer?["fun_menu_resize_player", target.PlayerName] ?? $"Resize: {target.PlayerName}",
|
|
||||||
"fun",
|
|
||||||
admin);
|
|
||||||
var resizeValues = new[]
|
|
||||||
{ (0.5f, "0.5"), (0.75f, "0.75"), (1f, "1"), (1.25f, "1.25"), (1.5f, "1.5"), (2f, "2"), (3f, "3") };
|
|
||||||
|
|
||||||
foreach (var (resize, display) in resizeValues)
|
|
||||||
{
|
|
||||||
_sharedApi.AddMenuOption(resizeSelectionMenu,
|
|
||||||
Localizer?["fun_menu_resize_value", display] ?? $"Size {display}",
|
|
||||||
_ =>
|
|
||||||
{
|
|
||||||
if (target.IsValid)
|
|
||||||
{
|
|
||||||
Resize(admin, target, resize);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return resizeSelectionMenu;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,271 +0,0 @@
|
|||||||
using System.Drawing;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Numerics;
|
|
||||||
using CounterStrikeSharp.API;
|
|
||||||
using CounterStrikeSharp.API.Core;
|
|
||||||
using CounterStrikeSharp.API.Modules.Admin;
|
|
||||||
using CounterStrikeSharp.API.Modules.Entities;
|
|
||||||
using CounterStrikeSharp.API.Modules.Memory;
|
|
||||||
using CounterStrikeSharp.API.Modules.UserMessages;
|
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin_FunCommands;
|
|
||||||
|
|
||||||
public static class PlayerExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Slaps the player pawn by applying optional damage and adding a random velocity knockback.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pawn">The player pawn to slap.</param>
|
|
||||||
/// <param name="damage">The amount of damage to apply (default is 0).</param>
|
|
||||||
public static void Slap(this CBasePlayerPawn pawn, int damage = 0)
|
|
||||||
{
|
|
||||||
PerformSlap(pawn, damage);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines if the player controller can target another player controller, respecting admin permissions and immunity.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="controller">The player controller who wants to target.</param>
|
|
||||||
/// <param name="target">The player controller being targeted.</param>
|
|
||||||
/// <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;
|
|
||||||
|
|
||||||
return AdminManager.CanPlayerTarget(controller, target) ||
|
|
||||||
AdminManager.CanPlayerTarget(new SteamID(controller.SteamID),
|
|
||||||
new SteamID(target.SteamID)) ||
|
|
||||||
AdminManager.GetPlayerImmunity(controller) >= AdminManager.GetPlayerImmunity(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the controller can target a player by SteamID, considering targeting permissions and immunities.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="controller">The attacker player controller.</param>
|
|
||||||
/// <param name="steamId">The SteamID of the target player.</param>
|
|
||||||
/// <returns>True if targeting is permitted, false otherwise.</returns>
|
|
||||||
public static bool CanTarget(this CCSPlayerController? controller, SteamID steamId)
|
|
||||||
{
|
|
||||||
if (controller is null) return true;
|
|
||||||
|
|
||||||
return AdminManager.CanPlayerTarget(new SteamID(controller.SteamID), steamId) ||
|
|
||||||
AdminManager.GetPlayerImmunity(controller) >= AdminManager.GetPlayerImmunity(steamId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the movement speed modifier of the player controller.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="controller">The player controller.</param>
|
|
||||||
/// <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;
|
|
||||||
|
|
||||||
playerPawnValue.VelocityModifier = speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the gravity scale for the player controller.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="controller">The player controller.</param>
|
|
||||||
/// <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;
|
|
||||||
|
|
||||||
playerPawnValue.ActualGravityScale = gravity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the player's in-game money amount.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="controller">The player controller.</param>
|
|
||||||
/// <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;
|
|
||||||
|
|
||||||
moneyServices.Account = money;
|
|
||||||
|
|
||||||
if (controller != null) Utilities.SetStateChanged(controller, "CCSPlayerController", "m_pInGameMoneyServices");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the player's health points.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="controller">The player controller.</param>
|
|
||||||
/// <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;
|
|
||||||
|
|
||||||
controller.PlayerPawn.Value.Health = health;
|
|
||||||
|
|
||||||
if (health > 100)
|
|
||||||
{
|
|
||||||
controller.PlayerPawn.Value.MaxHealth = health;
|
|
||||||
}
|
|
||||||
|
|
||||||
Utilities.SetStateChanged(controller.PlayerPawn.Value, "CBaseEntity", "m_iHealth");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Buries the player pawn by moving it down by a depth offset.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pawn">The player pawn to bury.</param>
|
|
||||||
/// <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,
|
|
||||||
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);
|
|
||||||
|
|
||||||
pawn.Teleport(newPos, newRotation, newVelocity);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unburies the player pawn by moving it up by a depth offset.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pawn">The player pawn to unbury.</param>
|
|
||||||
/// <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,
|
|
||||||
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);
|
|
||||||
|
|
||||||
pawn.Teleport(newPos, newRotation, newVelocity);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Freezes the player pawn, disabling movement.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pawn">The player pawn to freeze.</param>
|
|
||||||
public static void Freeze(this CBasePlayerPawn pawn)
|
|
||||||
{
|
|
||||||
pawn.MoveType = MoveType_t.MOVETYPE_INVALID;
|
|
||||||
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 11); // invalid
|
|
||||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unfreezes the player pawn, enabling movement.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pawn">The player pawn to unfreeze.</param>
|
|
||||||
public static void Unfreeze(this CBasePlayerPawn pawn)
|
|
||||||
{
|
|
||||||
pawn.MoveType = MoveType_t.MOVETYPE_WALK;
|
|
||||||
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>
|
|
||||||
/// <param name="pawn">The pawn to colorize.</param>
|
|
||||||
/// <param name="r">Red component (0-255).</param>
|
|
||||||
/// <param name="g">Green component (0-255).</param>
|
|
||||||
/// <param name="b">Blue component (0-255).</param>
|
|
||||||
/// <param name="a">Alpha (transparency) component (0-255).</param>
|
|
||||||
public static void Colorize(this CBasePlayerPawn pawn, int r = 255, int g = 255, int b = 255, int a = 255)
|
|
||||||
{
|
|
||||||
pawn.Render = Color.FromArgb(a, r, g, b);
|
|
||||||
Utilities.SetStateChanged(pawn, "CBaseModelEntity", "m_clrRender");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Toggles noclip mode for the player pawn.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pawn">The player pawn.</param>
|
|
||||||
public static void ToggleNoclip(this CBasePlayerPawn pawn)
|
|
||||||
{
|
|
||||||
if (pawn.MoveType == MoveType_t.MOVETYPE_NOCLIP)
|
|
||||||
{
|
|
||||||
pawn.MoveType = MoveType_t.MOVETYPE_WALK;
|
|
||||||
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 2); // walk
|
|
||||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pawn.MoveType = MoveType_t.MOVETYPE_NOCLIP;
|
|
||||||
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 8); // noclip
|
|
||||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Teleports a player controller to the position, rotation, and velocity of another player controller.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="controller">The controller to teleport.</param>
|
|
||||||
/// <param name="target">The target controller whose position to copy.</param>
|
|
||||||
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 } &&
|
|
||||||
target?.PlayerPawn.Value is { AbsOrigin: not null, AbsRotation: not null }
|
|
||||||
)
|
|
||||||
{
|
|
||||||
controller.PlayerPawn.Value.Teleport(
|
|
||||||
target.PlayerPawn.Value.AbsOrigin,
|
|
||||||
target.PlayerPawn.Value.AbsRotation,
|
|
||||||
target.PlayerPawn.Value.AbsVelocity
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies a slap effect to the given player pawn, optionally inflicting damage and adding velocity knockback.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pawn">The player pawn to slap.</param>
|
|
||||||
/// <param name="damage">The amount of damage to deal (default is 0).</param>
|
|
||||||
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>();
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
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.Z += random.Next(200) + 100;
|
|
||||||
|
|
||||||
pawn.AbsVelocity.X = vel.X;
|
|
||||||
pawn.AbsVelocity.Y = vel.Y;
|
|
||||||
pawn.AbsVelocity.Z = vel.Z;
|
|
||||||
|
|
||||||
if (controller != null && controller.IsValid)
|
|
||||||
{
|
|
||||||
var shakeMessage = UserMessage.FromPartialName("Shake");
|
|
||||||
shakeMessage.SetFloat("duration", 1);
|
|
||||||
shakeMessage.SetFloat("amplitude", 10);
|
|
||||||
shakeMessage.SetFloat("frequency", 1f);
|
|
||||||
shakeMessage.SetInt("command", 0);
|
|
||||||
shakeMessage.Recipients.Add(controller);
|
|
||||||
shakeMessage.Send();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (damage <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
pawn.Health -= damage;
|
|
||||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth");
|
|
||||||
|
|
||||||
if (pawn.Health <= 0)
|
|
||||||
pawn.CommitSuicide(true, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"fun_category_name": "أوامر المرح",
|
|
||||||
|
|
||||||
"fun_admin_give_message": "{lightred}{0}{default} أعطى {lightred}{1}{default} {lightred}{2}{default}!",
|
|
||||||
"fun_admin_strip_message": "{lightred}{0}{default} أخذ جميع أسلحة اللاعب {lightred}{1}{default}!",
|
|
||||||
"fun_admin_hp_message": "{lightred}{0}{default} غيّر عدد نقاط الحياة لـ {lightred}{1}{default}!",
|
|
||||||
"fun_admin_speed_message": "{lightred}{0}{default} غيّر السرعة لـ {lightred}{1}{default}!",
|
|
||||||
"fun_admin_gravity_message": "{lightred}{0}{default} غيّر الجاذبية لـ {lightred}{1}{default}!",
|
|
||||||
"fun_admin_money_message": "{lightred}{0}{default} غيّر المال لـ {lightred}{1}{default}!",
|
|
||||||
"fun_admin_god_message": "{lightred}{0}{default} غيّر وضع الله لـ {lightred}{1}{default}!",
|
|
||||||
"fun_admin_noclip_message": "{lightred}{0}{default} فعّل/ألغى نمط اللا تصادم لـ {lightred}{1}{default}!",
|
|
||||||
"fun_admin_freeze_message": "{lightred}{0}{default} جمد {lightred}{1}{default}!",
|
|
||||||
"fun_admin_unfreeze_message": "{lightred}{0}{default} أذاب {lightred}{1}{default}!",
|
|
||||||
"fun_admin_respawn_message": "{lightred}{0}{default} أحيى {lightred}{1}{default}!",
|
|
||||||
|
|
||||||
"fun_menu_god": "وضع الله",
|
|
||||||
"fun_menu_noclip": "اللا تصادم",
|
|
||||||
"fun_menu_respawn": "إحياء",
|
|
||||||
"fun_menu_give": "إعطاء سلاح",
|
|
||||||
"fun_menu_give_player": "إعطاء سلاح: {0}",
|
|
||||||
"fun_menu_strip": "نزع الأسلحة",
|
|
||||||
"fun_menu_freeze": "تجميد",
|
|
||||||
"fun_menu_hp": "ضبط الحياة",
|
|
||||||
"fun_menu_hp_player": "ضبط الحياة: {0}",
|
|
||||||
"fun_menu_speed": "ضبط السرعة",
|
|
||||||
"fun_menu_speed_player": "ضبط السرعة: {0}",
|
|
||||||
"fun_menu_gravity": "ضبط الجاذبية",
|
|
||||||
"fun_menu_gravity_player": "ضبط الجاذبية: {0}",
|
|
||||||
"fun_menu_money": "ضبط المال",
|
|
||||||
"fun_menu_money_player": "ضبط المال: {0}",
|
|
||||||
"fun_menu_hp_value": "{0} HP",
|
|
||||||
"fun_menu_speed_value": "السرعة {0}",
|
|
||||||
"fun_menu_gravity_value": "الجاذبية {0}",
|
|
||||||
"fun_menu_money_value": "${0}",
|
|
||||||
|
|
||||||
"fun_admin_resize_message": "{lightred}{0}{default} غيّر حجم {lightred}{1}{default} إلى {lightred}{2}{default}!",
|
|
||||||
"fun_menu_resize": "تغيير الحجم",
|
|
||||||
"fun_menu_resize_player": "تغيير الحجم: {0}",
|
|
||||||
"fun_menu_resize_value": "الحجم {0}"
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"fun_category_name": "Spaß-Befehle",
|
|
||||||
|
|
||||||
"fun_admin_give_message": "{lightred}{0}{default} hat {lightred}{1}{default} ein {lightred}{2}{default} gegeben!",
|
|
||||||
"fun_admin_strip_message": "{lightred}{0}{default} hat alle Waffen von Spieler {lightred}{1}{default} entfernt!",
|
|
||||||
"fun_admin_hp_message": "{lightred}{0}{default} hat die Lebenspunkte von {lightred}{1}{default} geändert!",
|
|
||||||
"fun_admin_speed_message": "{lightred}{0}{default} hat die Geschwindigkeit von {lightred}{1}{default} geändert!",
|
|
||||||
"fun_admin_gravity_message": "{lightred}{0}{default} hat die Schwerkraft von {lightred}{1}{default} geändert!",
|
|
||||||
"fun_admin_money_message": "{lightred}{0}{default} hat das Geld von {lightred}{1}{default} geändert!",
|
|
||||||
"fun_admin_god_message": "{lightred}{0}{default} hat den Gottmodus von {lightred}{1}{default} geändert!",
|
|
||||||
"fun_admin_noclip_message": "{lightred}{0}{default} hat den Noclip-Modus für {lightred}{1}{default} aktiviert/deaktiviert!",
|
|
||||||
"fun_admin_freeze_message": "{lightred}{0}{default} hat {lightred}{1}{default} eingefroren!",
|
|
||||||
"fun_admin_unfreeze_message": "{lightred}{0}{default} hat {lightred}{1}{default} aufgetaut!",
|
|
||||||
"fun_admin_respawn_message": "{lightred}{0}{default} hat {lightred}{1}{default} wiederbelebt!",
|
|
||||||
|
|
||||||
"fun_menu_god": "Gottmodus",
|
|
||||||
"fun_menu_noclip": "Noclip",
|
|
||||||
"fun_menu_respawn": "Wiederbeleben",
|
|
||||||
"fun_menu_give": "Waffe Geben",
|
|
||||||
"fun_menu_give_player": "Waffe Geben: {0}",
|
|
||||||
"fun_menu_strip": "Waffen Entfernen",
|
|
||||||
"fun_menu_freeze": "Einfrieren",
|
|
||||||
"fun_menu_hp": "HP Festlegen",
|
|
||||||
"fun_menu_hp_player": "HP Festlegen: {0}",
|
|
||||||
"fun_menu_speed": "Geschwindigkeit Festlegen",
|
|
||||||
"fun_menu_speed_player": "Geschwindigkeit Festlegen: {0}",
|
|
||||||
"fun_menu_gravity": "Schwerkraft Festlegen",
|
|
||||||
"fun_menu_gravity_player": "Schwerkraft Festlegen: {0}",
|
|
||||||
"fun_menu_money": "Geld Festlegen",
|
|
||||||
"fun_menu_money_player": "Geld Festlegen: {0}",
|
|
||||||
"fun_menu_hp_value": "{0} HP",
|
|
||||||
"fun_menu_speed_value": "Geschwindigkeit {0}",
|
|
||||||
"fun_menu_gravity_value": "Schwerkraft {0}",
|
|
||||||
"fun_menu_money_value": "${0}",
|
|
||||||
|
|
||||||
"fun_admin_resize_message": "{lightred}{0}{default} hat die Größe von {lightred}{1}{default} auf {lightred}{2}{default} geändert!",
|
|
||||||
"fun_menu_resize": "Größe Ändern",
|
|
||||||
"fun_menu_resize_player": "Größe Ändern: {0}",
|
|
||||||
"fun_menu_resize_value": "Größe {0}"
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"fun_category_name": "Fun Commands",
|
|
||||||
|
|
||||||
"fun_admin_give_message": "{lightred}{0}{default} gave {lightred}{1}{default} a {lightred}{2}{default}!",
|
|
||||||
"fun_admin_strip_message": "{lightred}{0}{default} took all of player {lightred}{1}{default} weapons!",
|
|
||||||
"fun_admin_hp_message": "{lightred}{0}{default} changed {lightred}{1}{default} hp amount!",
|
|
||||||
"fun_admin_speed_message": "{lightred}{0}{default} changed speed for {lightred}{1}{default}!",
|
|
||||||
"fun_admin_gravity_message": "{lightred}{0}{default} changed gravity for {lightred}{1}{default}!",
|
|
||||||
"fun_admin_money_message": "{lightred}{0}{default} changed money for {lightred}{1}{default}!",
|
|
||||||
"fun_admin_god_message": "{lightred}{0}{default} changed god mode for {lightred}{1}{default}!",
|
|
||||||
"fun_admin_noclip_message": "{lightred}{0}{default} toggled noclip for {lightred}{1}{default}!",
|
|
||||||
"fun_admin_freeze_message": "{lightred}{0}{default} froze {lightred}{1}{default}!",
|
|
||||||
"fun_admin_unfreeze_message": "{lightred}{0}{default} unfroze {lightred}{1}{default}!",
|
|
||||||
"fun_admin_respawn_message": "{lightred}{0}{default} respawned {lightred}{1}{default}!",
|
|
||||||
|
|
||||||
"fun_menu_god": "God Mode",
|
|
||||||
"fun_menu_noclip": "No Clip",
|
|
||||||
"fun_menu_respawn": "Respawn",
|
|
||||||
"fun_menu_give": "Give Weapon",
|
|
||||||
"fun_menu_give_player": "Give Weapon: {0}",
|
|
||||||
"fun_menu_strip": "Strip Weapons",
|
|
||||||
"fun_menu_freeze": "Freeze",
|
|
||||||
"fun_menu_hp": "Set HP",
|
|
||||||
"fun_menu_hp_player": "Set HP: {0}",
|
|
||||||
"fun_menu_speed": "Set Speed",
|
|
||||||
"fun_menu_speed_player": "Set Speed: {0}",
|
|
||||||
"fun_menu_gravity": "Set Gravity",
|
|
||||||
"fun_menu_gravity_player": "Set Gravity: {0}",
|
|
||||||
"fun_menu_money": "Set Money",
|
|
||||||
"fun_menu_money_player": "Set Money: {0}",
|
|
||||||
"fun_menu_hp_value": "{0} HP",
|
|
||||||
"fun_menu_speed_value": "Speed {0}",
|
|
||||||
"fun_menu_gravity_value": "Gravity {0}",
|
|
||||||
"fun_menu_money_value": "${0}",
|
|
||||||
|
|
||||||
"fun_admin_resize_message": "{lightred}{0}{default} resized {lightred}{1}{default} to {lightred}{2}{default}!",
|
|
||||||
"fun_menu_resize": "Resize Player",
|
|
||||||
"fun_menu_resize_player": "Resize: {0}",
|
|
||||||
"fun_menu_resize_value": "Size {0}"
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"fun_category_name": "Comandos Divertidos",
|
|
||||||
|
|
||||||
"fun_admin_give_message": "{lightred}{0}{default} dio {lightred}{1}{default} un {lightred}{2}{default}!",
|
|
||||||
"fun_admin_strip_message": "{lightred}{0}{default} quitó todas las armas del jugador {lightred}{1}{default}!",
|
|
||||||
"fun_admin_hp_message": "{lightred}{0}{default} cambió la cantidad de HP de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_speed_message": "{lightred}{0}{default} cambió la velocidad de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_gravity_message": "{lightred}{0}{default} cambió la gravedad de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_money_message": "{lightred}{0}{default} cambió el dinero de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_god_message": "{lightred}{0}{default} cambió el modo dios de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_noclip_message": "{lightred}{0}{default} alternó noclip para {lightred}{1}{default}!",
|
|
||||||
"fun_admin_freeze_message": "{lightred}{0}{default} congeló a {lightred}{1}{default}!",
|
|
||||||
"fun_admin_unfreeze_message": "{lightred}{0}{default} descongeló a {lightred}{1}{default}!",
|
|
||||||
"fun_admin_respawn_message": "{lightred}{0}{default} reapareció a {lightred}{1}{default}!",
|
|
||||||
|
|
||||||
"fun_menu_god": "Modo Dios",
|
|
||||||
"fun_menu_noclip": "Noclip",
|
|
||||||
"fun_menu_respawn": "Reaparecer",
|
|
||||||
"fun_menu_give": "Dar Arma",
|
|
||||||
"fun_menu_give_player": "Dar Arma: {0}",
|
|
||||||
"fun_menu_strip": "Quitar Armas",
|
|
||||||
"fun_menu_freeze": "Congelar",
|
|
||||||
"fun_menu_hp": "Establecer HP",
|
|
||||||
"fun_menu_hp_player": "Establecer HP: {0}",
|
|
||||||
"fun_menu_speed": "Establecer Velocidad",
|
|
||||||
"fun_menu_speed_player": "Establecer Velocidad: {0}",
|
|
||||||
"fun_menu_gravity": "Establecer Gravedad",
|
|
||||||
"fun_menu_gravity_player": "Establecer Gravedad: {0}",
|
|
||||||
"fun_menu_money": "Establecer Dinero",
|
|
||||||
"fun_menu_money_player": "Establecer Dinero: {0}",
|
|
||||||
"fun_menu_hp_value": "{0} HP",
|
|
||||||
"fun_menu_speed_value": "Velocidad {0}",
|
|
||||||
"fun_menu_gravity_value": "Gravedad {0}",
|
|
||||||
"fun_menu_money_value": "${0}",
|
|
||||||
|
|
||||||
"fun_admin_resize_message": "{lightred}{0}{default} cambió el tamaño de {lightred}{1}{default} a {lightred}{2}{default}!",
|
|
||||||
"fun_menu_resize": "Cambiar Tamaño",
|
|
||||||
"fun_menu_resize_player": "Cambiar Tamaño: {0}",
|
|
||||||
"fun_menu_resize_value": "Tamaño {0}"
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"fun_category_name": "دستورات سرگرمی",
|
|
||||||
|
|
||||||
"fun_admin_give_message": "{lightred}{0}{default} {lightred}{2}{default} را به {lightred}{1}{default} داد!",
|
|
||||||
"fun_admin_strip_message": "{lightred}{0}{default} تمام سلاحهای بازیکن {lightred}{1}{default} را گرفت!",
|
|
||||||
"fun_admin_hp_message": "{lightred}{0}{default} مقدار سلامت {lightred}{1}{default} را تغییر داد!",
|
|
||||||
"fun_admin_speed_message": "{lightred}{0}{default} سرعت {lightred}{1}{default} را تغییر داد!",
|
|
||||||
"fun_admin_gravity_message": "{lightred}{0}{default} جاذبه {lightred}{1}{default} را تغییر داد!",
|
|
||||||
"fun_admin_money_message": "{lightred}{0}{default} پول {lightred}{1}{default} را تغییر داد!",
|
|
||||||
"fun_admin_god_message": "{lightred}{0}{default} حالت خدا را برای {lightred}{1}{default} تغییر داد!",
|
|
||||||
"fun_admin_noclip_message": "{lightred}{0}{default} ناپدیدی را برای {lightred}{1}{default} فعال/غیرفعال کرد!",
|
|
||||||
"fun_admin_freeze_message": "{lightred}{0}{default} {lightred}{1}{default} را یخزده کرد!",
|
|
||||||
"fun_admin_unfreeze_message": "{lightred}{0}{default} {lightred}{1}{default} را از حالت یخ خارج کرد!",
|
|
||||||
"fun_admin_respawn_message": "{lightred}{0}{default} {lightred}{1}{default} را دوباره زنده کرد!",
|
|
||||||
|
|
||||||
"fun_menu_god": "حالت خدا",
|
|
||||||
"fun_menu_noclip": "ناپدیدی",
|
|
||||||
"fun_menu_respawn": "احیا",
|
|
||||||
"fun_menu_give": "دادن سلاح",
|
|
||||||
"fun_menu_give_player": "دادن سلاح: {0}",
|
|
||||||
"fun_menu_strip": "گرفتن سلاحها",
|
|
||||||
"fun_menu_freeze": "یخ زدن",
|
|
||||||
"fun_menu_hp": "تنظیم سلامت",
|
|
||||||
"fun_menu_hp_player": "تنظیم سلامت: {0}",
|
|
||||||
"fun_menu_speed": "تنظیم سرعت",
|
|
||||||
"fun_menu_speed_player": "تنظیم سرعت: {0}",
|
|
||||||
"fun_menu_gravity": "تنظیم جاذبه",
|
|
||||||
"fun_menu_gravity_player": "تنظیم جاذبه: {0}",
|
|
||||||
"fun_menu_money": "تنظیم پول",
|
|
||||||
"fun_menu_money_player": "تنظیم پول: {0}",
|
|
||||||
"fun_menu_hp_value": "{0} HP",
|
|
||||||
"fun_menu_speed_value": "سرعت {0}",
|
|
||||||
"fun_menu_gravity_value": "جاذبه {0}",
|
|
||||||
"fun_menu_money_value": "${0}",
|
|
||||||
|
|
||||||
"fun_admin_resize_message": "{lightred}{0}{default} اندازه {lightred}{1}{default} را به {lightred}{2}{default} تغییر داد!",
|
|
||||||
"fun_menu_resize": "تغییر اندازه",
|
|
||||||
"fun_menu_resize_player": "تغییر اندازه: {0}",
|
|
||||||
"fun_menu_resize_value": "اندازه {0}"
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"fun_category_name": "Commandes Amusantes",
|
|
||||||
|
|
||||||
"fun_admin_give_message": "{lightred}{0}{default} a donné {lightred}{2}{default} à {lightred}{1}{default}!",
|
|
||||||
"fun_admin_strip_message": "{lightred}{0}{default} a retiré toutes les armes de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_hp_message": "{lightred}{0}{default} a modifié la quantité de HP de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_speed_message": "{lightred}{0}{default} a modifié la vitesse de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_gravity_message": "{lightred}{0}{default} a modifié la gravité de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_money_message": "{lightred}{0}{default} a modifié l'argent de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_god_message": "{lightred}{0}{default} a modifié le mode dieu de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_noclip_message": "{lightred}{0}{default} a activé/désactivé le noclip pour {lightred}{1}{default}!",
|
|
||||||
"fun_admin_freeze_message": "{lightred}{0}{default} a gelé {lightred}{1}{default}!",
|
|
||||||
"fun_admin_unfreeze_message": "{lightred}{0}{default} a dégivré {lightred}{1}{default}!",
|
|
||||||
"fun_admin_respawn_message": "{lightred}{0}{default} a réapparu {lightred}{1}{default}!",
|
|
||||||
|
|
||||||
"fun_menu_god": "Mode Dieu",
|
|
||||||
"fun_menu_noclip": "Noclip",
|
|
||||||
"fun_menu_respawn": "Réapparition",
|
|
||||||
"fun_menu_give": "Donner Arme",
|
|
||||||
"fun_menu_give_player": "Donner Arme: {0}",
|
|
||||||
"fun_menu_strip": "Retirer Armes",
|
|
||||||
"fun_menu_freeze": "Geler",
|
|
||||||
"fun_menu_hp": "Définir HP",
|
|
||||||
"fun_menu_hp_player": "Définir HP: {0}",
|
|
||||||
"fun_menu_speed": "Définir Vitesse",
|
|
||||||
"fun_menu_speed_player": "Définir Vitesse: {0}",
|
|
||||||
"fun_menu_gravity": "Définir Gravité",
|
|
||||||
"fun_menu_gravity_player": "Définir Gravité: {0}",
|
|
||||||
"fun_menu_money": "Définir Argent",
|
|
||||||
"fun_menu_money_player": "Définir Argent: {0}",
|
|
||||||
"fun_menu_hp_value": "{0} HP",
|
|
||||||
"fun_menu_speed_value": "Vitesse {0}",
|
|
||||||
"fun_menu_gravity_value": "Gravité {0}",
|
|
||||||
"fun_menu_money_value": "${0}",
|
|
||||||
|
|
||||||
"fun_admin_resize_message": "{lightred}{0}{default} a redimensionné {lightred}{1}{default} à {lightred}{2}{default}!",
|
|
||||||
"fun_menu_resize": "Redimensionner",
|
|
||||||
"fun_menu_resize_player": "Redimensionner: {0}",
|
|
||||||
"fun_menu_resize_value": "Taille {0}"
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"fun_category_name": "Izklaidējošas Komandas",
|
|
||||||
|
|
||||||
"fun_admin_give_message": "{lightred}{0}{default} iedeva {lightred}{1}{default} {lightred}{2}{default}!",
|
|
||||||
"fun_admin_strip_message": "{lightred}{0}{default} noņēma visus {lightred}{1}{default} ieročus!",
|
|
||||||
"fun_admin_hp_message": "{lightred}{0}{default} mainīja {lightred}{1}{default} HP daudzumu!",
|
|
||||||
"fun_admin_speed_message": "{lightred}{0}{default} mainīja {lightred}{1}{default} ātrumu!",
|
|
||||||
"fun_admin_gravity_message": "{lightred}{0}{default} mainīja {lightred}{1}{default} gravitāciju!",
|
|
||||||
"fun_admin_money_message": "{lightred}{0}{default} mainīja {lightred}{1}{default} naudu!",
|
|
||||||
"fun_admin_god_message": "{lightred}{0}{default} mainīja dieva režīmu priekš {lightred}{1}{default}!",
|
|
||||||
"fun_admin_noclip_message": "{lightred}{0}{default} aktivizēja/deaktivizēja noclip priekš {lightred}{1}{default}!",
|
|
||||||
"fun_admin_freeze_message": "{lightred}{0}{default} sasaldēja {lightred}{1}{default}!",
|
|
||||||
"fun_admin_unfreeze_message": "{lightred}{0}{default} atkausēja {lightred}{1}{default}!",
|
|
||||||
"fun_admin_respawn_message": "{lightred}{0}{default} atdzīvināja {lightred}{1}{default}!",
|
|
||||||
|
|
||||||
"fun_menu_god": "Dieva Režīms",
|
|
||||||
"fun_menu_noclip": "Noclip",
|
|
||||||
"fun_menu_respawn": "Atdzīvināt",
|
|
||||||
"fun_menu_give": "Dot Ieroci",
|
|
||||||
"fun_menu_give_player": "Dot Ieroci: {0}",
|
|
||||||
"fun_menu_strip": "Noņemt Ieročus",
|
|
||||||
"fun_menu_freeze": "Sasaldēt",
|
|
||||||
"fun_menu_hp": "Uzstādīt HP",
|
|
||||||
"fun_menu_hp_player": "Uzstādīt HP: {0}",
|
|
||||||
"fun_menu_speed": "Uzstādīt Ātrumu",
|
|
||||||
"fun_menu_speed_player": "Uzstādīt Ātrumu: {0}",
|
|
||||||
"fun_menu_gravity": "Uzstādīt Gravitāciju",
|
|
||||||
"fun_menu_gravity_player": "Uzstādīt Gravitāciju: {0}",
|
|
||||||
"fun_menu_money": "Uzstādīt Naudu",
|
|
||||||
"fun_menu_money_player": "Uzstādīt Naudu: {0}",
|
|
||||||
"fun_menu_hp_value": "{0} HP",
|
|
||||||
"fun_menu_speed_value": "Ātrums {0}",
|
|
||||||
"fun_menu_gravity_value": "Gravitācija {0}",
|
|
||||||
"fun_menu_money_value": "${0}",
|
|
||||||
|
|
||||||
"fun_admin_resize_message": "{lightred}{0}{default} mainīja {lightred}{1}{default} izmēru uz {lightred}{2}{default}!",
|
|
||||||
"fun_menu_resize": "Mainīt Izmēru",
|
|
||||||
"fun_menu_resize_player": "Mainīt Izmēru: {0}",
|
|
||||||
"fun_menu_resize_value": "Izmērs {0}"
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"fun_category_name": "Komendy Rozrywkowe",
|
|
||||||
|
|
||||||
"fun_admin_give_message": "{lightred}{0}{default} dał {lightred}{1}{default} przedmiot {lightred}{2}{default}!",
|
|
||||||
"fun_admin_strip_message": "{lightred}{0}{default} zabrał wszystkie bronie {lightred}{1}{default}!",
|
|
||||||
"fun_admin_hp_message": "{lightred}{0}{default} zmienił ilość hp dla {lightred}{1}{default}!",
|
|
||||||
"fun_admin_speed_message": "{lightred}{0}{default} zmienił prędkość dla {lightred}{1}{default}!",
|
|
||||||
"fun_admin_gravity_message": "{lightred}{0}{default} zmienił grawitację dla {lightred}{1}{default}!",
|
|
||||||
"fun_admin_money_message": "{lightred}{0}{default} zmienił pieniądze dla {lightred}{1}{default}!",
|
|
||||||
"fun_admin_god_message": "{lightred}{0}{default} zmienił tryb Boga dla {lightred}{1}{default}!",
|
|
||||||
"fun_admin_noclip_message": "{lightred}{0}{default} ustawił latanie dla {lightred}{1}{default}!",
|
|
||||||
"fun_admin_freeze_message": "{lightred}{0}{default} zamroził {lightred}{1}{default}!",
|
|
||||||
"fun_admin_unfreeze_message": "{lightred}{0}{default} odmroził {lightred}{1}{default}!",
|
|
||||||
"fun_admin_respawn_message": "{lightred}{0}{default} odrodził {lightred}{1}{default}!",
|
|
||||||
|
|
||||||
"fun_menu_god": "Tryb Boga",
|
|
||||||
"fun_menu_noclip": "Latanie",
|
|
||||||
"fun_menu_respawn": "Odrodzenie",
|
|
||||||
"fun_menu_give": "Daj Broń",
|
|
||||||
"fun_menu_give_player": "Daj Broń: {0}",
|
|
||||||
"fun_menu_strip": "Zabierz Bronie",
|
|
||||||
"fun_menu_freeze": "Zamrożenie",
|
|
||||||
"fun_menu_hp": "Ustaw HP",
|
|
||||||
"fun_menu_hp_player": "Ustaw HP: {0}",
|
|
||||||
"fun_menu_speed": "Ustaw Prędkość",
|
|
||||||
"fun_menu_speed_player": "Ustaw Prędkość: {0}",
|
|
||||||
"fun_menu_gravity": "Ustaw Grawitację",
|
|
||||||
"fun_menu_gravity_player": "Ustaw Grawitację: {0}",
|
|
||||||
"fun_menu_money": "Ustaw Pieniądze",
|
|
||||||
"fun_menu_money_player": "Ustaw Pieniądze: {0}",
|
|
||||||
"fun_menu_hp_value": "{0} HP",
|
|
||||||
"fun_menu_speed_value": "Prędkość {0}",
|
|
||||||
"fun_menu_gravity_value": "Grawitacja {0}",
|
|
||||||
"fun_menu_money_value": "${0}",
|
|
||||||
|
|
||||||
"fun_admin_resize_message": "{lightred}{0}{default} zmienił rozmiar {lightred}{1}{default} na {lightred}{2}{default}!",
|
|
||||||
"fun_menu_resize": "Zmień Rozmiar",
|
|
||||||
"fun_menu_resize_player": "Zmień Rozmiar: {0}",
|
|
||||||
"fun_menu_resize_value": "Rozmiar {0}"
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"fun_category_name": "Comandos Divertidos",
|
|
||||||
|
|
||||||
"fun_admin_give_message": "{lightred}{0}{default} deu {lightred}{1}{default} um(a) {lightred}{2}{default}!",
|
|
||||||
"fun_admin_strip_message": "{lightred}{0}{default} tirou todas as armas de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_hp_message": "{lightred}{0}{default} mudou a quantidade de HP de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_speed_message": "{lightred}{0}{default} mudou a velocidade de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_gravity_message": "{lightred}{0}{default} mudou a gravidade de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_money_message": "{lightred}{0}{default} mudou a quantidade de dinheiro de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_god_message": "{lightred}{0}{default} mudou o modo Deus de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_noclip_message": "{lightred}{0}{default} ativou/desativou o noclip para {lightred}{1}{default}!",
|
|
||||||
"fun_admin_freeze_message": "{lightred}{0}{default} congelou {lightred}{1}{default}!",
|
|
||||||
"fun_admin_unfreeze_message": "{lightred}{0}{default} descongelou {lightred}{1}{default}!",
|
|
||||||
"fun_admin_respawn_message": "{lightred}{0}{default} reanimou {lightred}{1}{default}!",
|
|
||||||
|
|
||||||
"fun_menu_god": "Modo Deus",
|
|
||||||
"fun_menu_noclip": "Noclip",
|
|
||||||
"fun_menu_respawn": "Reanimar",
|
|
||||||
"fun_menu_give": "Dar Arma",
|
|
||||||
"fun_menu_give_player": "Dar Arma: {0}",
|
|
||||||
"fun_menu_strip": "Remover Armas",
|
|
||||||
"fun_menu_freeze": "Congelar",
|
|
||||||
"fun_menu_hp": "Definir HP",
|
|
||||||
"fun_menu_hp_player": "Definir HP: {0}",
|
|
||||||
"fun_menu_speed": "Definir Velocidade",
|
|
||||||
"fun_menu_speed_player": "Definir Velocidade: {0}",
|
|
||||||
"fun_menu_gravity": "Definir Gravidade",
|
|
||||||
"fun_menu_gravity_player": "Definir Gravidade: {0}",
|
|
||||||
"fun_menu_money": "Definir Dinheiro",
|
|
||||||
"fun_menu_money_player": "Definir Dinheiro: {0}",
|
|
||||||
"fun_menu_hp_value": "{0} HP",
|
|
||||||
"fun_menu_speed_value": "Velocidade {0}",
|
|
||||||
"fun_menu_gravity_value": "Gravidade {0}",
|
|
||||||
"fun_menu_money_value": "${0}",
|
|
||||||
|
|
||||||
"fun_admin_resize_message": "{lightred}{0}{default} redimensionou {lightred}{1}{default} para {lightred}{2}{default}!",
|
|
||||||
"fun_menu_resize": "Redimensionar",
|
|
||||||
"fun_menu_resize_player": "Redimensionar: {0}",
|
|
||||||
"fun_menu_resize_value": "Tamanho {0}"
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"fun_category_name": "Comandos Divertidos",
|
|
||||||
|
|
||||||
"fun_admin_give_message": "{lightred}{0}{default} deu {lightred}{1}{default} um(a) {lightred}{2}{default}!",
|
|
||||||
"fun_admin_strip_message": "{lightred}{0}{default} tirou todas as armas de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_hp_message": "{lightred}{0}{default} mudou a quantidade de HP de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_speed_message": "{lightred}{0}{default} mudou a velocidade de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_gravity_message": "{lightred}{0}{default} mudou a gravidade de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_money_message": "{lightred}{0}{default} mudou a quantidade de dinheiro de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_god_message": "{lightred}{0}{default} mudou o modo Deus de {lightred}{1}{default}!",
|
|
||||||
"fun_admin_noclip_message": "{lightred}{0}{default} ativou/desativou o noclip para {lightred}{1}{default}!",
|
|
||||||
"fun_admin_freeze_message": "{lightred}{0}{default} congelou {lightred}{1}{default}!",
|
|
||||||
"fun_admin_unfreeze_message": "{lightred}{0}{default} descongelou {lightred}{1}{default}!",
|
|
||||||
"fun_admin_respawn_message": "{lightred}{0}{default} reanimou {lightred}{1}{default}!",
|
|
||||||
|
|
||||||
"fun_menu_god": "Modo Deus",
|
|
||||||
"fun_menu_noclip": "Noclip",
|
|
||||||
"fun_menu_respawn": "Reanimar",
|
|
||||||
"fun_menu_give": "Dar Arma",
|
|
||||||
"fun_menu_give_player": "Dar Arma: {0}",
|
|
||||||
"fun_menu_strip": "Remover Armas",
|
|
||||||
"fun_menu_freeze": "Congelar",
|
|
||||||
"fun_menu_hp": "Definir HP",
|
|
||||||
"fun_menu_hp_player": "Definir HP: {0}",
|
|
||||||
"fun_menu_speed": "Definir Velocidade",
|
|
||||||
"fun_menu_speed_player": "Definir Velocidade: {0}",
|
|
||||||
"fun_menu_gravity": "Definir Gravidade",
|
|
||||||
"fun_menu_gravity_player": "Definir Gravidade: {0}",
|
|
||||||
"fun_menu_money": "Definir Dinheiro",
|
|
||||||
"fun_menu_money_player": "Definir Dinheiro: {0}",
|
|
||||||
"fun_menu_hp_value": "{0} HP",
|
|
||||||
"fun_menu_speed_value": "Velocidade {0}",
|
|
||||||
"fun_menu_gravity_value": "Gravidade {0}",
|
|
||||||
"fun_menu_money_value": "${0}",
|
|
||||||
|
|
||||||
"fun_admin_resize_message": "{lightred}{0}{default} redimensionou {lightred}{1}{default} para {lightred}{2}{default}!",
|
|
||||||
"fun_menu_resize": "Redimensionar",
|
|
||||||
"fun_menu_resize_player": "Redimensionar: {0}",
|
|
||||||
"fun_menu_resize_value": "Tamanho {0}"
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"fun_category_name": "Развлекательные Команды",
|
|
||||||
|
|
||||||
"fun_admin_give_message": "{lightred}{0}{default} дал {lightred}{1}{default} {lightred}{2}{default}!",
|
|
||||||
"fun_admin_strip_message": "{lightred}{0}{default} забрал все оружие у {lightred}{1}{default}!",
|
|
||||||
"fun_admin_hp_message": "{lightred}{0}{default} изменил количество HP у {lightred}{1}{default}!",
|
|
||||||
"fun_admin_speed_message": "{lightred}{0}{default} изменил скорость {lightred}{1}{default}!",
|
|
||||||
"fun_admin_gravity_message": "{lightred}{0}{default} изменил гравитацию для {lightred}{1}{default}!",
|
|
||||||
"fun_admin_money_message": "{lightred}{0}{default} изменил количество денег у {lightred}{1}{default}!",
|
|
||||||
"fun_admin_god_message": "{lightred}{0}{default} изменил режим бога для {lightred}{1}{default}!",
|
|
||||||
"fun_admin_noclip_message": "{lightred}{0}{default} включил/выключил noclip для {lightred}{1}{default}!",
|
|
||||||
"fun_admin_freeze_message": "{lightred}{0}{default} заморозил {lightred}{1}{default}!",
|
|
||||||
"fun_admin_unfreeze_message": "{lightred}{0}{default} разморозил {lightred}{1}{default}!",
|
|
||||||
"fun_admin_respawn_message": "{lightred}{0}{default} возродил {lightred}{1}{default}!",
|
|
||||||
|
|
||||||
"fun_menu_god": "Режим Бога",
|
|
||||||
"fun_menu_noclip": "Noclip",
|
|
||||||
"fun_menu_respawn": "Возрождение",
|
|
||||||
"fun_menu_give": "Выдать Оружие",
|
|
||||||
"fun_menu_give_player": "Выдать Оружие: {0}",
|
|
||||||
"fun_menu_strip": "Забрать Оружие",
|
|
||||||
"fun_menu_freeze": "Заморозить",
|
|
||||||
"fun_menu_hp": "Установить HP",
|
|
||||||
"fun_menu_hp_player": "Установить HP: {0}",
|
|
||||||
"fun_menu_speed": "Установить Скорость",
|
|
||||||
"fun_menu_speed_player": "Установить Скорость: {0}",
|
|
||||||
"fun_menu_gravity": "Установить Гравитацию",
|
|
||||||
"fun_menu_gravity_player": "Установить Гравитацию: {0}",
|
|
||||||
"fun_menu_money": "Установить Деньги",
|
|
||||||
"fun_menu_money_player": "Установить Деньги: {0}",
|
|
||||||
"fun_menu_hp_value": "{0} HP",
|
|
||||||
"fun_menu_speed_value": "Скорость {0}",
|
|
||||||
"fun_menu_gravity_value": "Гравитация {0}",
|
|
||||||
"fun_menu_money_value": "${0}",
|
|
||||||
|
|
||||||
"fun_admin_resize_message": "{lightred}{0}{default} изменил размер {lightred}{1}{default} на {lightred}{2}{default}!",
|
|
||||||
"fun_menu_resize": "Изменить Размер",
|
|
||||||
"fun_menu_resize_player": "Изменить Размер: {0}",
|
|
||||||
"fun_menu_resize_value": "Размер {0}"
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"fun_category_name": "Eğlence Komutları",
|
|
||||||
|
|
||||||
"fun_admin_give_message": "{lightred}{0}{default} {lightred}{1}{default}'e {lightred}{2}{default} verdi!",
|
|
||||||
"fun_admin_strip_message": "{lightred}{0}{default} {lightred}{1}{default}'in tüm silahlarını aldı!",
|
|
||||||
"fun_admin_hp_message": "{lightred}{0}{default} {lightred}{1}{default}'in HP miktarını değiştirdi!",
|
|
||||||
"fun_admin_speed_message": "{lightred}{0}{default} {lightred}{1}{default}'in hızını değiştirdi!",
|
|
||||||
"fun_admin_gravity_message": "{lightred}{0}{default} {lightred}{1}{default}'in yer çekimini değiştirdi!",
|
|
||||||
"fun_admin_money_message": "{lightred}{0}{default} {lightred}{1}{default}'in parasını değiştirdi!",
|
|
||||||
"fun_admin_god_message": "{lightred}{0}{default} {lightred}{1}{default}'in tanrı modunu değiştirdi!",
|
|
||||||
"fun_admin_noclip_message": "{lightred}{0}{default} {lightred}{1}{default} için noclip'i açtı/kapatı!",
|
|
||||||
"fun_admin_freeze_message": "{lightred}{0}{default} {lightred}{1}{default}'i dondurdu!",
|
|
||||||
"fun_admin_unfreeze_message": "{lightred}{0}{default} {lightred}{1}{default}'in dondurmasını çözdü!",
|
|
||||||
"fun_admin_respawn_message": "{lightred}{0}{default} {lightred}{1}{default}'i yeniden doğurdu!",
|
|
||||||
|
|
||||||
"fun_menu_god": "Tanrı Modu",
|
|
||||||
"fun_menu_noclip": "Noclip",
|
|
||||||
"fun_menu_respawn": "Yeniden Doğma",
|
|
||||||
"fun_menu_give": "Silah Ver",
|
|
||||||
"fun_menu_give_player": "Silah Ver: {0}",
|
|
||||||
"fun_menu_strip": "Silahları Al",
|
|
||||||
"fun_menu_freeze": "Dondur",
|
|
||||||
"fun_menu_hp": "HP Ayarla",
|
|
||||||
"fun_menu_hp_player": "HP Ayarla: {0}",
|
|
||||||
"fun_menu_speed": "Hız Ayarla",
|
|
||||||
"fun_menu_speed_player": "Hız Ayarla: {0}",
|
|
||||||
"fun_menu_gravity": "Yer Çekimi Ayarla",
|
|
||||||
"fun_menu_gravity_player": "Yer Çekimi Ayarla: {0}",
|
|
||||||
"fun_menu_money": "Para Ayarla",
|
|
||||||
"fun_menu_money_player": "Para Ayarla: {0}",
|
|
||||||
"fun_menu_hp_value": "{0} HP",
|
|
||||||
"fun_menu_speed_value": "Hız {0}",
|
|
||||||
"fun_menu_gravity_value": "Yer Çekimi {0}",
|
|
||||||
"fun_menu_money_value": "${0}",
|
|
||||||
|
|
||||||
"fun_admin_resize_message": "{lightred}{0}{default} {lightred}{1}{default}'in boyutunu {lightred}{2}{default} olarak değiştirdi!",
|
|
||||||
"fun_menu_resize": "Boyut Değiştir",
|
|
||||||
"fun_menu_resize_player": "Boyut Değiştir: {0}",
|
|
||||||
"fun_menu_resize_value": "Boyut {0}"
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"fun_category_name": "趣味命令",
|
|
||||||
|
|
||||||
"fun_admin_give_message": "{lightred}{0}{default} 给了 {lightred}{1}{default} {lightred}{2}{default}!",
|
|
||||||
"fun_admin_strip_message": "{lightred}{0}{default} 移除了 {lightred}{1}{default} 的所有武器!",
|
|
||||||
"fun_admin_hp_message": "{lightred}{0}{default} 修改了 {lightred}{1}{default} 的生命值!",
|
|
||||||
"fun_admin_speed_message": "{lightred}{0}{default} 修改了 {lightred}{1}{default} 的速度!",
|
|
||||||
"fun_admin_gravity_message": "{lightred}{0}{default} 修改了 {lightred}{1}{default} 的重力!",
|
|
||||||
"fun_admin_money_message": "{lightred}{0}{default} 修改了 {lightred}{1}{default} 的金钱!",
|
|
||||||
"fun_admin_god_message": "{lightred}{0}{default} 切换了 {lightred}{1}{default} 的上帝模式!",
|
|
||||||
"fun_admin_noclip_message": "{lightred}{0}{default} 切换了 {lightred}{1}{default} 的穿墙模式!",
|
|
||||||
"fun_admin_freeze_message": "{lightred}{0}{default} 冻结了 {lightred}{1}{default}!",
|
|
||||||
"fun_admin_unfreeze_message": "{lightred}{0}{default} 解冻了 {lightred}{1}{default}!",
|
|
||||||
"fun_admin_respawn_message": "{lightred}{0}{default} 复活了 {lightred}{1}{default}!",
|
|
||||||
|
|
||||||
"fun_menu_god": "上帝模式",
|
|
||||||
"fun_menu_noclip": "穿墙模式",
|
|
||||||
"fun_menu_respawn": "复活",
|
|
||||||
"fun_menu_give": "给予武器",
|
|
||||||
"fun_menu_give_player": "给予武器: {0}",
|
|
||||||
"fun_menu_strip": "移除武器",
|
|
||||||
"fun_menu_freeze": "冻结",
|
|
||||||
"fun_menu_hp": "设置生命值",
|
|
||||||
"fun_menu_hp_player": "设置生命值: {0}",
|
|
||||||
"fun_menu_speed": "设置速度",
|
|
||||||
"fun_menu_speed_player": "设置速度: {0}",
|
|
||||||
"fun_menu_gravity": "设置重力",
|
|
||||||
"fun_menu_gravity_player": "设置重力: {0}",
|
|
||||||
"fun_menu_money": "设置金钱",
|
|
||||||
"fun_menu_money_player": "设置金钱: {0}",
|
|
||||||
"fun_menu_hp_value": "{0} HP",
|
|
||||||
"fun_menu_speed_value": "速度 {0}",
|
|
||||||
"fun_menu_gravity_value": "重力 {0}",
|
|
||||||
"fun_menu_money_value": "${0}",
|
|
||||||
|
|
||||||
"fun_admin_resize_message": "{lightred}{0}{default} 将 {lightred}{1}{default} 的大小改为 {lightred}{2}{default}!",
|
|
||||||
"fun_menu_resize": "调整大小",
|
|
||||||
"fun_menu_resize_player": "调整大小: {0}",
|
|
||||||
"fun_menu_resize_value": "大小 {0}"
|
|
||||||
}
|
|
||||||
@@ -1,384 +0,0 @@
|
|||||||
# CS2-SimpleAdmin Fun Commands Module
|
|
||||||
|
|
||||||
This module serves as a **reference implementation** for creating CS2-SimpleAdmin modules. It demonstrates best practices for menu creation, command registration, translation support, and API usage.
|
|
||||||
|
|
||||||
## 📚 What This Module Teaches
|
|
||||||
|
|
||||||
This module is designed to be educational and shows you how to:
|
|
||||||
|
|
||||||
1. ✅ **Register commands dynamically** from configuration
|
|
||||||
2. ✅ **Create menu categories** and menu items
|
|
||||||
3. ✅ **Use per-player translations** with `ShowAdminActivityLocalized`
|
|
||||||
4. ✅ **Handle player targeting** and validation
|
|
||||||
5. ✅ **Implement proper cleanup** on module unload
|
|
||||||
6. ✅ **Structure code** using partial classes for organization
|
|
||||||
7. ✅ **Cache data** for performance (weapons cache)
|
|
||||||
8. ✅ **Use configuration** to enable/disable features
|
|
||||||
|
|
||||||
## 🎯 Features
|
|
||||||
|
|
||||||
This module provides fun admin commands:
|
|
||||||
|
|
||||||
- **God Mode** (`css_god`) - Toggle god mode for players
|
|
||||||
- **No Clip** (`css_noclip`) - Enable no-clip mode
|
|
||||||
- **Freeze/Unfreeze** (`css_freeze`, `css_unfreeze`) - Freeze players in place
|
|
||||||
- **Respawn** (`css_respawn`) - Respawn dead players
|
|
||||||
- **Give Weapon** (`css_give`) - Give weapons to players
|
|
||||||
- **Strip Weapons** (`css_strip`) - Remove all player weapons
|
|
||||||
- **Set HP** (`css_hp`) - Set player health
|
|
||||||
- **Set Speed** (`css_speed`) - Modify player movement speed
|
|
||||||
- **Set Gravity** (`css_gravity`) - Change player gravity
|
|
||||||
- **Set Money** (`css_money`) - Set player money
|
|
||||||
|
|
||||||
## 📁 File Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
CS2-SimpleAdmin_FunCommands/
|
|
||||||
├── CS2-SimpleAdmin_FunCommands.cs # Main plugin file - initialization, registration
|
|
||||||
├── Commands.cs # Command handlers
|
|
||||||
├── Actions.cs # Action methods (God, NoClip, Freeze, etc.)
|
|
||||||
├── Menus.cs # Menu creation using SimpleAdmin API
|
|
||||||
├── Config.cs # Configuration with command lists
|
|
||||||
└── lang/ # Translation files (13 languages)
|
|
||||||
├── en.json
|
|
||||||
├── pl.json
|
|
||||||
├── ru.json
|
|
||||||
└── ... (10 more languages)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔍 Code Organization Explained
|
|
||||||
|
|
||||||
### 1. Main Plugin File (`CS2-SimpleAdmin_FunCommands.cs`)
|
|
||||||
|
|
||||||
**Key Concepts Demonstrated:**
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
public partial class CS2_SimpleAdmin_FunCommands : BasePlugin, IPluginConfig<Config>
|
|
||||||
{
|
|
||||||
// ✅ BEST PRACTICE: Use capability system to get API
|
|
||||||
private ICS2_SimpleAdminApi? _sharedApi;
|
|
||||||
private readonly PluginCapability<ICS2_SimpleAdminApi> _pluginCapability = new("simpleadmin:api");
|
|
||||||
|
|
||||||
// ✅ BEST PRACTICE: Cache expensive data
|
|
||||||
private static Dictionary<int, CsItem>? _weaponsCache;
|
|
||||||
|
|
||||||
// ✅ BEST PRACTICE: Track menu registration state
|
|
||||||
private bool _menusRegistered = false;
|
|
||||||
|
|
||||||
public override void OnAllPluginsLoaded(bool hotReload)
|
|
||||||
{
|
|
||||||
// Get the API
|
|
||||||
_sharedApi = _pluginCapability.Get();
|
|
||||||
|
|
||||||
// Register commands
|
|
||||||
RegisterFunCommands();
|
|
||||||
|
|
||||||
// ✅ BEST PRACTICE: Wait for SimpleAdmin to be ready before registering menus
|
|
||||||
_sharedApi.OnSimpleAdminReady += RegisterFunMenus;
|
|
||||||
RegisterFunMenus(); // Fallback for hot reload
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Why partial classes?**
|
|
||||||
- Separates concerns (commands, actions, menus)
|
|
||||||
- Makes code easier to navigate
|
|
||||||
- Each file has a specific purpose
|
|
||||||
|
|
||||||
### 2. Configuration (`Config.cs`)
|
|
||||||
|
|
||||||
**Key Concept:** Command lists for flexibility
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
public class Config : IBasePluginConfig
|
|
||||||
{
|
|
||||||
// ✅ BEST PRACTICE: Allow multiple command aliases
|
|
||||||
public List<string> NoclipCommands { get; set; } = ["css_noclip"];
|
|
||||||
public List<string> GodCommands { get; set; } = ["css_god"];
|
|
||||||
// ... more command lists
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits:**
|
|
||||||
- Users can disable features by emptying the list
|
|
||||||
- Users can add command aliases (e.g., `["css_god", "css_godmode"]`)
|
|
||||||
- Menus only register if commands exist
|
|
||||||
|
|
||||||
### 3. Commands (`Commands.cs`)
|
|
||||||
|
|
||||||
**Key Concepts Demonstrated:**
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
[CommandHelper(1, "<#userid or name>")]
|
|
||||||
[RequiresPermissions("@css/cheats")]
|
|
||||||
private void OnGodCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
// ✅ BEST PRACTICE: Use API to get targets (handles target syntax)
|
|
||||||
var targets = _sharedApi!.GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
// ✅ BEST PRACTICE: Filter for alive players
|
|
||||||
var playersToTarget = targets.Players.Where(player =>
|
|
||||||
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
|
||||||
|
|
||||||
// ✅ BEST PRACTICE: Check targeting permissions
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
God(caller, player);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ✅ BEST PRACTICE: Always log commands
|
|
||||||
_sharedApi.LogCommand(caller, command);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Actions (`Actions.cs`)
|
|
||||||
|
|
||||||
**Key Concepts Demonstrated:**
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
private void God(CCSPlayerController? caller, CCSPlayerController player)
|
|
||||||
{
|
|
||||||
// Perform the action
|
|
||||||
if (!GodPlayers.Add(player.Slot))
|
|
||||||
{
|
|
||||||
GodPlayers.Remove(player.Slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ BEST PRACTICE: Use per-player language support
|
|
||||||
var activityArgs = new object[] { "CALLER", player.PlayerName };
|
|
||||||
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
|
|
||||||
{
|
|
||||||
if (Localizer != null)
|
|
||||||
{
|
|
||||||
// Each player sees message in their configured language!
|
|
||||||
_sharedApi!.ShowAdminActivityLocalized(Localizer, "fun_admin_god_message", callerName, false, activityArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ BEST PRACTICE: Log the action
|
|
||||||
_sharedApi!.LogCommand(caller, $"css_god {player.PlayerName}");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Menus (`Menus.cs`)
|
|
||||||
|
|
||||||
**Key Concepts Demonstrated:**
|
|
||||||
|
|
||||||
#### Simple Player Selection Menu
|
|
||||||
```csharp
|
|
||||||
private object CreateGodModeMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
// ✅ BEST PRACTICE: Use CreateMenuWithPlayers for simple player selection
|
|
||||||
return _sharedApi!.CreateMenuWithPlayers("God Mode", "fun", admin,
|
|
||||||
player => player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(player),
|
|
||||||
God); // Direct method reference
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Nested Menu with Value Selection
|
|
||||||
```csharp
|
|
||||||
private object CreateSetHpMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
// ✅ BEST PRACTICE: Use CreateMenuWithBack for menus with back button
|
|
||||||
var menu = _sharedApi!.CreateMenuWithBack("Set HP", "fun", admin);
|
|
||||||
|
|
||||||
var players = _sharedApi.GetValidPlayers().Where(p =>
|
|
||||||
p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
// ✅ BEST PRACTICE: AddSubMenu automatically adds back button to submenu
|
|
||||||
_sharedApi.AddSubMenu(menu, playerName, p => CreateHpSelectionMenu(admin, player));
|
|
||||||
}
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateHpSelectionMenu(CCSPlayerController admin, CCSPlayerController target)
|
|
||||||
{
|
|
||||||
var hpMenu = _sharedApi!.CreateMenuWithBack($"Set HP: {target.PlayerName}", "fun", admin);
|
|
||||||
var hpValues = new[] { 1, 10, 25, 50, 100, 200, 500, 999 };
|
|
||||||
|
|
||||||
foreach (var hp in hpValues)
|
|
||||||
{
|
|
||||||
// ✅ BEST PRACTICE: AddMenuOption for simple actions
|
|
||||||
_sharedApi.AddMenuOption(hpMenu, $"{hp} HP", _ =>
|
|
||||||
{
|
|
||||||
// ✅ BEST PRACTICE: Always validate before executing
|
|
||||||
if (target.IsValid)
|
|
||||||
{
|
|
||||||
target.SetHp(hp);
|
|
||||||
LogAndShowActivity(admin, target, "fun_admin_hp_message", "css_hp", hp.ToString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return hpMenu;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. Translations
|
|
||||||
|
|
||||||
**Key Concept:** Module-specific translations
|
|
||||||
|
|
||||||
```json
|
|
||||||
// lang/en.json
|
|
||||||
{
|
|
||||||
"fun_admin_god_message": "{lightred}{0}{default} changed god mode for {lightred}{1}{default}!",
|
|
||||||
"fun_admin_hp_message": "{lightred}{0}{default} changed {lightred}{1}{default} hp amount!"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Why module translations?**
|
|
||||||
- Your module is independent from SimpleAdmin
|
|
||||||
- You can update translations without affecting main plugin
|
|
||||||
- Each player sees messages in their language automatically
|
|
||||||
|
|
||||||
## 🛠️ How to Use This as a Template
|
|
||||||
|
|
||||||
### Step 1: Copy the Module
|
|
||||||
```bash
|
|
||||||
cp -r CS2-SimpleAdmin_FunCommands YourModuleName
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 2: Rename Files
|
|
||||||
- Rename `.csproj` file
|
|
||||||
- Rename all `.cs` files to match your module name
|
|
||||||
- Update namespace in all files
|
|
||||||
|
|
||||||
### Step 3: Update References
|
|
||||||
- Change `namespace CS2_SimpleAdmin_FunCommands` to `namespace YourModuleName`
|
|
||||||
- Update plugin metadata (name, version, author, description)
|
|
||||||
|
|
||||||
### Step 4: Modify Config
|
|
||||||
```csharp
|
|
||||||
public class Config : IBasePluginConfig
|
|
||||||
{
|
|
||||||
public int Version { get; set; } = 1;
|
|
||||||
|
|
||||||
// Add your own command lists
|
|
||||||
public List<string> YourCommands { get; set; } = ["css_yourcommand"];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 5: Add Your Commands
|
|
||||||
Look at `Commands.cs` for examples of command handlers
|
|
||||||
|
|
||||||
### Step 6: Add Your Menus
|
|
||||||
Look at `Menus.cs` for examples of menu creation
|
|
||||||
|
|
||||||
### Step 7: Add Translations
|
|
||||||
Create language files in `lang/{language}.json` (e.g., `lang/en.json`, `lang/pl.json`)
|
|
||||||
|
|
||||||
## 📖 Learning Path
|
|
||||||
|
|
||||||
If you're new to module development, study files in this order:
|
|
||||||
|
|
||||||
1. **Config.cs** - Understand configuration structure
|
|
||||||
2. **CS2-SimpleAdmin_FunCommands.cs** - See initialization and API acquisition
|
|
||||||
3. **Commands.cs** - Learn command registration and handling
|
|
||||||
4. **Actions.cs** - Understand action methods and translations
|
|
||||||
5. **Menus.cs** - Study menu creation patterns
|
|
||||||
|
|
||||||
## 🎓 Best Practices Demonstrated
|
|
||||||
|
|
||||||
### ✅ Command Registration
|
|
||||||
```csharp
|
|
||||||
// Dynamic registration based on config
|
|
||||||
if (Config.GodCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.GodCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.RegisterCommand(command, "Enable god mode", OnGodCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### ✅ Target Validation
|
|
||||||
```csharp
|
|
||||||
// Always check if player can be targeted
|
|
||||||
if (!caller.CanTarget(player)) return;
|
|
||||||
|
|
||||||
// Always validate player state
|
|
||||||
if (!player.IsValid) return;
|
|
||||||
```
|
|
||||||
|
|
||||||
### ✅ Translation Usage
|
|
||||||
```csharp
|
|
||||||
// Use module's localizer for per-player language support
|
|
||||||
if (Localizer != null)
|
|
||||||
{
|
|
||||||
_sharedApi.ShowAdminActivityLocalized(Localizer, "translation_key", callerName, false, args);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### ✅ Cleanup on Unload
|
|
||||||
```csharp
|
|
||||||
public override void Unload(bool hotReload)
|
|
||||||
{
|
|
||||||
// Unregister all commands
|
|
||||||
if (Config.GodCommands.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var command in Config.GodCommands)
|
|
||||||
{
|
|
||||||
_sharedApi.UnRegisterCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unregister all menus
|
|
||||||
if (Config.GodCommands.Count > 0)
|
|
||||||
_sharedApi.UnregisterMenu("fun", "god");
|
|
||||||
|
|
||||||
// Remove event handlers
|
|
||||||
_sharedApi.OnSimpleAdminReady -= RegisterFunMenus;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### ✅ Data Caching
|
|
||||||
```csharp
|
|
||||||
// Cache expensive operations
|
|
||||||
private static Dictionary<int, CsItem> GetWeaponsCache()
|
|
||||||
{
|
|
||||||
if (_weaponsCache != null) return _weaponsCache;
|
|
||||||
|
|
||||||
var weaponsArray = Enum.GetValues(typeof(CsItem));
|
|
||||||
_weaponsCache = new Dictionary<int, CsItem>();
|
|
||||||
// ... populate cache
|
|
||||||
return _weaponsCache;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔗 Related Documentation
|
|
||||||
|
|
||||||
- **[MODULE_DEVELOPMENT.md](../MODULE_DEVELOPMENT.md)** - Complete API reference
|
|
||||||
- **[TRANSLATION_EXAMPLE.md](../TRANSLATION_EXAMPLE.md)** - Translation usage guide
|
|
||||||
- **[CS2-SimpleAdminApi](../../CS2-SimpleAdminApi/)** - API interface definitions
|
|
||||||
|
|
||||||
## 💡 Tips
|
|
||||||
|
|
||||||
1. **Start Simple** - Begin with one command and one menu, then expand
|
|
||||||
2. **Test Thoroughly** - Test with multiple players, different permissions, and languages
|
|
||||||
3. **Handle Errors** - Always validate player state before actions
|
|
||||||
4. **Log Everything** - Use `_sharedApi.LogCommand()` for all admin actions
|
|
||||||
5. **Use Partial Classes** - Split your code into logical files
|
|
||||||
6. **Comment Your Code** - Especially if others will use it as reference
|
|
||||||
|
|
||||||
## 🐛 Common Mistakes to Avoid
|
|
||||||
|
|
||||||
- ❌ **Don't** forget to check `IsValid` before accessing player properties
|
|
||||||
- ❌ **Don't** skip permission checks (`CanTarget`, `RequiresPermissions`)
|
|
||||||
- ❌ **Don't** forget to unregister commands/menus in `Unload()`
|
|
||||||
- ❌ **Don't** use SimpleAdmin's translations - create your own
|
|
||||||
- ❌ **Don't** hardcode command names - use config lists instead
|
|
||||||
|
|
||||||
## 🤝 Contributing
|
|
||||||
|
|
||||||
If you improve this module or find better patterns, please contribute back so others can learn!
|
|
||||||
|
|
||||||
## 📄 License
|
|
||||||
|
|
||||||
This module is provided as-is for educational purposes. Feel free to use it as a template for your own modules.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"sdk": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"rollForward": "latestMinor",
|
|
||||||
"allowPrerelease": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,602 +0,0 @@
|
|||||||
# Module Development Guide - CS2-SimpleAdmin
|
|
||||||
|
|
||||||
> **🎓 New to module development?** Start with the [Fun Commands Module](./CS2-SimpleAdmin_FunCommands/) - it's a fully documented reference implementation showing all best practices!
|
|
||||||
|
|
||||||
This guide explains how to create modules for CS2-SimpleAdmin with custom commands, menus, and translations.
|
|
||||||
|
|
||||||
## 📖 Table of Contents
|
|
||||||
|
|
||||||
1. [Quick Start](#quick-start)
|
|
||||||
2. [Learning Resources](#learning-resources)
|
|
||||||
3. [API Methods Reference](#api-methods)
|
|
||||||
4. [Menu Patterns](#menu-patterns)
|
|
||||||
5. [Best Practices](#best-practices)
|
|
||||||
6. [Common Patterns](#common-patterns)
|
|
||||||
7. [Troubleshooting](#troubleshooting)
|
|
||||||
|
|
||||||
## 🚀 Quick Start
|
|
||||||
|
|
||||||
### Step 1: Study the Example Module
|
|
||||||
|
|
||||||
The **[CS2-SimpleAdmin_FunCommands](./CS2-SimpleAdmin_FunCommands/)** module is your best learning resource. It demonstrates:
|
|
||||||
|
|
||||||
✅ Command registration from config
|
|
||||||
✅ Dynamic menu creation
|
|
||||||
✅ Per-player translation support
|
|
||||||
✅ Proper cleanup on unload
|
|
||||||
✅ Code organization with partial classes
|
|
||||||
✅ All menu patterns you'll need
|
|
||||||
|
|
||||||
**Start here:** Read [`CS2-SimpleAdmin_FunCommands/README.md`](./CS2-SimpleAdmin_FunCommands/README.md)
|
|
||||||
|
|
||||||
### Step 2: Create Your Module Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
YourModule/
|
|
||||||
├── YourModule.csproj # Project file
|
|
||||||
├── YourModule.cs # Main plugin class
|
|
||||||
├── Commands.cs # Command handlers (optional)
|
|
||||||
├── Menus.cs # Menu creation (optional)
|
|
||||||
├── Config.cs # Configuration
|
|
||||||
└── lang/ # Translations
|
|
||||||
├── en.json
|
|
||||||
├── pl.json
|
|
||||||
└── ru.json
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 3: Minimal Working Example
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using CounterStrikeSharp.API.Core;
|
|
||||||
using CounterStrikeSharp.API.Core.Capabilities;
|
|
||||||
using CS2_SimpleAdminApi;
|
|
||||||
|
|
||||||
public class MyModule : BasePlugin
|
|
||||||
{
|
|
||||||
private ICS2_SimpleAdminApi? _api;
|
|
||||||
private readonly PluginCapability<ICS2_SimpleAdminApi> _pluginCapability = new("simpleadmin:api");
|
|
||||||
|
|
||||||
public override void OnAllPluginsLoaded(bool hotReload)
|
|
||||||
{
|
|
||||||
_api = _pluginCapability.Get();
|
|
||||||
if (_api == null)
|
|
||||||
{
|
|
||||||
Logger.LogError("CS2-SimpleAdmin API not found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register menus after API is ready
|
|
||||||
_api.OnSimpleAdminReady += RegisterMenus;
|
|
||||||
RegisterMenus(); // Fallback for hot reload
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RegisterMenus()
|
|
||||||
{
|
|
||||||
if (_api == null) return;
|
|
||||||
|
|
||||||
// 1. Register a new category
|
|
||||||
_api.RegisterMenuCategory("mymodule", "My Module", "@css/generic");
|
|
||||||
|
|
||||||
// 2. Register menu items in the category
|
|
||||||
_api.RegisterMenu("mymodule", "action1", "Action 1", CreateAction1Menu, "@css/generic");
|
|
||||||
_api.RegisterMenu("mymodule", "action2", "Action 2", CreateAction2Menu, "@css/kick");
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateAction1Menu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
// Create a menu with automatic back button
|
|
||||||
var menu = _api!.CreateMenuWithBack("Action 1 Menu", "mymodule", admin);
|
|
||||||
|
|
||||||
// Add menu options
|
|
||||||
_api.AddMenuOption(menu, "Option 1", player =>
|
|
||||||
{
|
|
||||||
player.PrintToChat("You selected Option 1");
|
|
||||||
});
|
|
||||||
|
|
||||||
_api.AddMenuOption(menu, "Option 2", player =>
|
|
||||||
{
|
|
||||||
player.PrintToChat("You selected Option 2");
|
|
||||||
});
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateAction2Menu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
// Use the built-in player selection menu
|
|
||||||
return _api!.CreateMenuWithPlayers("Select Player", "mymodule", admin,
|
|
||||||
player => player.IsValid && admin.CanTarget(player),
|
|
||||||
(adminPlayer, targetPlayer) =>
|
|
||||||
{
|
|
||||||
adminPlayer.PrintToChat($"You selected {targetPlayer.PlayerName}");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Unload(bool hotReload)
|
|
||||||
{
|
|
||||||
if (_api == null) return;
|
|
||||||
|
|
||||||
// Clean up registered menus
|
|
||||||
_api.UnregisterMenu("mymodule", "action1");
|
|
||||||
_api.UnregisterMenu("mymodule", "action2");
|
|
||||||
_api.OnSimpleAdminReady -= RegisterMenus;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📚 Learning Resources
|
|
||||||
|
|
||||||
### For Beginners
|
|
||||||
|
|
||||||
1. **Start Here:** [`CS2-SimpleAdmin_FunCommands/README.md`](./CS2-SimpleAdmin_FunCommands/README.md)
|
|
||||||
- Explains every file and pattern
|
|
||||||
- Shows code organization
|
|
||||||
- Demonstrates all menu types
|
|
||||||
|
|
||||||
2. **Read the Code:** Study these files in order:
|
|
||||||
- `Config.cs` - Simple configuration
|
|
||||||
- `CS2-SimpleAdmin_FunCommands.cs` - Plugin initialization
|
|
||||||
- `Commands.cs` - Command registration
|
|
||||||
- `Menus.cs` - Menu creation patterns
|
|
||||||
|
|
||||||
3. **Translations:** [`TRANSLATION_EXAMPLE.md`](./TRANSLATION_EXAMPLE.md)
|
|
||||||
- How to use module translations
|
|
||||||
- Per-player language support
|
|
||||||
- Best practices
|
|
||||||
|
|
||||||
### Key Concepts
|
|
||||||
|
|
||||||
| Concept | What It Does | Example File |
|
|
||||||
|---------|-------------|--------------|
|
|
||||||
| **API Capability** | Get access to SimpleAdmin API | `CS2-SimpleAdmin_FunCommands.cs:37` |
|
|
||||||
| **Command Registration** | Register console commands | `Commands.cs:15-34` |
|
|
||||||
| **Menu Registration** | Add menus to admin menu | `CS2-SimpleAdmin_FunCommands.cs:130-141` |
|
|
||||||
| **Translations** | Per-player language support | `Actions.cs:20-31` |
|
|
||||||
| **Cleanup** | Unregister on plugin unload | `CS2-SimpleAdmin_FunCommands.cs:63-97` |
|
|
||||||
|
|
||||||
## 🎯 Menu Patterns
|
|
||||||
|
|
||||||
The FunCommands module demonstrates **3 essential menu patterns** you'll use in every module:
|
|
||||||
|
|
||||||
### Pattern 1: Simple Player Selection
|
|
||||||
**When to use:** Select a player and immediately execute an action
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Example: God Mode menu
|
|
||||||
private object CreateGodModeMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
return _api.CreateMenuWithPlayers(
|
|
||||||
"God Mode", // Title
|
|
||||||
"yourmodule", // Category ID
|
|
||||||
admin, // Admin
|
|
||||||
player => player.IsValid && admin.CanTarget(player), // Filter
|
|
||||||
(adminPlayer, target) => // Action
|
|
||||||
{
|
|
||||||
// Execute action immediately
|
|
||||||
ToggleGodMode(target);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**See:** `CS2-SimpleAdmin_FunCommands/Menus.cs:21-29`
|
|
||||||
|
|
||||||
### Pattern 2: Nested Menu (Player → Value)
|
|
||||||
**When to use:** Select a player, then select a value/option for that player
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Example: Set HP menu (player selection)
|
|
||||||
private object CreateSetHpMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var menu = _api.CreateMenuWithBack("Set HP", "yourmodule", admin);
|
|
||||||
var players = _api.GetValidPlayers().Where(p => admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
// AddSubMenu automatically adds back button to submenu
|
|
||||||
_api.AddSubMenu(menu, player.PlayerName,
|
|
||||||
p => CreateHpValueMenu(admin, player));
|
|
||||||
}
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Example: Set HP menu (value selection)
|
|
||||||
private object CreateHpValueMenu(CCSPlayerController admin, CCSPlayerController target)
|
|
||||||
{
|
|
||||||
var menu = _api.CreateMenuWithBack($"HP for {target.PlayerName}", "yourmodule", admin);
|
|
||||||
var values = new[] { 50, 100, 200, 500 };
|
|
||||||
|
|
||||||
foreach (var hp in values)
|
|
||||||
{
|
|
||||||
_api.AddMenuOption(menu, $"{hp} HP", _ =>
|
|
||||||
{
|
|
||||||
if (target.IsValid) // Always validate!
|
|
||||||
{
|
|
||||||
target.PlayerPawn.Value.Health = hp;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**See:** `CS2-SimpleAdmin_FunCommands/Menus.cs:134-173`
|
|
||||||
|
|
||||||
### Pattern 3: Nested Menu with Complex Data
|
|
||||||
**When to use:** Need to display more complex options (like weapons with icons, items with descriptions)
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Example: Give Weapon menu
|
|
||||||
private object CreateGiveWeaponMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var menu = _api.CreateMenuWithBack("Give Weapon", "yourmodule", admin);
|
|
||||||
var players = _api.GetValidPlayers().Where(p => admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
_api.AddSubMenu(menu, player.PlayerName,
|
|
||||||
p => CreateWeaponSelectionMenu(admin, player));
|
|
||||||
}
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateWeaponSelectionMenu(CCSPlayerController admin, CCSPlayerController target)
|
|
||||||
{
|
|
||||||
var menu = _api.CreateMenuWithBack($"Weapons for {target.PlayerName}", "yourmodule", admin);
|
|
||||||
|
|
||||||
// Use cached data for performance
|
|
||||||
foreach (var weapon in GetWeaponsCache())
|
|
||||||
{
|
|
||||||
_api.AddMenuOption(menu, weapon.Value.ToString(), _ =>
|
|
||||||
{
|
|
||||||
if (target.IsValid)
|
|
||||||
{
|
|
||||||
target.GiveNamedItem(weapon.Value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**See:** `CS2-SimpleAdmin_FunCommands/Menus.cs:67-109`
|
|
||||||
|
|
||||||
## 📋 API Methods Reference
|
|
||||||
|
|
||||||
### 1. Category Management
|
|
||||||
|
|
||||||
#### `RegisterMenuCategory(string categoryId, string categoryName, string permission = "@css/generic")`
|
|
||||||
|
|
||||||
Registers a new menu category that appears in the main admin menu.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
- `categoryId` - Unique identifier for the category (e.g., "fun", "vip", "economy")
|
|
||||||
- `categoryName` - Display name shown in menu (e.g., "Fun Commands")
|
|
||||||
- `permission` - Required permission to see the category (default: "@css/generic")
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```csharp
|
|
||||||
_api.RegisterMenuCategory("vip", "VIP Features", "@css/vip");
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Menu Registration
|
|
||||||
|
|
||||||
#### `RegisterMenu(string categoryId, string menuId, string menuName, Func<CCSPlayerController, object> menuFactory, string? permission = null)`
|
|
||||||
|
|
||||||
Registers a menu item within a category.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
- `categoryId` - The category to add this menu to
|
|
||||||
- `menuId` - Unique identifier for the menu
|
|
||||||
- `menuName` - Display name in the category menu
|
|
||||||
- `menuFactory` - Function that creates the menu when selected (receives admin player)
|
|
||||||
- `permission` - Optional permission required to see this menu item
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```csharp
|
|
||||||
_api.RegisterMenu("fun", "godmode", "God Mode", CreateGodModeMenu, "@css/cheats");
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `UnregisterMenu(string categoryId, string menuId)`
|
|
||||||
|
|
||||||
Removes a menu item from a category.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```csharp
|
|
||||||
_api.UnregisterMenu("fun", "godmode");
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Menu Creation
|
|
||||||
|
|
||||||
#### `CreateMenuWithBack(string title, string categoryId, CCSPlayerController player)`
|
|
||||||
|
|
||||||
Creates a menu with an automatic "Back" button that returns to the category menu.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
- `title` - Menu title
|
|
||||||
- `categoryId` - Category this menu belongs to (for back navigation)
|
|
||||||
- `player` - The admin player viewing the menu
|
|
||||||
|
|
||||||
**Returns:** `object` (MenuBuilder instance)
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```csharp
|
|
||||||
var menu = _api.CreateMenuWithBack("Weapon Selection", "fun", admin);
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `CreateMenuWithPlayers(string title, string categoryId, CCSPlayerController admin, Func<CCSPlayerController, bool> filter, Action<CCSPlayerController, CCSPlayerController> onSelect)`
|
|
||||||
|
|
||||||
Creates a menu with a list of players, filtered and with automatic back button.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
- `title` - Menu title
|
|
||||||
- `categoryId` - Category for back navigation
|
|
||||||
- `admin` - The admin player viewing the menu
|
|
||||||
- `filter` - Function to filter which players appear in the menu
|
|
||||||
- `onSelect` - Action to execute when a player is selected (receives admin and target)
|
|
||||||
|
|
||||||
**Returns:** `object` (MenuBuilder instance)
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```csharp
|
|
||||||
return _api.CreateMenuWithPlayers("Select Player to Kick", "admin", admin,
|
|
||||||
player => player.IsValid && admin.CanTarget(player),
|
|
||||||
(adminPlayer, targetPlayer) =>
|
|
||||||
{
|
|
||||||
// Kick the selected player
|
|
||||||
Server.ExecuteCommand($"css_kick {targetPlayer.UserId}");
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Menu Manipulation
|
|
||||||
|
|
||||||
#### `AddMenuOption(object menu, string name, Action<CCSPlayerController> action, bool disabled = false, string? permission = null)`
|
|
||||||
|
|
||||||
Adds a clickable option to a menu.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
- `menu` - The menu object (from CreateMenuWithBack)
|
|
||||||
- `name` - Display name of the option
|
|
||||||
- `action` - Action to execute when clicked (receives the player who clicked)
|
|
||||||
- `disabled` - Whether the option is disabled (grayed out)
|
|
||||||
- `permission` - Optional permission required to see this option
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```csharp
|
|
||||||
_api.AddMenuOption(menu, "Give AK-47", player =>
|
|
||||||
{
|
|
||||||
player.GiveNamedItem("weapon_ak47");
|
|
||||||
}, permission: "@css/cheats");
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `AddSubMenu(object menu, string name, Func<CCSPlayerController, object> subMenuFactory, bool disabled = false, string? permission = null)`
|
|
||||||
|
|
||||||
Adds a submenu option that opens another menu. **Automatically adds a back button to the submenu.**
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
- `menu` - The parent menu
|
|
||||||
- `name` - Display name of the submenu option
|
|
||||||
- `subMenuFactory` - Function that creates the submenu (receives the player)
|
|
||||||
- `disabled` - Whether the option is disabled
|
|
||||||
- `permission` - Optional permission required
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```csharp
|
|
||||||
_api.AddSubMenu(menu, "Weapon Category", player =>
|
|
||||||
{
|
|
||||||
var weaponMenu = _api.CreateMenuWithBack("Weapons", "fun", player);
|
|
||||||
_api.AddMenuOption(weaponMenu, "AK-47", p => p.GiveNamedItem("weapon_ak47"));
|
|
||||||
_api.AddMenuOption(weaponMenu, "AWP", p => p.GiveNamedItem("weapon_awp"));
|
|
||||||
return weaponMenu;
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `OpenMenu(object menu, CCSPlayerController player)`
|
|
||||||
|
|
||||||
Opens a menu for a specific player.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```csharp
|
|
||||||
var menu = _api.CreateMenuWithBack("Custom Menu", "fun", player);
|
|
||||||
_api.AddMenuOption(menu, "Test", p => p.PrintToChat("Test!"));
|
|
||||||
_api.OpenMenu(menu, player);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Advanced Examples
|
|
||||||
|
|
||||||
### Nested Menus with Player Selection
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
private object CreateGiveWeaponMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var menu = _api.CreateMenuWithBack("Give Weapon", "fun", admin);
|
|
||||||
var players = _api.GetValidPlayers()
|
|
||||||
.Where(p => p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
// Add submenu for each player - automatic back button will be added
|
|
||||||
_api.AddSubMenu(menu, player.PlayerName, p => CreateWeaponSelectionMenu(admin, player));
|
|
||||||
}
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateWeaponSelectionMenu(CCSPlayerController admin, CCSPlayerController target)
|
|
||||||
{
|
|
||||||
var weaponMenu = _api.CreateMenuWithBack($"Weapons for {target.PlayerName}", "fun", admin);
|
|
||||||
|
|
||||||
var weapons = new[] { "weapon_ak47", "weapon_m4a1", "weapon_awp", "weapon_deagle" };
|
|
||||||
foreach (var weapon in weapons)
|
|
||||||
{
|
|
||||||
_api.AddMenuOption(weaponMenu, weapon, _ =>
|
|
||||||
{
|
|
||||||
if (target.IsValid)
|
|
||||||
{
|
|
||||||
target.GiveNamedItem(weapon);
|
|
||||||
admin.PrintToChat($"Gave {weapon} to {target.PlayerName}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return weaponMenu;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dynamic Menu with Value Selection
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
private object CreateSetHpMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var menu = _api.CreateMenuWithBack("Set HP", "admin", admin);
|
|
||||||
var players = _api.GetValidPlayers().Where(p => admin.CanTarget(p));
|
|
||||||
|
|
||||||
foreach (var player in players)
|
|
||||||
{
|
|
||||||
_api.AddSubMenu(menu, player.PlayerName, p => CreateHpValueMenu(admin, player));
|
|
||||||
}
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object CreateHpValueMenu(CCSPlayerController admin, CCSPlayerController target)
|
|
||||||
{
|
|
||||||
var hpMenu = _api.CreateMenuWithBack($"HP for {target.PlayerName}", "admin", admin);
|
|
||||||
var hpValues = new[] { 1, 50, 100, 200, 500, 1000 };
|
|
||||||
|
|
||||||
foreach (var hp in hpValues)
|
|
||||||
{
|
|
||||||
_api.AddMenuOption(hpMenu, $"{hp} HP", _ =>
|
|
||||||
{
|
|
||||||
if (target.IsValid && target.PlayerPawn?.Value != null)
|
|
||||||
{
|
|
||||||
target.PlayerPawn.Value.Health = hp;
|
|
||||||
admin.PrintToChat($"Set {target.PlayerName} HP to {hp}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return hpMenu;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Permission-Based Menu Options
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
private object CreateAdminToolsMenu(CCSPlayerController admin)
|
|
||||||
{
|
|
||||||
var menu = _api.CreateMenuWithBack("Admin Tools", "admin", admin);
|
|
||||||
|
|
||||||
// Only root admins can see this
|
|
||||||
_api.AddMenuOption(menu, "Dangerous Action", player =>
|
|
||||||
{
|
|
||||||
player.PrintToChat("Executing dangerous action...");
|
|
||||||
}, permission: "@css/root");
|
|
||||||
|
|
||||||
// All admins can see this
|
|
||||||
_api.AddMenuOption(menu, "Safe Action", player =>
|
|
||||||
{
|
|
||||||
player.PrintToChat("Executing safe action...");
|
|
||||||
}, permission: "@css/generic");
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Always check for API availability**
|
|
||||||
```csharp
|
|
||||||
if (_api == null) return;
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Validate player state before actions**
|
|
||||||
```csharp
|
|
||||||
if (target.IsValid && target.PlayerPawn?.Value != null)
|
|
||||||
{
|
|
||||||
// Safe to perform action
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Use descriptive category and menu IDs**
|
|
||||||
- Good: `"economy"`, `"vip_features"`, `"fun_commands"`
|
|
||||||
- Bad: `"cat1"`, `"menu"`, `"test"`
|
|
||||||
|
|
||||||
4. **Clean up on unload**
|
|
||||||
```csharp
|
|
||||||
public override void Unload(bool hotReload)
|
|
||||||
{
|
|
||||||
_api?.UnregisterMenu("mymodule", "mymenu");
|
|
||||||
_api.OnSimpleAdminReady -= RegisterMenus;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
5. **Use appropriate permissions**
|
|
||||||
- `@css/generic` - All admins
|
|
||||||
- `@css/ban` - Admins who can ban
|
|
||||||
- `@css/kick` - Admins who can kick
|
|
||||||
- `@css/root` - Root admins only
|
|
||||||
- Custom permissions from your module
|
|
||||||
|
|
||||||
6. **Handle hot reload**
|
|
||||||
```csharp
|
|
||||||
_api.OnSimpleAdminReady += RegisterMenus;
|
|
||||||
RegisterMenus(); // Fallback for hot reload case
|
|
||||||
```
|
|
||||||
|
|
||||||
## Automatic Back Button
|
|
||||||
|
|
||||||
The menu system **automatically adds a "Back" button** to all submenus created with:
|
|
||||||
- `CreateMenuWithBack()` - Returns to the category menu
|
|
||||||
- `AddSubMenu()` - Returns to the parent menu
|
|
||||||
|
|
||||||
You don't need to manually add back buttons - the system handles navigation automatically!
|
|
||||||
|
|
||||||
## Getting Valid Players
|
|
||||||
|
|
||||||
Use the API helper method to get valid, connected players:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var players = _api.GetValidPlayers();
|
|
||||||
|
|
||||||
// With filtering
|
|
||||||
var alivePlayers = _api.GetValidPlayers()
|
|
||||||
.Where(p => p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE);
|
|
||||||
|
|
||||||
var targetablePlayers = _api.GetValidPlayers()
|
|
||||||
.Where(p => admin.CanTarget(p));
|
|
||||||
```
|
|
||||||
|
|
||||||
## Complete Module Example
|
|
||||||
|
|
||||||
See the `CS2-SimpleAdmin_FunCommands` module in the `Modules/` directory for a complete, production-ready example of:
|
|
||||||
- Category registration
|
|
||||||
- Multiple menu types
|
|
||||||
- Nested menus with automatic back buttons
|
|
||||||
- Player selection menus
|
|
||||||
- Permission-based access
|
|
||||||
- Proper cleanup on unload
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
**Q: My category doesn't appear in the admin menu**
|
|
||||||
- Ensure you're calling `RegisterMenuCategory()` after the API is ready
|
|
||||||
- Check that the player has the required permission
|
|
||||||
- Verify the category has at least one menu registered with `RegisterMenu()`
|
|
||||||
|
|
||||||
**Q: Back button doesn't work**
|
|
||||||
- Make sure you're using `CreateMenuWithBack()` instead of creating menus manually
|
|
||||||
- The `categoryId` parameter must match the category you registered
|
|
||||||
- Use `AddSubMenu()` for nested menus - it handles back navigation automatically
|
|
||||||
|
|
||||||
**Q: Menu appears but is empty**
|
|
||||||
- Check that you're adding options with `AddMenuOption()` or `AddSubMenu()`
|
|
||||||
- Verify permission checks aren't filtering out all options
|
|
||||||
- Ensure player validation in filters isn't too restrictive
|
|
||||||
|
|
||||||
**Q: API is null in OnAllPluginsLoaded**
|
|
||||||
- Wait for the `OnSimpleAdminReady` event instead of immediate registration
|
|
||||||
- Make sure CS2-SimpleAdmin is loaded before your module
|
|
||||||
@@ -1,268 +0,0 @@
|
|||||||
# Module Translation Guide
|
|
||||||
|
|
||||||
> **🎓 New to translations?** This guide shows you how to add multi-language support to your module!
|
|
||||||
|
|
||||||
## Why Use Module Translations?
|
|
||||||
|
|
||||||
When you use SimpleAdmin API's translation system, **each player automatically sees messages in their preferred language**!
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
- 🇬🇧 **Player with English:** "Admin gave PlayerName a weapon!"
|
|
||||||
- 🇵🇱 **Player with Polish:** "Admin dał PlayerName broń!"
|
|
||||||
- 🇷🇺 **Player with Russian:** "Admin дал PlayerName оружие!"
|
|
||||||
|
|
||||||
**All from ONE line of code!**
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
### Step 1: Create Your Translation Files
|
|
||||||
|
|
||||||
Create a `lang` folder in your module with translation files for each language:
|
|
||||||
|
|
||||||
```
|
|
||||||
YourModule/
|
|
||||||
└── lang/
|
|
||||||
├── en.json
|
|
||||||
├── pl.json
|
|
||||||
└── ru.json
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example: `lang/en.json`**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"yourmod_admin_action": "{lightred}{0}{default} performed action on {lightred}{1}{default}!"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example: `lang/pl.json`**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"yourmod_admin_action": "{lightred}{0}{default} wykonał akcję na {lightred}{1}{default}!"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 2: Use in Your Code
|
|
||||||
|
|
||||||
**✅ RECOMMENDED METHOD:** `ShowAdminActivityLocalized`
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Show activity with per-player language support
|
|
||||||
var args = new object[] { "CALLER", targetPlayer.PlayerName };
|
|
||||||
if (admin == null || !_api.IsAdminSilent(admin))
|
|
||||||
{
|
|
||||||
if (Localizer != null)
|
|
||||||
{
|
|
||||||
// Each player sees this in their language!
|
|
||||||
_api.ShowAdminActivityLocalized(
|
|
||||||
Localizer, // Your module's localizer
|
|
||||||
"yourmod_admin_action", // Translation key
|
|
||||||
admin.PlayerName, // Caller name
|
|
||||||
false, // dontPublish
|
|
||||||
args); // Message arguments
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
That's it! SimpleAdmin handles the rest.
|
|
||||||
|
|
||||||
## Complete Example
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using CounterStrikeSharp.API.Core;
|
|
||||||
using CS2_SimpleAdminApi;
|
|
||||||
|
|
||||||
public partial class MyModule : BasePlugin
|
|
||||||
{
|
|
||||||
private ICS2_SimpleAdminApi? _api;
|
|
||||||
|
|
||||||
private void GiveWeaponToPlayer(CCSPlayerController admin, CCSPlayerController target, string weaponName)
|
|
||||||
{
|
|
||||||
// Give the weapon
|
|
||||||
target.GiveNamedItem(weaponName);
|
|
||||||
|
|
||||||
var callerName = admin.PlayerName;
|
|
||||||
|
|
||||||
// Show activity using module's localizer - each player sees it in their language!
|
|
||||||
if (admin == null || !_api!.IsAdminSilent(admin))
|
|
||||||
{
|
|
||||||
var args = new object[] { "CALLER", target.PlayerName, weaponName };
|
|
||||||
if (Localizer != null)
|
|
||||||
{
|
|
||||||
_api!.ShowAdminActivityLocalized(Localizer, "yourmod_admin_give_message", callerName, false, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log the command
|
|
||||||
_api!.LogCommand(admin, $"css_give {target.PlayerName} {weaponName}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔑 Important: The "CALLER" Placeholder
|
|
||||||
|
|
||||||
**Always use `"CALLER"` as the first argument** in your translation messages!
|
|
||||||
|
|
||||||
The API automatically replaces `"CALLER"` based on the server's `ShowActivityType` configuration:
|
|
||||||
|
|
||||||
| ShowActivityType | What Players See |
|
|
||||||
|-----------------|-----------------|
|
|
||||||
| `1` | Non-admins see "Admin", admins see real name |
|
|
||||||
| `2+` | Everyone sees real admin name |
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"yourmod_message": "{0} did something to {1}"
|
|
||||||
↑ This will be replaced with "Admin" or admin's name
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var args = new object[] { "CALLER", targetPlayer.PlayerName };
|
|
||||||
// ↑ API replaces this automatically
|
|
||||||
```
|
|
||||||
|
|
||||||
## 💡 Pro Tips
|
|
||||||
|
|
||||||
### Tip 1: Use a Helper Method
|
|
||||||
|
|
||||||
Create a reusable helper to reduce code duplication:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
/// <summary>
|
|
||||||
/// Helper method to show activity and log command
|
|
||||||
/// Copy this to your module!
|
|
||||||
/// </summary>
|
|
||||||
private void LogAndShowActivity(
|
|
||||||
CCSPlayerController? caller,
|
|
||||||
CCSPlayerController target,
|
|
||||||
string translationKey,
|
|
||||||
string baseCommand,
|
|
||||||
params string[] extraArgs)
|
|
||||||
{
|
|
||||||
var callerName = caller?.PlayerName ?? "Console";
|
|
||||||
|
|
||||||
// Build args: "CALLER" + target name + any extra args
|
|
||||||
var args = new List<object> { "CALLER", target.PlayerName };
|
|
||||||
args.AddRange(extraArgs);
|
|
||||||
|
|
||||||
// Show activity with per-player language support
|
|
||||||
if (caller == null || !_api.IsAdminSilent(caller))
|
|
||||||
{
|
|
||||||
if (Localizer != null)
|
|
||||||
{
|
|
||||||
_api.ShowAdminActivityLocalized(
|
|
||||||
Localizer,
|
|
||||||
translationKey,
|
|
||||||
callerName,
|
|
||||||
false,
|
|
||||||
args.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build and log command
|
|
||||||
var logCommand = $"{baseCommand} {target.PlayerName}";
|
|
||||||
if (extraArgs.Length > 0)
|
|
||||||
{
|
|
||||||
logCommand += $" {string.Join(" ", extraArgs)}";
|
|
||||||
}
|
|
||||||
_api.LogCommand(caller, logCommand);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
```csharp
|
|
||||||
// Simple action
|
|
||||||
LogAndShowActivity(admin, target, "yourmod_kick_message", "css_kick");
|
|
||||||
|
|
||||||
// Action with parameters
|
|
||||||
LogAndShowActivity(admin, target, "yourmod_hp_message", "css_hp", "100");
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tip 2: Translation Key Naming Convention
|
|
||||||
|
|
||||||
Use a consistent prefix for your module:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"yourmod_admin_action1": "...",
|
|
||||||
"yourmod_admin_action2": "...",
|
|
||||||
"yourmod_error_notarget": "..."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This prevents conflicts with other modules and makes it clear which module owns the translation.
|
|
||||||
|
|
||||||
### Tip 3: Color Formatting
|
|
||||||
|
|
||||||
Use CounterStrikeSharp color tags in your translations:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"yourmod_message": "{lightred}{0}{default} gave {green}{1}{default} a {yellow}{2}{default}!"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Available colors:**
|
|
||||||
- `{default}`, `{white}`, `{darkred}`, `{green}`, `{lightyellow}`
|
|
||||||
- `{lightblue}`, `{olive}`, `{lime}`, `{red}`, `{purple}`
|
|
||||||
- `{grey}`, `{yellow}`, `{gold}`, `{silver}`, `{blue}`
|
|
||||||
- `{darkblue}`, `{bluegrey}`, `{magenta}`, `{lightred}`, `{orange}`
|
|
||||||
|
|
||||||
## 📖 Real Example: Fun Commands Module
|
|
||||||
|
|
||||||
The **[CS2-SimpleAdmin_FunCommands](./CS2-SimpleAdmin_FunCommands/)** module is a perfect reference:
|
|
||||||
|
|
||||||
**Translation files:** `Modules/CS2-SimpleAdmin_FunCommands/lang/`
|
|
||||||
- Has 13 languages (en, pl, ru, de, fr, es, etc.)
|
|
||||||
- Shows proper key naming (`fun_admin_*`)
|
|
||||||
- Demonstrates color usage
|
|
||||||
|
|
||||||
**Code examples:** `Modules/CS2-SimpleAdmin_FunCommands/CS2-SimpleAdmin_FunCommands/Actions.cs`
|
|
||||||
- Lines 20-31: God mode with translations
|
|
||||||
- Lines 48-59: NoClip with translations
|
|
||||||
- Lines 76-86: Freeze with translations
|
|
||||||
|
|
||||||
**Helper method:** `Modules/CS2-SimpleAdmin_FunCommands/CS2-SimpleAdmin_FunCommands/Commands.cs:274-306`
|
|
||||||
|
|
||||||
## ❌ Common Mistakes
|
|
||||||
|
|
||||||
### Mistake 1: Forgetting "CALLER"
|
|
||||||
```csharp
|
|
||||||
// ❌ WRONG
|
|
||||||
var args = new object[] { admin.PlayerName, target.PlayerName };
|
|
||||||
|
|
||||||
// ✅ CORRECT
|
|
||||||
var args = new object[] { "CALLER", target.PlayerName };
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mistake 2: Using SimpleAdmin's Translations
|
|
||||||
```csharp
|
|
||||||
// ❌ WRONG - Uses SimpleAdmin's keys
|
|
||||||
_api.ShowAdminActivity("sa_admin_kick", ...)
|
|
||||||
|
|
||||||
// ✅ CORRECT - Uses YOUR module's keys
|
|
||||||
_api.ShowAdminActivityLocalized(Localizer, "yourmod_kick", ...)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mistake 3: Not Checking Localizer
|
|
||||||
```csharp
|
|
||||||
// ❌ WRONG - Will crash if Localizer is null
|
|
||||||
_api.ShowAdminActivityLocalized(Localizer, "key", ...)
|
|
||||||
|
|
||||||
// ✅ CORRECT - Check first
|
|
||||||
if (Localizer != null)
|
|
||||||
{
|
|
||||||
_api.ShowAdminActivityLocalized(Localizer, "key", ...)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔗 See Also
|
|
||||||
|
|
||||||
- **[MODULE_DEVELOPMENT.md](./MODULE_DEVELOPMENT.md)** - Complete module development guide
|
|
||||||
- **[CS2-SimpleAdmin_FunCommands/README.md](./CS2-SimpleAdmin_FunCommands/README.md)** - Reference implementation
|
|
||||||
- **[CounterStrikeSharp Localization](https://docs.cssharp.dev/guides/localization/)** - Official CSS localization docs
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Need help?** Study the FunCommands module - it demonstrates all these patterns correctly!
|
|
||||||
Reference in New Issue
Block a user