Save round information into replay_final.yml (#23013)
* 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
This commit is contained in:
@@ -2,6 +2,10 @@
|
|||||||
using Robust.Shared;
|
using Robust.Shared;
|
||||||
using Robust.Shared.ContentPack;
|
using Robust.Shared.ContentPack;
|
||||||
using Robust.Shared.Replays;
|
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;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.GameTicking;
|
namespace Content.Server.GameTicking;
|
||||||
@@ -10,12 +14,15 @@ public sealed partial class GameTicker
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IReplayRecordingManager _replays = default!;
|
[Dependency] private readonly IReplayRecordingManager _replays = default!;
|
||||||
[Dependency] private readonly IResourceManager _resourceManager = default!;
|
[Dependency] private readonly IResourceManager _resourceManager = default!;
|
||||||
|
[Dependency] private readonly ISerializationManager _serialman = default!;
|
||||||
|
|
||||||
|
|
||||||
private ISawmill _sawmillReplays = default!;
|
private ISawmill _sawmillReplays = default!;
|
||||||
|
|
||||||
private void InitializeReplays()
|
private void InitializeReplays()
|
||||||
{
|
{
|
||||||
_replays.RecordingFinished += ReplaysOnRecordingFinished;
|
_replays.RecordingFinished += ReplaysOnRecordingFinished;
|
||||||
|
_replays.RecordingStopped += ReplaysOnRecordingStopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -108,6 +115,20 @@ public sealed partial class GameTicker
|
|||||||
data.Directory.Rename(data.Path, state.MoveToPath.Value);
|
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()
|
private ResPath GetAutoReplayPath()
|
||||||
{
|
{
|
||||||
var cfgValue = _cfg.GetCVar(CCVars.ReplayAutoRecordName);
|
var cfgValue = _cfg.GetCVar(CCVars.ReplayAutoRecordName);
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ namespace Content.Server.GameTicking
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
private GameRunLevel _runLevel;
|
private GameRunLevel _runLevel;
|
||||||
|
|
||||||
|
private RoundEndMessageEvent.RoundEndPlayerInfo[]? _replayRoundPlayerInfo;
|
||||||
|
|
||||||
|
private string? _replayRoundText;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public GameRunLevel RunLevel
|
public GameRunLevel RunLevel
|
||||||
{
|
{
|
||||||
@@ -372,11 +376,14 @@ namespace Content.Server.GameTicking
|
|||||||
PlayerOOCName = contentPlayerData?.Name ?? "(IMPOSSIBLE: REGISTERED MIND WITH NO OWNER)",
|
PlayerOOCName = contentPlayerData?.Name ?? "(IMPOSSIBLE: REGISTERED MIND WITH NO OWNER)",
|
||||||
// Character name takes precedence over current entity name
|
// Character name takes precedence over current entity name
|
||||||
PlayerICName = playerIcName,
|
PlayerICName = playerIcName,
|
||||||
|
PlayerGuid = userId,
|
||||||
PlayerNetEntity = GetNetEntity(entity),
|
PlayerNetEntity = GetNetEntity(entity),
|
||||||
Role = antag
|
Role = antag
|
||||||
? roles.First(role => role.Antagonist).Name
|
? roles.First(role => role.Antagonist).Name
|
||||||
: roles.FirstOrDefault().Name ?? Loc.GetString("game-ticker-unknown-role"),
|
: roles.FirstOrDefault().Name ?? Loc.GetString("game-ticker-unknown-role"),
|
||||||
Antag = antag,
|
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,
|
Observer = observer,
|
||||||
Connected = connected
|
Connected = connected
|
||||||
};
|
};
|
||||||
@@ -389,6 +396,9 @@ namespace Content.Server.GameTicking
|
|||||||
|
|
||||||
RaiseNetworkEvent(new RoundEndMessageEvent(gamemodeTitle, roundEndText, roundDuration, RoundId,
|
RaiseNetworkEvent(new RoundEndMessageEvent(gamemodeTitle, roundEndText, roundDuration, RoundId,
|
||||||
listOfPlayerInfoFinal.Length, listOfPlayerInfoFinal, LobbySong, sound));
|
listOfPlayerInfoFinal.Length, listOfPlayerInfoFinal, LobbySong, sound));
|
||||||
|
|
||||||
|
_replayRoundPlayerInfo = listOfPlayerInfoFinal;
|
||||||
|
_replayRoundText = roundEndText;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void SendRoundEndDiscordMessage()
|
private async void SendRoundEndDiscordMessage()
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Shared.Roles;
|
using Content.Shared.Roles;
|
||||||
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.Replays;
|
using Robust.Shared.Replays;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||||
@@ -144,18 +145,37 @@ namespace Content.Shared.GameTicking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable, DataDefinition]
|
||||||
public sealed class RoundEndMessageEvent : EntityEventArgs
|
public sealed partial class RoundEndMessageEvent : EntityEventArgs
|
||||||
{
|
{
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable, DataDefinition]
|
||||||
public struct RoundEndPlayerInfo
|
public partial struct RoundEndPlayerInfo
|
||||||
{
|
{
|
||||||
|
[DataField]
|
||||||
public string PlayerOOCName;
|
public string PlayerOOCName;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
public string? PlayerICName;
|
public string? PlayerICName;
|
||||||
|
|
||||||
|
[DataField, NonSerialized]
|
||||||
|
public NetUserId? PlayerGuid;
|
||||||
|
|
||||||
public string Role;
|
public string Role;
|
||||||
|
|
||||||
|
[DataField, NonSerialized]
|
||||||
|
public string[] JobPrototypes;
|
||||||
|
|
||||||
|
[DataField, NonSerialized]
|
||||||
|
public string[] AntagPrototypes;
|
||||||
|
|
||||||
public NetEntity? PlayerNetEntity;
|
public NetEntity? PlayerNetEntity;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
public bool Antag;
|
public bool Antag;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
public bool Observer;
|
public bool Observer;
|
||||||
|
|
||||||
public bool Connected;
|
public bool Connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,4 +16,5 @@ public readonly record struct MindGetAllRolesEvent(List<RoleInfo> Roles);
|
|||||||
/// <param name="Name">Name of the role.</param>
|
/// <param name="Name">Name of the role.</param>
|
||||||
/// <param name="Antagonist">Whether or not this role makes this player an antagonist.</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>
|
/// <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);
|
||||||
|
|||||||
@@ -27,16 +27,18 @@ public abstract class SharedRoleSystem : EntitySystem
|
|||||||
private void OnJobGetAllRoles(EntityUid uid, JobComponent component, ref MindGetAllRolesEvent args)
|
private void OnJobGetAllRoles(EntityUid uid, JobComponent component, ref MindGetAllRolesEvent args)
|
||||||
{
|
{
|
||||||
var name = "game-ticker-unknown-role";
|
var name = "game-ticker-unknown-role";
|
||||||
|
var prototype = "";
|
||||||
string? playTimeTracker = null;
|
string? playTimeTracker = null;
|
||||||
if (component.Prototype != null && _prototypes.TryIndex(component.Prototype, out JobPrototype? job))
|
if (component.Prototype != null && _prototypes.TryIndex(component.Prototype, out JobPrototype? job))
|
||||||
{
|
{
|
||||||
name = job.Name;
|
name = job.Name;
|
||||||
|
prototype = job.ID;
|
||||||
playTimeTracker = job.PlayTimeTracker;
|
playTimeTracker = job.PlayTimeTracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
name = Loc.GetString(name);
|
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
|
protected void SubscribeAntagEvents<T>() where T : AntagonistRoleComponent
|
||||||
@@ -44,13 +46,15 @@ public abstract class SharedRoleSystem : EntitySystem
|
|||||||
SubscribeLocalEvent((EntityUid _, T component, ref MindGetAllRolesEvent args) =>
|
SubscribeLocalEvent((EntityUid _, T component, ref MindGetAllRolesEvent args) =>
|
||||||
{
|
{
|
||||||
var name = "game-ticker-unknown-role";
|
var name = "game-ticker-unknown-role";
|
||||||
|
var prototype = "";
|
||||||
if (component.PrototypeId != null && _prototypes.TryIndex(component.PrototypeId, out AntagPrototype? antag))
|
if (component.PrototypeId != null && _prototypes.TryIndex(component.PrototypeId, out AntagPrototype? antag))
|
||||||
{
|
{
|
||||||
name = antag.Name;
|
name = antag.Name;
|
||||||
|
prototype = antag.ID;
|
||||||
}
|
}
|
||||||
name = Loc.GetString(name);
|
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);
|
SubscribeLocalEvent((EntityUid _, T _, ref MindIsAntagonistEvent args) => args.IsAntagonist = true);
|
||||||
|
|||||||
Reference in New Issue
Block a user