]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Save round information into replay_final.yml (#23013)
authorVasilis <vasilis@pikachu.systems>
Sat, 17 Feb 2024 20:30:54 +0000 (21:30 +0100)
committerGitHub <noreply@github.com>
Sat, 17 Feb 2024 20:30:54 +0000 (21:30 +0100)
* Save round information into the replay

* Add round end text too

* This is way better

* Get actual job

* oop

* OK THERE

* Fake line endings to make life easier

* I was told this yaml is legal

* I just realised this will make my life easier

* REVIEWS BABY IM A PROGRAMMER MOMMY

* Live pjb reaction

* Live pjb reaction 2

* Reviews 2

* Dont need this

* Please no more have mercy on my soul

* Oh frick

Content.Server/GameTicking/GameTicker.Replays.cs
Content.Server/GameTicking/GameTicker.RoundFlow.cs
Content.Shared/GameTicking/SharedGameTicker.cs
Content.Shared/Roles/MindGetAllRolesEvent.cs
Content.Shared/Roles/SharedRoleSystem.cs

index 7e1a553a8568027de6363f20e7c4d82a97af5a84..f23482585cc37f929a55f63a82d25a2c2defb978 100644 (file)
@@ -2,6 +2,10 @@
 using Robust.Shared;
 using Robust.Shared.ContentPack;
 using Robust.Shared.Replays;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Mapping;
+using Robust.Shared.Serialization.Markdown.Value;
 using Robust.Shared.Utility;
 
 namespace Content.Server.GameTicking;
@@ -10,12 +14,15 @@ public sealed partial class GameTicker
 {
     [Dependency] private readonly IReplayRecordingManager _replays = default!;
     [Dependency] private readonly IResourceManager _resourceManager = default!;
+    [Dependency] private readonly ISerializationManager _serialman = default!;
+
 
     private ISawmill _sawmillReplays = default!;
 
     private void InitializeReplays()
     {
         _replays.RecordingFinished += ReplaysOnRecordingFinished;
+        _replays.RecordingStopped += ReplaysOnRecordingStopped;
     }
 
     /// <summary>
@@ -108,6 +115,20 @@ public sealed partial class GameTicker
         data.Directory.Rename(data.Path, state.MoveToPath.Value);
     }
 
+    private void ReplaysOnRecordingStopped(MappingDataNode metadata)
+    {
+        // Write round info like map and round end summery into the replay_final.yml file. Useful for external parsers.
+
+        metadata["map"] = new ValueDataNode(_gameMapManager.GetSelectedMap()?.MapName);
+        metadata["gamemode"] = new ValueDataNode(CurrentPreset != null ? Loc.GetString(CurrentPreset.ModeTitle) : string.Empty);
+        metadata["roundEndPlayers"] = _serialman.WriteValue(_replayRoundPlayerInfo);
+        metadata["roundEndText"] = new ValueDataNode(_replayRoundText);
+        metadata["server_id"] = new ValueDataNode(_configurationManager.GetCVar(CCVars.ServerId));
+        // These should be set to null to prepare them for the next round.
+        _replayRoundPlayerInfo = null;
+        _replayRoundText = null;
+    }
+
     private ResPath GetAutoReplayPath()
     {
         var cfgValue = _cfg.GetCVar(CCVars.ReplayAutoRecordName);
index 6f0463aaf8fd2323a4232ec0a1e1c503b6d4dbe2..ffe26aeb346ad3868a69022d1a8dc2582c78c84e 100644 (file)
@@ -46,6 +46,10 @@ namespace Content.Server.GameTicking
         [ViewVariables]
         private GameRunLevel _runLevel;
 
+        private RoundEndMessageEvent.RoundEndPlayerInfo[]? _replayRoundPlayerInfo;
+
+        private string? _replayRoundText;
+
         [ViewVariables]
         public GameRunLevel RunLevel
         {
@@ -372,11 +376,14 @@ namespace Content.Server.GameTicking
                     PlayerOOCName = contentPlayerData?.Name ?? "(IMPOSSIBLE: REGISTERED MIND WITH NO OWNER)",
                     // Character name takes precedence over current entity name
                     PlayerICName = playerIcName,
+                    PlayerGuid = userId,
                     PlayerNetEntity = GetNetEntity(entity),
                     Role = antag
                         ? roles.First(role => role.Antagonist).Name
                         : roles.FirstOrDefault().Name ?? Loc.GetString("game-ticker-unknown-role"),
                     Antag = antag,
+                    JobPrototypes = roles.Where(role => !role.Antagonist).Select(role => role.Prototype).ToArray(),
+                    AntagPrototypes = roles.Where(role => role.Antagonist).Select(role => role.Prototype).ToArray(),
                     Observer = observer,
                     Connected = connected
                 };
@@ -389,6 +396,9 @@ namespace Content.Server.GameTicking
 
             RaiseNetworkEvent(new RoundEndMessageEvent(gamemodeTitle, roundEndText, roundDuration, RoundId,
                 listOfPlayerInfoFinal.Length, listOfPlayerInfoFinal, LobbySong, sound));
+
+            _replayRoundPlayerInfo = listOfPlayerInfoFinal;
+            _replayRoundText = roundEndText;
         }
 
         private async void SendRoundEndDiscordMessage()
index 7778588f97cfa4ff1733483b09ffef6e2440614b..2677d499c2ff996d86a0fbb0f34f698457e1223e 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Shared.Roles;
+using Robust.Shared.Network;
 using Robust.Shared.Replays;
 using Robust.Shared.Serialization;
 using Robust.Shared.Serialization.Markdown.Mapping;
@@ -144,18 +145,37 @@ namespace Content.Shared.GameTicking
         }
     }
 
