diff --git a/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs b/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs index 7fce4f1f09..f327287bd1 100644 --- a/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs +++ b/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Content.Server.Administration.Logs; using Content.Server.Database; using Content.Shared.Administration.Logs; using Content.Shared.CCVar; using NUnit.Framework; +using Robust.Server.Player; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -194,4 +196,49 @@ public class AddTests : ContentIntegrationTest return count >= amount; }); } + + [Test] + public async Task AddPlayerSessionLog() + { + var (client, server) = await StartConnectedServerClientPair(serverOptions: new ServerContentIntegrationOption + { + CVarOverrides = + { + [CCVars.AdminLogsQueueSendDelay.Name] = "0" + }, + Pool = true + }); + + await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync()); + + var sPlayers = server.ResolveDependency(); + var sSystems = server.ResolveDependency(); + + var sAdminLogSystem = sSystems.GetEntitySystem(); + Guid playerGuid = default; + + await server.WaitPost(() => + {; + var player = sPlayers.ServerSessions.First(); + playerGuid = player.UserId; + + Assert.DoesNotThrow(() => + { + sAdminLogSystem.Add(LogType.Unknown, $"{player:Player} test log."); + }); + }); + + await WaitUntil(server, async () => + { + var logs = sAdminLogSystem.CurrentRoundLogs(); + + await foreach (var log in logs) + { + Assert.That(log.Players, Does.Contain(playerGuid)); + return true; + } + + return false; + }); + } } diff --git a/Content.Server/Administration/Logs/AdminLogSystem.Json.cs b/Content.Server/Administration/Logs/AdminLogSystem.Json.cs new file mode 100644 index 0000000000..5a1f764ed1 --- /dev/null +++ b/Content.Server/Administration/Logs/AdminLogSystem.Json.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using Content.Server.Administration.Logs.Converters; +using Robust.Server.GameObjects; +using Robust.Server.Player; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; + +namespace Content.Server.Administration.Logs; + +public partial class AdminLogSystem +{ + private static readonly JsonNamingPolicy NamingPolicy = JsonNamingPolicy.CamelCase; + + // Init only + private JsonSerializerOptions _jsonOptions = default!; + + private void InitializeJson() + { + _jsonOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = NamingPolicy + }; + + foreach (var converter in _reflection.FindTypesWithAttribute()) + { + var instance = _typeFactory.CreateInstance(converter); + _jsonOptions.Converters.Add(instance); + } + + var converterNames = _jsonOptions.Converters.Select(converter => converter.GetType().Name); + _sawmill.Info($"Admin log converters found: {string.Join(" ", converterNames)}"); + } + + private (JsonDocument json, List players, List<(int id, string? name)> entities) ToJson( + Dictionary properties) + { + var entities = new List<(int id, string? name)>(); + var players = new List(); + var parsed = new Dictionary(); + + foreach (var key in properties.Keys) + { + var value = properties[key]; + value = value switch + { + IPlayerSession player => new SerializablePlayer(player), + _ => value + }; + + var parsedKey = NamingPolicy.ConvertName(key); + parsed.Add(parsedKey, value); + + EntityUid? entityId = properties[key] switch + { + EntityUid id => id, + IEntity entity => entity.Uid, + IPlayerSession {AttachedEntityUid: { }} session => session.AttachedEntityUid.Value, + IComponent component => component.OwnerUid, + _ => null + }; + + if (entityId is not { } uid) + { + continue; + } + + var entityName = _entityManager.TryGetEntity(uid, out var resolvedEntity) + ? resolvedEntity.Name + : null; + + entities.Add(((int) uid, entityName)); + + if (_entityManager.TryGetComponent(uid, out ActorComponent? actor)) + { + players.Add(actor.PlayerSession.UserId.UserId); + } + } + + return (JsonSerializer.SerializeToDocument(parsed, _jsonOptions), players, entities); + } + +} diff --git a/Content.Server/Administration/Logs/AdminLogSystem.cs b/Content.Server/Administration/Logs/AdminLogSystem.cs index 75364d4507..8c25249e0d 100644 --- a/Content.Server/Administration/Logs/AdminLogSystem.cs +++ b/Content.Server/Administration/Logs/AdminLogSystem.cs @@ -1,19 +1,14 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Linq; using System.Text.Json; -using System.Text.Json.Serialization; using System.Threading.Tasks; -using Content.Server.Administration.Logs.Converters; using Content.Server.Database; using Content.Server.GameTicking; using Content.Server.GameTicking.Events; using Content.Shared.Administration.Logs; using Content.Shared.CCVar; using Prometheus; -using Robust.Server.GameObjects; -using Robust.Server.Player; using Robust.Shared; using Robust.Shared.Configuration; using Robust.Shared.GameObjects; @@ -23,7 +18,7 @@ using Robust.Shared.Reflection; namespace Content.Server.Administration.Logs; -public class AdminLogSystem : SharedAdminLogSystem +public partial class AdminLogSystem : SharedAdminLogSystem { [Dependency] private readonly IConfigurationManager _configuration = default!; [Dependency] private readonly IEntityManager _entityManager = default!; @@ -52,11 +47,8 @@ public class AdminLogSystem : SharedAdminLogSystem "admin_logs_sent", "Amount of logs sent to the database in a round."); - private static readonly JsonNamingPolicy NamingPolicy = JsonNamingPolicy.CamelCase; - // Init only private ISawmill _sawmill = default!; - private JsonSerializerOptions _jsonOptions = default!; // CVars private bool _metricsEnabled; @@ -74,19 +66,8 @@ public class AdminLogSystem : SharedAdminLogSystem base.Initialize(); _sawmill = _logManager.GetSawmill(SawmillId); - _jsonOptions = new JsonSerializerOptions - { - PropertyNamingPolicy = NamingPolicy - }; - foreach (var converter in _reflection.FindTypesWithAttribute()) - { - var instance = _typeFactory.CreateInstance(converter); - _jsonOptions.Converters.Add(instance); - } - - var converterNames = _jsonOptions.Converters.Select(converter => converter.GetType().Name); - _sawmill.Info($"Admin log converters found: {string.Join(" ", converterNames)}"); + InitializeJson(); _configuration.OnValueChanged(CVars.MetricsEnabled, value => _metricsEnabled = value, true); @@ -171,48 +152,6 @@ public class AdminLogSystem : SharedAdminLogSystem } } - public (JsonDocument json, List players, List<(int id, string? name)> entities) ToJson( - Dictionary properties) - { - var entities = new List<(int id, string? name)>(); - var players = new List(); - var parsed = new Dictionary(); - - foreach (var key in properties.Keys) - { - var value = properties[key]; - var parsedKey = NamingPolicy.ConvertName(key); - parsed.Add(parsedKey, value); - - EntityUid? entityId = properties[key] switch - { - EntityUid id => id, - IEntity entity => entity.Uid, - IPlayerSession {AttachedEntityUid: { }} session => session.AttachedEntityUid.Value, - IComponent component => component.OwnerUid, - _ => null - }; - - if (entityId is not { } uid) - { - continue; - } - - var entityName = _entityManager.TryGetEntity(uid, out var resolvedEntity) - ? resolvedEntity.Name - : null; - - entities.Add(((int) uid, entityName)); - - if (_entityManager.TryGetComponent(uid, out ActorComponent? actor)) - { - players.Add(actor.PlayerSession.UserId.UserId); - } - } - - return (JsonSerializer.SerializeToDocument(parsed, _jsonOptions), players, entities); - } - private async void Add(LogType type, LogImpact impact, string message, JsonDocument json, List players, List<(int id, string? name)> entities) { var log = new AdminLog diff --git a/Content.Server/Administration/Logs/Converters/PlayerSessionConverter.cs b/Content.Server/Administration/Logs/Converters/PlayerSessionConverter.cs new file mode 100644 index 0000000000..275dbc617c --- /dev/null +++ b/Content.Server/Administration/Logs/Converters/PlayerSessionConverter.cs @@ -0,0 +1,33 @@ +using System.Text.Json; +using Robust.Server.Player; + +namespace Content.Server.Administration.Logs.Converters; + +[AdminLogConverter] +public class PlayerSessionConverter : AdminLogConverter +{ + public override void Write(Utf8JsonWriter writer, SerializablePlayer value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + + if (value.Player.AttachedEntity != null) + { + writer.WriteNumber("id", (int) value.Player.AttachedEntity.Uid); + writer.WriteString("name", value.Player.AttachedEntity.Name); + } + + writer.WriteString("player", value.Player.UserId.UserId); + + writer.WriteEndObject(); + } +} + +public readonly struct SerializablePlayer +{ + public readonly IPlayerSession Player; + + public SerializablePlayer(IPlayerSession player) + { + Player = player; + } +} diff --git a/Content.Shared/Administration/Logs/LogType.cs b/Content.Shared/Administration/Logs/LogType.cs index 1bc45c4215..20485b37f2 100644 --- a/Content.Shared/Administration/Logs/LogType.cs +++ b/Content.Shared/Administration/Logs/LogType.cs @@ -10,7 +10,7 @@ public enum LogType Slip = 4, EventAnnounced = 5, EventStarted = 6, - EventRan = 6, + EventRan = 16, EventStopped = 7, ShuttleCalled = 8, ShuttleRecalled = 9, diff --git a/Content.Shared/Slippery/SharedSlipperySystem.cs b/Content.Shared/Slippery/SharedSlipperySystem.cs index e5c160ee6b..08a95403b3 100644 --- a/Content.Shared/Slippery/SharedSlipperySystem.cs +++ b/Content.Shared/Slippery/SharedSlipperySystem.cs @@ -100,7 +100,7 @@ namespace Content.Shared.Slippery PlaySound(component); - _adminLog.Add(LogType.Slip, $"{component.Owner} slipped on collision with {otherBody.Owner}"); + _adminLog.Add(LogType.Slip, LogImpact.Low, $"{component.Owner} slipped on collision with {otherBody.Owner}"); return true; }