zio fork 1.8.2b-zio16: backport unban, rename and css_history from upstream 1.9.0
From upstream 530f603: - css_unban updates the ban cache at once (and clears the engine banid with UnlockedCommands) instead of waiting up to a minute for the cache refresh. - css_prename is saved in sa_renames (migration 017) and re-applied on connect and name change, so it survives restarts. css_rename from a hidden admin renames again. - New css_history command and Players > History menu entry. Commands missing from an existing Commands.json are registered under their default name. - Migrations are copied with a glob, so 017 reaches the build output. Upstream's admin-loading changes, css_say change and css_penalties fix are left out.
This commit is contained in:
parent
8bec658db4
commit
71d51d7b92
35 changed files with 539 additions and 143 deletions
|
|
@ -45,6 +45,7 @@ public static class RegisterCommands
|
|||
new("css_who", CS2_SimpleAdmin.Instance.OnWhoCommand),
|
||||
new("css_disconnected", CS2_SimpleAdmin.Instance.OnDisconnectedCommand),
|
||||
new("css_warns", CS2_SimpleAdmin.Instance.OnWarnsCommand),
|
||||
new("css_history", CS2_SimpleAdmin.Instance.OnHistoryCommand),
|
||||
new("css_players", CS2_SimpleAdmin.Instance.OnPlayersCommand),
|
||||
new("css_kick", CS2_SimpleAdmin.Instance.OnKickCommand),
|
||||
new("css_map", CS2_SimpleAdmin.Instance.OnMapCommand),
|
||||
|
|
@ -130,6 +131,7 @@ public static class RegisterCommands
|
|||
{ "css_who", new Command { Aliases = ["css_who"] } },
|
||||
{ "css_disconnected", new Command { Aliases = ["css_disconnected", "css_last"] } },
|
||||
{ "css_warns", new Command { Aliases = ["css_warns"] } },
|
||||
{ "css_history", new Command { Aliases = ["css_history", "css_penaltyhistory"] } },
|
||||
{ "css_players", new Command { Aliases = ["css_players"] } },
|
||||
{ "css_kick", new Command { Aliases = ["css_kick"] } },
|
||||
{ "css_map", new Command { Aliases = ["css_map", "css_changemap"] } },
|
||||
|
|
@ -198,6 +200,13 @@ public static class RegisterCommands
|
|||
CS2_SimpleAdmin.Instance.AddCommand(alias, "", mapping.Callback);
|
||||
}
|
||||
}
|
||||
|
||||
// Commands added after Commands.json was generated are registered under their default name
|
||||
foreach (var mapping in CommandMappings.Where(m => !commandsConfig.Commands.ContainsKey(m.CommandKey)))
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogInformation($"Registering command: `{mapping.CommandKey}` (not in Commands.json, using default alias)");
|
||||
CS2_SimpleAdmin.Instance.AddCommand(mapping.CommandKey, "", mapping.Callback);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (name, definitions) in _commandDefinitions)
|
||||
|
|
|
|||
|
|
@ -846,6 +846,99 @@ public partial class CS2_SimpleAdmin
|
|||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints a player's full penalty history (bans, gags, mutes, silences, warns) to the caller's console.
|
||||
/// Accepts a SteamID64 so it also works for players who are not online.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the command or null for console.</param>
|
||||
/// <param name="command">The command containing the target and an optional penalty type filter.</param>
|
||||
[RequiresPermissions("@css/kick")]
|
||||
[CommandHelper(minArgs: 1, usage: "<#userid or name or steamid64> [bans|gags|mutes|silences|warns]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnHistoryCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null || _localizer == null) return;
|
||||
|
||||
var filter = command.ArgCount > 2 ? command.GetArg(2).ToLowerInvariant() : null;
|
||||
if (filter is not (null or "bans" or "gags" or "mutes" or "silences" or "warns"))
|
||||
{
|
||||
command.ReplyToCommand("Usage: css_history <#userid or name or steamid64> [bans|gags|mutes|silences|warns]");
|
||||
return;
|
||||
}
|
||||
|
||||
ulong steamId;
|
||||
string? name = null;
|
||||
|
||||
if (Helper.ValidateSteamId(command.GetArg(1), out var parsedSteamId) && parsedSteamId != null)
|
||||
{
|
||||
if (caller != null && !caller.CanTarget(parsedSteamId)) return;
|
||||
|
||||
steamId = parsedSteamId.SteamId64;
|
||||
if (PlayersInfo.TryGetValue(steamId, out var info)) name = info.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
|
||||
var players = targets.Players.Where(p => p is { IsValid: true, IsBot: false, IsHLTV: false }).ToList();
|
||||
if (players.Count != 1)
|
||||
{
|
||||
command.ReplyToCommand("Only one target is allowed.");
|
||||
return;
|
||||
}
|
||||
|
||||
var player = players[0];
|
||||
if (caller != null && !caller.CanTarget(player)) return;
|
||||
|
||||
steamId = player.SteamID;
|
||||
name = player.PlayerName;
|
||||
}
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
Action<string> print = caller == null ? Server.PrintToConsole : caller.PrintToConsole;
|
||||
var callerSteamId = caller?.SteamID;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var rows = await PlayerManager.GetPenaltyHistory(steamId, filter);
|
||||
// Offline players are only known by SteamID; borrow the name stored on their latest record
|
||||
name ??= rows.Count > 0 ? (string?)rows[0].player_name : null;
|
||||
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
// The slot may have been reused by another player while the query ran
|
||||
if (caller != null && (!caller.IsValid || caller.SteamID != callerSteamId)) return;
|
||||
PrintHistory(print, name ?? steamId.ToString(), steamId, rows);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes history rows from <see cref="PlayerManager.GetPenaltyHistory"/> as one console line each.
|
||||
/// </summary>
|
||||
internal void PrintHistory(Action<string> print, string name, ulong steamId, List<dynamic> rows)
|
||||
{
|
||||
if (_localizer == null) return;
|
||||
|
||||
if (rows.Count == 0)
|
||||
{
|
||||
print(_localizer["sa_history_none", name]);
|
||||
return;
|
||||
}
|
||||
|
||||
print(_localizer["sa_history_header", name, steamId, rows.Count]);
|
||||
foreach (var r in rows)
|
||||
{
|
||||
var duration = (int)r.duration == 0 ? "perm" : $"{(int)r.duration}m";
|
||||
var line = $"[{Date(r.created)}] {(string)r.type,-7} {(string)r.status,-8} {duration,-6} by {(string)r.admin_name}: {(string)r.reason}";
|
||||
if (r.lift_date != null)
|
||||
line += $" | lifted {Date(r.lift_date)} by {(string?)r.lift_admin ?? "?"}: {(string?)r.lift_reason}";
|
||||
print(line);
|
||||
}
|
||||
|
||||
static string Date(object? value) => PlayerManager.ToDateTime(value)?.ToString("yyyy-MM-dd HH:mm") ?? "?";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists players currently connected to the server with options to output JSON or filter duplicate IPs.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -292,10 +292,9 @@ public partial class CS2_SimpleAdmin
|
|||
var adminActivityArgs = new object[] { "CALLER", player.PlayerName, newName };
|
||||
|
||||
// Display admin activity message to other players
|
||||
if (caller != null && SilentPlayers.Contains(caller.Slot)) return;
|
||||
if (caller != null && !SilentPlayers.Contains(caller.Slot))
|
||||
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||
|
||||
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||
|
||||
// Rename the player
|
||||
player.Rename(newName);
|
||||
});
|
||||
|
|
@ -344,15 +343,18 @@ public partial class CS2_SimpleAdmin
|
|||
}
|
||||
|
||||
// Determine if the new name is valid and update the renamed players list
|
||||
var steamId = player.SteamID;
|
||||
if (!string.IsNullOrEmpty(newName))
|
||||
{
|
||||
RenamedPlayers[player.SteamID] = newName;
|
||||
RenamedPlayers[steamId] = newName;
|
||||
player.Rename(newName);
|
||||
}
|
||||
else
|
||||
{
|
||||
RenamedPlayers.Remove(player.SteamID);
|
||||
RenamedPlayers.Remove(steamId);
|
||||
}
|
||||
|
||||
Task.Run(() => PlayerManager.SaveRenamedPlayer(steamId, newName));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue