From 61cd13a4703a4872acd7795e25686a432857ea2b Mon Sep 17 00:00:00 2001 From: Simon <63975668+Simyon264@users.noreply.github.com> Date: Thu, 12 Mar 2026 19:40:47 +0100 Subject: [PATCH] Add banning to server api (#43205) add banning to server api --- Content.Server/Administration/ServerApi.cs | 80 ++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/Content.Server/Administration/ServerApi.cs b/Content.Server/Administration/ServerApi.cs index 7a1c6a82060..2f46d2d3272 100644 --- a/Content.Server/Administration/ServerApi.cs +++ b/Content.Server/Administration/ServerApi.cs @@ -7,8 +7,10 @@ using System.Text.Json; using System.Text.Json.Nodes; using System.Threading.Tasks; using Content.Server._NF.Administration; +using Content.Server.Administration.Managers; using Content.Server.Administration.Systems; using Content.Server.Administration.Managers; // Frontier +using Content.Server.Database; using Content.Server.GameTicking; using Content.Server.GameTicking.Presets; using Content.Server.GameTicking.Rules.Components; @@ -17,6 +19,7 @@ using Content.Server.RoundEnd; using Content.Shared.Administration.Managers; using Content.Shared.Administration; // Frontier using Content.Shared.CCVar; +using Content.Shared.Database; using Content.Shared.GameTicking.Components; using Content.Shared.Prototypes; using Robust.Server.ServerStatus; @@ -61,6 +64,9 @@ public sealed partial class ServerApi : IPostInjectInit [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!; [Dependency] private readonly ILocalizationManager _loc = default!; + [Dependency] private readonly IPlayerLocator _locator = default!; + [Dependency] private readonly IBanManager _bans = default!; + [Dependency] private readonly IServerDbManager _db = default!; private string _token = string.Empty; private ISawmill _sawmill = default!; @@ -79,6 +85,7 @@ public sealed partial class ServerApi : IPostInjectInit RegisterActorHandler(HttpMethod.Post, "/admin/actions/round/end", ActionRoundEnd); RegisterActorHandler(HttpMethod.Post, "/admin/actions/round/restartnow", ActionRoundRestartNow); RegisterActorHandler(HttpMethod.Post, "/admin/actions/kick", ActionKick); + RegisterActorHandler(HttpMethod.Post, "/admin/actions/ban", ActionBan); RegisterActorHandler(HttpMethod.Post, "/admin/actions/add_game_rule", ActionAddGameRule); RegisterActorHandler(HttpMethod.Post, "/admin/actions/end_game_rule", ActionEndGameRule); RegisterActorHandler(HttpMethod.Post, "/admin/actions/force_preset", ActionForcePreset); @@ -312,6 +319,70 @@ public sealed partial class ServerApi : IPostInjectInit }); } + /// + /// Bans a player. + /// + private async Task ActionBan(IStatusHandlerContext context, Actor actor) + { + var body = await ReadJson(context); + if (body == null) + return; + + await RunOnMainThread(async () => + { + var located = await _locator.LookupIdByNameOrIdAsync(body.Guid.ToString()); + + if (located == null) + { + await RespondError( + context, + ErrorCode.PlayerNotFound, + HttpStatusCode.UnprocessableContent, + "Player not found"); + return; + } + + var bans = await _db.GetBansAsync(userId: located.UserId, + address: null, + hwId: null, + modernHWIds: located.LastModernHWIds, + includeUnbanned: false); + if (bans.Count > 0) + { + await RespondError( + context, + ErrorCode.PlayerAlreadyBanned, + HttpStatusCode.Conflict, + "Player is already banned."); + return; + } + + var reason = body.Reason ?? "No reason supplied"; + var info = new CreateServerBanInfo(reason); + + info.AddHWId(located.LastHWId); + info.AddUser(located.UserId, located.Username); + info.WithSeverity(body.Severity); + if (body.Minutes != null && body.Minutes != 0) + { + info.WithMinutes(body.Minutes.Value); + } + + info.WithBanningAdmin(new NetUserId(actor.Guid)); + + // Add the ip if the user is currently connected. + if (_playerManager.TryGetSessionById(new NetUserId(body.Guid), out var player)) + { + info.AddAddress(player.Channel.RemoteEndPoint.Address); + } + + _bans.CreateServerBan(info); + await RespondOk(context); + + _sawmill.Info($"Banned player {located.Username} ({located.UserId}) for {reason} lasting {body.Minutes ?? 0} minutes by {FormatLogActor(actor)}"); + }); + } + /// /// Kicks a player. /// @@ -653,6 +724,14 @@ public sealed partial class ServerApi : IPostInjectInit public string? Reason { get; init; } } + private sealed class BanActionBody + { + public required Guid Guid { get; init; } + public string? Reason { get; init; } + public NoteSeverity Severity { get; init; } + public uint? Minutes { get; init; } + } + private sealed class GameRuleActionBody { public required string GameRuleId { get; init; } @@ -694,6 +773,7 @@ public sealed partial class ServerApi : IPostInjectInit PlayerNotFound = 4, GameRuleNotFound = 5, BadRequest = 6, + PlayerAlreadyBanned = 7, } #endregion