-    [Serializable, NetSerializable]
-    public sealed class RoundEndMessageEvent : EntityEventArgs
+    [Serializable, NetSerializable, DataDefinition]
+    public sealed partial class RoundEndMessageEvent : EntityEventArgs
     {
-        [Serializable, NetSerializable]
-        public struct RoundEndPlayerInfo
+        [Serializable, NetSerializable, DataDefinition]
+        public partial struct RoundEndPlayerInfo
         {
+            [DataField]
             public string PlayerOOCName;
+
+            [DataField]
             public string? PlayerICName;
+
+            [DataField, NonSerialized]
+            public NetUserId? PlayerGuid;
+
             public string Role;
+
+            [DataField, NonSerialized]
+            public string[] JobPrototypes;
+
+            [DataField, NonSerialized]
+            public string[] AntagPrototypes;
+
             public NetEntity? PlayerNetEntity;
+
+            [DataField]
             public bool Antag;
+
+            [DataField]
             public bool Observer;
+
             public bool Connected;
         }
 
index 9313d94edfd6714314ff929acb2feed9fec616f6..69878739084bc7ecc65546b2ba438519c59e1f4e 100644 (file)
@@ -16,4 +16,5 @@ public readonly record struct MindGetAllRolesEvent(List<RoleInfo> Roles);
 /// <param name="Name">Name of the role.</param>
 /// <param name="Antagonist">Whether or not this role makes this player an antagonist.</param>
 /// <param name="PlayTimeTrackerId">The <see cref="PlayTimeTrackerPrototype"/> id associated with the role.</param>
-public readonly record struct RoleInfo(Component Component, string Name, bool Antagonist, string? PlayTimeTrackerId);
+/// <param name="Prototype">The prototype ID of the role</param>
+public readonly record struct RoleInfo(Component Component, string Name, bool Antagonist, string? PlayTimeTrackerId, string Prototype);
index 05d6ab9f37f3d2dabae95af28cddd8c0c50cbd95..24db1d567747748979a462d264a7af2a5977df8f 100644 (file)
@@ -27,16 +27,18 @@ public abstract class SharedRoleSystem : EntitySystem
     private void OnJobGetAllRoles(EntityUid uid, JobComponent component, ref MindGetAllRolesEvent args)
     {
         var name = "game-ticker-unknown-role";
+        var prototype = "";
         string? playTimeTracker = null;
         if (component.Prototype != null && _prototypes.TryIndex(component.Prototype, out JobPrototype? job))
         {
             name = job.Name;
+            prototype = job.ID;
             playTimeTracker = job.PlayTimeTracker;
         }
 
         name = Loc.GetString(name);
 
-        args.Roles.Add(new RoleInfo(component, name, false, playTimeTracker));
+        args.Roles.Add(new RoleInfo(component, name, false, playTimeTracker, prototype));
     }
 
     protected void SubscribeAntagEvents<T>() where T : AntagonistRoleComponent
@@ -44,13 +46,15 @@ public abstract class SharedRoleSystem : EntitySystem
         SubscribeLocalEvent((EntityUid _, T component, ref MindGetAllRolesEvent args) =>
         {
             var name = "game-ticker-unknown-role";
+            var prototype = "";
             if (component.PrototypeId != null && _prototypes.TryIndex(component.PrototypeId, out AntagPrototype? antag))
             {
                 name = antag.Name;
+                prototype = antag.ID;
             }
             name = Loc.GetString(name);
 
-            args.Roles.Add(new RoleInfo(component, name, true, null));
+            args.Roles.Add(new RoleInfo(component, name, true, null, prototype));
         });
 
         SubscribeLocalEvent((EntityUid _, T _, ref MindIsAntagonistEvent args) => args.IsAntagonist = true);