Add support for client-side replays (#17168)

This commit is contained in:
Leon Friedrich
2023-06-19 05:23:31 +12:00
committed by GitHub
parent cc81a7511b
commit b03d9a90ab
22 changed files with 183 additions and 91 deletions

View File

@@ -11,6 +11,7 @@ using Robust.Shared.Map;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Players; using Robust.Shared.Players;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Replays;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -26,6 +27,7 @@ namespace Content.Client.Popups
[Dependency] private readonly IResourceCache _resource = default!; [Dependency] private readonly IResourceCache _resource = default!;
[Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IUserInterfaceManager _uiManager = default!; [Dependency] private readonly IUserInterfaceManager _uiManager = default!;
[Dependency] private readonly IReplayRecordingManager _replayRecording = default!;
public IReadOnlyList<WorldPopupLabel> WorldLabels => _aliveWorldLabels; public IReadOnlyList<WorldPopupLabel> WorldLabels => _aliveWorldLabels;
public IReadOnlyList<CursorPopupLabel> CursorLabels => _aliveCursorLabels; public IReadOnlyList<CursorPopupLabel> CursorLabels => _aliveCursorLabels;
@@ -52,8 +54,16 @@ namespace Content.Client.Popups
.RemoveOverlay<PopupOverlay>(); .RemoveOverlay<PopupOverlay>();
} }
private void PopupMessage(string message, PopupType type, EntityCoordinates coordinates, EntityUid? entity = null) private void PopupMessage(string message, PopupType type, EntityCoordinates coordinates, EntityUid? entity, bool recordReplay)
{ {
if (recordReplay && _replayRecording.IsRecording)
{
if (entity != null)
_replayRecording.RecordClientMessage(new PopupEntityEvent(message, type, entity.Value));
else
_replayRecording.RecordClientMessage(new PopupCoordinatesEvent(message, type, coordinates));
}
var label = new WorldPopupLabel(coordinates) var label = new WorldPopupLabel(coordinates)
{ {
Text = message, Text = message,
@@ -66,23 +76,26 @@ namespace Content.Client.Popups
#region Abstract Method Implementations #region Abstract Method Implementations
public override void PopupCoordinates(string message, EntityCoordinates coordinates, PopupType type = PopupType.Small) public override void PopupCoordinates(string message, EntityCoordinates coordinates, PopupType type = PopupType.Small)
{ {
PopupMessage(message, type, coordinates, null); PopupMessage(message, type, coordinates, null, true);
} }
public override void PopupCoordinates(string message, EntityCoordinates coordinates, ICommonSession recipient, PopupType type = PopupType.Small) public override void PopupCoordinates(string message, EntityCoordinates coordinates, ICommonSession recipient, PopupType type = PopupType.Small)
{ {
if (_playerManager.LocalPlayer?.Session == recipient) if (_playerManager.LocalPlayer?.Session == recipient)
PopupMessage(message, type, coordinates, null); PopupMessage(message, type, coordinates, null, true);
} }
public override void PopupCoordinates(string message, EntityCoordinates coordinates, EntityUid recipient, PopupType type = PopupType.Small) public override void PopupCoordinates(string message, EntityCoordinates coordinates, EntityUid recipient, PopupType type = PopupType.Small)
{ {
if (_playerManager.LocalPlayer?.ControlledEntity == recipient) if (_playerManager.LocalPlayer?.ControlledEntity == recipient)
PopupMessage(message, type, coordinates, null); PopupMessage(message, type, coordinates, null, true);
} }
public override void PopupCursor(string message, PopupType type = PopupType.Small) private void PopupCursorInternal(string message, PopupType type, bool recordReplay)
{ {
if (recordReplay && _replayRecording.IsRecording)
_replayRecording.RecordClientMessage(new PopupCursorEvent(message, type));
var label = new CursorPopupLabel(_inputManager.MouseScreenPosition) var label = new CursorPopupLabel(_inputManager.MouseScreenPosition)
{ {
Text = message, Text = message,
@@ -92,6 +105,9 @@ namespace Content.Client.Popups
_aliveCursorLabels.Add(label); _aliveCursorLabels.Add(label);
} }
public override void PopupCursor(string message, PopupType type = PopupType.Small)
=> PopupCursorInternal(message, type, true);
public override void PopupCursor(string message, ICommonSession recipient, PopupType type = PopupType.Small) public override void PopupCursor(string message, ICommonSession recipient, PopupType type = PopupType.Small)
{ {
if (_playerManager.LocalPlayer?.Session == recipient) if (_playerManager.LocalPlayer?.Session == recipient)
@@ -137,12 +153,8 @@ namespace Content.Client.Popups
public override void PopupEntity(string message, EntityUid uid, PopupType type = PopupType.Small) public override void PopupEntity(string message, EntityUid uid, PopupType type = PopupType.Small)
{ {
if (!EntityManager.EntityExists(uid)) if (TryComp(uid, out TransformComponent? transform))
return; PopupMessage(message, type, transform.Coordinates, uid, true);
var transform = EntityManager.GetComponent<TransformComponent>(uid);
PopupMessage(message, type, transform.Coordinates, uid);
} }
#endregion #endregion
@@ -151,17 +163,18 @@ namespace Content.Client.Popups
private void OnPopupCursorEvent(PopupCursorEvent ev) private void OnPopupCursorEvent(PopupCursorEvent ev)
{ {
PopupCursor(ev.Message, ev.Type); PopupCursorInternal(ev.Message, ev.Type, false);
} }
private void OnPopupCoordinatesEvent(PopupCoordinatesEvent ev) private void OnPopupCoordinatesEvent(PopupCoordinatesEvent ev)
{ {
PopupCoordinates(ev.Message, ev.Coordinates, ev.Type); PopupMessage(ev.Message, ev.Type, ev.Coordinates, null, false);
} }
private void OnPopupEntityEvent(PopupEntityEvent ev) private void OnPopupEntityEvent(PopupEntityEvent ev)
{ {
PopupEntity(ev.Message, ev.Uid, ev.Type); if (TryComp(ev.Uid, out TransformComponent? transform))
PopupMessage(ev.Message, ev.Type, transform.Coordinates, ev.Uid, false);
} }
private void OnRoundRestart(RoundRestartCleanupEvent ev) private void OnRoundRestart(RoundRestartCleanupEvent ev)

View File

@@ -22,6 +22,7 @@ using Robust.Client.State;
using Robust.Client.Timing; using Robust.Client.Timing;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Shared.ContentPack; using Robust.Shared.ContentPack;
using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Client.Replay; namespace Content.Client.Replay;
@@ -99,6 +100,8 @@ public sealed class ContentReplayPlaybackManager
{ {
switch (message) switch (message)
{ {
case BoundUserInterfaceMessage:
break; // TODO REPLAYS refactor BUIs
case ChatMessage chat: case ChatMessage chat:
// Just pass on the chat message to the UI controller, but skip speech-bubbles if we are fast-forwarding. // Just pass on the chat message to the UI controller, but skip speech-bubbles if we are fast-forwarding.
_uiMan.GetUIController<ChatUIController>().ProcessChatMessage(chat, speechBubble: !skipEffects); _uiMan.GetUIController<ChatUIController>().ProcessChatMessage(chat, speechBubble: !skipEffects);
@@ -129,8 +132,7 @@ public sealed class ContentReplayPlaybackManager
return false; return false;
} }
private void OnReplayPlaybackStarted(MappingDataNode metadata, List<object> objects)
private void OnReplayPlaybackStarted()
{ {
_conGrp.Implementation = new ReplayConGroup(); _conGrp.Implementation = new ReplayConGroup();
} }

View File

@@ -3,6 +3,7 @@ using Content.Shared.Movement.Components;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Network;
namespace Content.Client.Replay.Spectator; namespace Content.Client.Replay.Spectator;
@@ -13,61 +14,99 @@ public sealed partial class ReplaySpectatorSystem
/// <summary> /// <summary>
/// Simple struct containing position & rotation data for maintaining a persistent view when jumping around in time. /// Simple struct containing position & rotation data for maintaining a persistent view when jumping around in time.
/// </summary> /// </summary>
public struct SpectatorPosition public struct SpectatorData
{ {
// TODO REPLAYS handle ghost-following. // TODO REPLAYS handle ghost-following.
/// <summary>
/// The current entity being spectated.
/// </summary>
public EntityUid Entity; public EntityUid Entity;
/// <summary>
/// The player that was originally controlling <see cref="Entity"/>
/// </summary>
public NetUserId? Controller;
public (EntityCoordinates Coords, Angle Rot)? Local; public (EntityCoordinates Coords, Angle Rot)? Local;
public (EntityCoordinates Coords, Angle Rot)? World; public (EntityCoordinates Coords, Angle Rot)? World;
public (EntityUid? Ent, Angle Rot)? Eye; public (EntityUid? Ent, Angle Rot)? Eye;
} }
public SpectatorPosition GetSpectatorPosition() public SpectatorData GetSpectatorData()
{ {
var obs = new SpectatorPosition(); var data = new SpectatorData();
if (_player.LocalPlayer?.ControlledEntity is { } player && TryComp(player, out TransformComponent? xform) && xform.MapUid != null)
if (_player.LocalPlayer?.ControlledEntity is not { } player)
return data;
foreach (var session in _player.Sessions)
{ {
obs.Local = (xform.Coordinates, xform.LocalRotation); if (session.UserId == _player.LocalPlayer?.UserId)
obs.World = (new(xform.MapUid.Value, xform.WorldPosition), xform.WorldRotation); continue;
if (TryComp(player, out InputMoverComponent? mover)) if (session.AttachedEntity == player)
obs.Eye = (mover.RelativeEntity, mover.TargetRelativeRotation); {
data.Controller = session.UserId;
obs.Entity = player; break;
}
} }
return obs; if (!TryComp(player, out TransformComponent? xform) || xform.MapUid == null)
return data;
data.Local = (xform.Coordinates, xform.LocalRotation);
data.World = (new(xform.MapUid.Value, xform.WorldPosition), xform.WorldRotation);
if (TryComp(player, out InputMoverComponent? mover))
data.Eye = (mover.RelativeEntity, mover.TargetRelativeRotation);
data.Entity = player;
return data;
} }
private void OnBeforeSetTick() private void OnBeforeSetTick()
{ {
_oldPosition = GetSpectatorPosition(); _spectatorData = GetSpectatorData();
} }
private void OnAfterSetTick() private void OnAfterSetTick()
{ {
if (_oldPosition != null) if (_spectatorData != null)
SetSpectatorPosition(_oldPosition.Value); SetSpectatorPosition(_spectatorData.Value);
_oldPosition = null; _spectatorData = null;
} }
public void SetSpectatorPosition(SpectatorPosition spectatorPosition) public void SetSpectatorPosition(SpectatorData data)
{ {
if (Exists(spectatorPosition.Entity) && Transform(spectatorPosition.Entity).MapID != MapId.Nullspace) if (_player.LocalPlayer == null)
return;
if (data.Controller != null
&& _player.SessionsDict.TryGetValue(data.Controller.Value, out var session)
&& Exists(session.AttachedEntity)
&& Transform(session.AttachedEntity.Value).MapID != MapId.Nullspace)
{ {
_player.LocalPlayer!.AttachEntity(spectatorPosition.Entity, EntityManager, _client); _player.LocalPlayer.AttachEntity(session.AttachedEntity.Value, EntityManager, _client);
return; return;
} }
if (spectatorPosition.Local != null && spectatorPosition.Local.Value.Coords.IsValid(EntityManager)) if (Exists(data.Entity) && Transform(data.Entity).MapID != MapId.Nullspace)
{ {
var newXform = SpawnSpectatorGhost(spectatorPosition.Local.Value.Coords, false); _player.LocalPlayer.AttachEntity(data.Entity, EntityManager, _client);
newXform.LocalRotation = spectatorPosition.Local.Value.Rot; return;
} }
else if (spectatorPosition.World != null && spectatorPosition.World.Value.Coords.IsValid(EntityManager))
if (data.Local != null && data.Local.Value.Coords.IsValid(EntityManager))
{ {
var newXform = SpawnSpectatorGhost(spectatorPosition.World.Value.Coords, true); var newXform = SpawnSpectatorGhost(data.Local.Value.Coords, false);
newXform.LocalRotation = spectatorPosition.World.Value.Rot; newXform.LocalRotation = data.Local.Value.Rot;
}
else if (data.World != null && data.World.Value.Coords.IsValid(EntityManager))
{
var newXform = SpawnSpectatorGhost(data.World.Value.Coords, true);
newXform.LocalRotation = data.World.Value.Rot;
} }
else if (TryFindFallbackSpawn(out var coords)) else if (TryFindFallbackSpawn(out var coords))
{ {
@@ -80,15 +119,21 @@ public sealed partial class ReplaySpectatorSystem
return; return;
} }
if (spectatorPosition.Eye != null && TryComp(_player.LocalPlayer?.ControlledEntity, out InputMoverComponent? newMover)) if (data.Eye != null && TryComp(_player.LocalPlayer.ControlledEntity, out InputMoverComponent? newMover))
{ {
newMover.RelativeEntity = spectatorPosition.Eye.Value.Ent; newMover.RelativeEntity = data.Eye.Value.Ent;
newMover.TargetRelativeRotation = newMover.RelativeRotation = spectatorPosition.Eye.Value.Rot; newMover.TargetRelativeRotation = newMover.RelativeRotation = data.Eye.Value.Rot;
} }
} }
private bool TryFindFallbackSpawn(out EntityCoordinates coords) private bool TryFindFallbackSpawn(out EntityCoordinates coords)
{ {
if (_replayPlayback.TryGetRecorderEntity(out var recorder))
{
coords = new EntityCoordinates(recorder.Value, default);
return true;
}
var uid = EntityQuery<MapGridComponent>() var uid = EntityQuery<MapGridComponent>()
.OrderByDescending(x => x.LocalAABB.Size.LengthSquared) .OrderByDescending(x => x.LocalAABB.Size.LengthSquared)
.FirstOrDefault()?.Owner; .FirstOrDefault()?.Owner;

View File

@@ -84,6 +84,7 @@ public sealed partial class ReplaySpectatorSystem
_stateMan.RequestStateChange<ReplayGhostState>(); _stateMan.RequestStateChange<ReplayGhostState>();
_spectatorData = GetSpectatorData();
return xform; return xform;
} }

View File

@@ -6,6 +6,8 @@ using Robust.Client.Player;
using Robust.Client.Replays.Playback; using Robust.Client.Replays.Playback;
using Robust.Client.State; using Robust.Client.State;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.Network;
using Robust.Shared.Serialization.Markdown.Mapping;
namespace Content.Client.Replay.Spectator; namespace Content.Client.Replay.Spectator;
@@ -29,7 +31,7 @@ public sealed partial class ReplaySpectatorSystem : EntitySystem
[Dependency] private readonly SharedContentEyeSystem _eye = default!; [Dependency] private readonly SharedContentEyeSystem _eye = default!;
[Dependency] private readonly IReplayPlaybackManager _replayPlayback = default!; [Dependency] private readonly IReplayPlaybackManager _replayPlayback = default!;
private SpectatorPosition? _oldPosition; private SpectatorData? _spectatorData;
public const string SpectateCmd = "replay_spectate"; public const string SpectateCmd = "replay_spectate";
public override void Initialize() public override void Initialize()
@@ -58,15 +60,19 @@ public sealed partial class ReplaySpectatorSystem : EntitySystem
_replayPlayback.ReplayPlaybackStopped -= OnPlaybackStopped; _replayPlayback.ReplayPlaybackStopped -= OnPlaybackStopped;
} }
private void OnPlaybackStarted() private void OnPlaybackStarted(MappingDataNode yamlMappingNode, List<object> objects)
{ {
InitializeMovement(); InitializeMovement();
SetSpectatorPosition(default);
_conHost.RegisterCommand(SpectateCmd, _conHost.RegisterCommand(SpectateCmd,
Loc.GetString("cmd-replay-spectate-desc"), Loc.GetString("cmd-replay-spectate-desc"),
Loc.GetString("cmd-replay-spectate-help"), Loc.GetString("cmd-replay-spectate-help"),
SpectateCommand, SpectateCommand,
SpectateCompletions); SpectateCompletions);
if (_replayPlayback.TryGetRecorderEntity(out var recorder))
SpectateEntity(recorder.Value);
else
SetSpectatorPosition(default);
} }
private void OnPlaybackStopped() private void OnPlaybackStopped()

View File

@@ -66,9 +66,10 @@ public sealed class SubFloorHideSystem : SharedSubFloorHideSystem
private void UpdateAll() private void UpdateAll()
{ {
foreach (var (_, appearance) in EntityManager.EntityQuery<SubFloorHideComponent, AppearanceComponent>(true)) var query = AllEntityQuery<SubFloorHideComponent, AppearanceComponent>();
while (query.MoveNext(out var uid, out _, out var appearance))
{ {
_appearance.MarkDirty(appearance, true); _appearance.QueueUpdate(uid, appearance);
} }
} }
} }

View File

@@ -29,6 +29,7 @@ using Robust.Shared.Configuration;
using Robust.Shared.Input.Binding; using Robust.Shared.Input.Binding;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Replays;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -46,6 +47,8 @@ public sealed class ChatUIController : UIController
[Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IStateManager _state = default!; [Dependency] private readonly IStateManager _state = default!;
[Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IReplayRecordingManager _replayRecording = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[UISystemDependency] private readonly ExamineSystem? _examine = default; [UISystemDependency] private readonly ExamineSystem? _examine = default;
[UISystemDependency] private readonly GhostSystem? _ghost = default; [UISystemDependency] private readonly GhostSystem? _ghost = default;
@@ -758,7 +761,17 @@ public sealed class ChatUIController : UIController
_manager.SendMessage(text, prefixChannel == 0 ? channel : prefixChannel); _manager.SendMessage(text, prefixChannel == 0 ? channel : prefixChannel);
} }
private void OnChatMessage(MsgChatMessage message) => ProcessChatMessage(message.Message); private void OnChatMessage(MsgChatMessage message)
{
var msg = message.Message;
ProcessChatMessage(msg);
if ((msg.Channel & ChatChannel.AdminRelated) == 0 ||
_cfg.GetCVar(CCVars.ReplayRecordAdminChat))
{
_replayRecording.RecordClientMessage(msg);
}
}
public void ProcessChatMessage(ChatMessage msg, bool speechBubble = true) public void ProcessChatMessage(ChatMessage msg, bool speechBubble = true)
{ {

View File

@@ -10,11 +10,10 @@ using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared; using Robust.Shared;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.Serialization.Markdown.Value; using Robust.Shared.Serialization.Markdown.Value;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using TerraFX.Interop.Windows;
using static Robust.Shared.Replays.IReplayRecordingManager; using static Robust.Shared.Replays.IReplayRecordingManager;
using IResourceManager = Robust.Shared.ContentPack.IResourceManager;
namespace Content.Replay.Menu; namespace Content.Replay.Menu;
@@ -94,7 +93,8 @@ public sealed class ReplayMainScreen : State
var forkId = string.Empty; var forkId = string.Empty;
if (data.TryGet<ValueDataNode>(Fork, out var forkNode)) if (data.TryGet<ValueDataNode>(Fork, out var forkNode))
{ {
// TODO REPLAYS somehow distribute and load from build.json? // TODO Replay client build info.
// When distributing the client we need to distribute a build.json or provide these cvars some other way?
var clientFork = _cfg.GetCVar(CVars.BuildForkId); var clientFork = _cfg.GetCVar(CVars.BuildForkId);
if (string.IsNullOrWhiteSpace(clientFork)) if (string.IsNullOrWhiteSpace(clientFork))
forkId = forkNode.Value; forkId = forkNode.Value;
@@ -181,6 +181,7 @@ public sealed class ReplayMainScreen : State
info.HorizontalAlignment = Control.HAlignment.Left; info.HorizontalAlignment = Control.HAlignment.Left;
info.VerticalAlignment = Control.VAlignment.Top; info.VerticalAlignment = Control.VAlignment.Top;
info.SetMarkup(Loc.GetString( info.SetMarkup(Loc.GetString(
"replay-info-info", "replay-info-info",
("file", file), ("file", file),

View File

@@ -65,7 +65,6 @@ public sealed class PlayGlobalSoundCommand : IConsoleCommand
} }
else else
{ {
// TODO REPLAYS uhhh.. what to do with this?
replay = false; replay = false;
filter = Filter.Empty(); filter = Filter.Empty();

View File

@@ -253,7 +253,7 @@ namespace Content.Server.Chat.Managers
if ((channel & ChatChannel.AdminRelated) == 0 || if ((channel & ChatChannel.AdminRelated) == 0 ||
_configurationManager.GetCVar(CCVars.ReplayRecordAdminChat)) _configurationManager.GetCVar(CCVars.ReplayRecordAdminChat))
{ {
_replay.QueueReplayMessage(msg); _replay.RecordServerMessage(msg);
} }
} }
@@ -271,7 +271,7 @@ namespace Content.Server.Chat.Managers
if ((channel & ChatChannel.AdminRelated) == 0 || if ((channel & ChatChannel.AdminRelated) == 0 ||
_configurationManager.GetCVar(CCVars.ReplayRecordAdminChat)) _configurationManager.GetCVar(CCVars.ReplayRecordAdminChat))
{ {
_replay.QueueReplayMessage(msg); _replay.RecordServerMessage(msg);
} }
} }
@@ -301,7 +301,7 @@ namespace Content.Server.Chat.Managers
if ((channel & ChatChannel.AdminRelated) == 0 || if ((channel & ChatChannel.AdminRelated) == 0 ||
_configurationManager.GetCVar(CCVars.ReplayRecordAdminChat)) _configurationManager.GetCVar(CCVars.ReplayRecordAdminChat))
{ {
_replay.QueueReplayMessage(msg); _replay.RecordServerMessage(msg);
} }
} }

View File

@@ -397,7 +397,7 @@ public sealed partial class ChatSystem : SharedChatSystem
_chatManager.ChatMessageToOne(ChatChannel.Whisper, obfuscatedMessage, wrappedobfuscatedMessage, source, false, session.ConnectedClient); _chatManager.ChatMessageToOne(ChatChannel.Whisper, obfuscatedMessage, wrappedobfuscatedMessage, source, false, session.ConnectedClient);
} }
_replay.QueueReplayMessage(new ChatMessage(ChatChannel.Whisper, message, wrappedMessage, source, MessageRangeHideChatForReplay(range))); _replay.RecordServerMessage(new ChatMessage(ChatChannel.Whisper, message, wrappedMessage, source, MessageRangeHideChatForReplay(range)));
var ev = new EntitySpokeEvent(source, message, channel, obfuscatedMessage); var ev = new EntitySpokeEvent(source, message, channel, obfuscatedMessage);
RaiseLocalEvent(source, ev, true); RaiseLocalEvent(source, ev, true);
@@ -548,7 +548,7 @@ public sealed partial class ChatSystem : SharedChatSystem
_chatManager.ChatMessageToOne(channel, message, wrappedMessage, source, entHideChat, session.ConnectedClient); _chatManager.ChatMessageToOne(channel, message, wrappedMessage, source, entHideChat, session.ConnectedClient);
} }
_replay.QueueReplayMessage(new ChatMessage(channel, message, wrappedMessage, source, MessageRangeHideChatForReplay(range))); _replay.RecordServerMessage(new ChatMessage(channel, message, wrappedMessage, source, MessageRangeHideChatForReplay(range)));
} }
/// <summary> /// <summary>

View File

@@ -25,7 +25,7 @@ public sealed class TypingIndicatorSystem : SharedTypingIndicatorSystem
// when player poses entity we want to make sure that there is typing indicator // when player poses entity we want to make sure that there is typing indicator
EnsureComp<TypingIndicatorComponent>(ev.Entity); EnsureComp<TypingIndicatorComponent>(ev.Entity);
// we also need appearance component to sync visual state // we also need appearance component to sync visual state
EnsureComp<ServerAppearanceComponent>(ev.Entity); EnsureComp<AppearanceComponent>(ev.Entity);
} }
private void OnPlayerDetached(EntityUid uid, TypingIndicatorComponent component, PlayerDetachedEvent args) private void OnPlayerDetached(EntityUid uid, TypingIndicatorComponent component, PlayerDetachedEvent args)

View File

@@ -50,7 +50,7 @@ public sealed partial class ExplosionSystem : EntitySystem
// restricted to something like the same map, but whatever. // restricted to something like the same map, but whatever.
_pvsSys.AddGlobalOverride(explosionEntity); _pvsSys.AddGlobalOverride(explosionEntity);
var appearance = AddComp<ServerAppearanceComponent>(explosionEntity); var appearance = AddComp<AppearanceComponent>(explosionEntity);
_appearance.SetData(explosionEntity, ExplosionAppearanceData.Progress, 1, appearance); _appearance.SetData(explosionEntity, ExplosionAppearanceData.Progress, 1, appearance);
return explosionEntity; return explosionEntity;

View File

@@ -4,7 +4,6 @@ using Content.Server.Ghost;
using Content.Server.Maps; using Content.Server.Maps;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Players; using Content.Server.Players;
using Content.Shared.CCVar;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Preferences; using Content.Shared.Preferences;
using JetBrains.Annotations; using JetBrains.Annotations;
@@ -18,7 +17,6 @@ using Robust.Shared.Player;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Content.Shared.Database; using Content.Shared.Database;
using Robust.Shared.Asynchronous; using Robust.Shared.Asynchronous;
@@ -66,9 +64,6 @@ namespace Content.Server.GameTicking
} }
} }
[ViewVariables]
public int RoundId { get; private set; }
/// <summary> /// <summary>
/// Returns true if the round's map is eligible to be updated. /// Returns true if the round's map is eligible to be updated.
/// </summary> /// </summary>

View File

@@ -1,6 +1,5 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Administration.Managers; using Content.Server.Administration.Managers;
using Content.Server.Chat;
using Content.Server.Chat.Managers; using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems; using Content.Server.Chat.Systems;
using Content.Server.Database; using Content.Server.Database;
@@ -14,23 +13,17 @@ using Content.Server.Station.Systems;
using Content.Shared.Chat; using Content.Shared.Chat;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Mobs.Systems;
using Content.Shared.Roles; using Content.Shared.Roles;
using Robust.Server; using Robust.Server;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Console; using Robust.Shared.Console;
#if EXCEPTION_TOLERANCE #if EXCEPTION_TOLERANCE
using Robust.Shared.Exceptions; using Robust.Shared.Exceptions;
#endif #endif
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Replays;
using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Serialization.Markdown.Value;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -68,7 +61,6 @@ namespace Content.Server.GameTicking
DebugTools.Assert(_prototypeManager.Index<JobPrototype>(FallbackOverflowJob).Name == FallbackOverflowJobName, DebugTools.Assert(_prototypeManager.Index<JobPrototype>(FallbackOverflowJob).Name == FallbackOverflowJobName,
"Overflow role does not have the correct name!"); "Overflow role does not have the correct name!");
InitializeGameRules(); InitializeGameRules();
_replay.OnRecordingStarted += OnRecordingStart;
_initialized = true; _initialized = true;
} }
@@ -88,12 +80,6 @@ namespace Content.Server.GameTicking
base.Shutdown(); base.Shutdown();
ShutdownGameRules(); ShutdownGameRules();
_replay.OnRecordingStarted -= OnRecordingStart;
}
private void OnRecordingStart((MappingDataNode, List<object>) data)
{
data.Item1["roundId"] = new ValueDataNode(RoundId.ToString());
} }
private void SendServerMessage(string message) private void SendServerMessage(string message)
@@ -123,7 +109,6 @@ namespace Content.Server.GameTicking
#if EXCEPTION_TOLERANCE #if EXCEPTION_TOLERANCE
[Dependency] private readonly IRuntimeLog _runtimeLog = default!; [Dependency] private readonly IRuntimeLog _runtimeLog = default!;
#endif #endif
[Dependency] private readonly StationSystem _stationSystem = default!;
[Dependency] private readonly StationSpawningSystem _stationSpawning = default!; [Dependency] private readonly StationSpawningSystem _stationSpawning = default!;
[Dependency] private readonly StationJobsSystem _stationJobs = default!; [Dependency] private readonly StationJobsSystem _stationJobs = default!;
[Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly DamageableSystem _damageable = default!;
@@ -133,6 +118,5 @@ namespace Content.Server.GameTicking
[Dependency] private readonly ServerUpdateManager _serverUpdates = default!; [Dependency] private readonly ServerUpdateManager _serverUpdates = default!;
[Dependency] private readonly PlayTimeTrackingSystem _playTimeTrackings = default!; [Dependency] private readonly PlayTimeTrackingSystem _playTimeTrackings = default!;
[Dependency] private readonly UserDbDataManager _userDb = default!; [Dependency] private readonly UserDbDataManager _userDb = default!;
[Dependency] private readonly IReplayRecordingManager _replay = default!;
} }
} }

View File

@@ -80,7 +80,7 @@ namespace Content.Server.Pointing.EntitySystems
RaiseNetworkEvent(new PopupEntityEvent(message, PopupType.Small, source), viewerEntity); RaiseNetworkEvent(new PopupEntityEvent(message, PopupType.Small, source), viewerEntity);
} }
_replay.QueueReplayMessage(new PopupEntityEvent(viewerMessage, PopupType.Small, source)); _replay.RecordServerMessage(new PopupEntityEvent(viewerMessage, PopupType.Small, source));
} }
public bool InRange(EntityUid pointer, EntityCoordinates coordinates) public bool InRange(EntityUid pointer, EntityCoordinates coordinates)

View File

@@ -122,7 +122,7 @@ public sealed class RadioSystem : EntitySystem
else else
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Radio message from {ToPrettyString(messageSource):user} on {channel.LocalizedName}: {message}"); _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Radio message from {ToPrettyString(messageSource):user} on {channel.LocalizedName}: {message}");
_replay.QueueReplayMessage(chat); _replay.RecordServerMessage(chat);
_messages.Remove(message); _messages.Remove(message);
} }

View File

@@ -1596,6 +1596,6 @@ namespace Content.Shared.CCVar
/// false. /// false.
/// </summary> /// </summary>
public static readonly CVarDef<bool> ReplayRecordAdminChat = public static readonly CVarDef<bool> ReplayRecordAdminChat =
CVarDef.Create("replay.record_admin_chat", false, CVar.SERVERONLY); CVarDef.Create("replay.record_admin_chat", false, CVar.ARCHIVE);
} }
} }

View File

@@ -1,16 +1,41 @@
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Replays;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Utility; using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Serialization.Markdown.Value;
namespace Content.Shared.GameTicking namespace Content.Shared.GameTicking
{ {
public abstract class SharedGameTicker : EntitySystem public abstract class SharedGameTicker : EntitySystem
{ {
[Dependency] private readonly IReplayRecordingManager _replay = default!;
// See ideally these would be pulled from the job definition or something. // See ideally these would be pulled from the job definition or something.
// But this is easier, and at least it isn't hardcoded. // But this is easier, and at least it isn't hardcoded.
//TODO: Move these, they really belong in StationJobsSystem or a cvar. //TODO: Move these, they really belong in StationJobsSystem or a cvar.
public const string FallbackOverflowJob = "Passenger"; public const string FallbackOverflowJob = "Passenger";
public const string FallbackOverflowJobName = "job-name-passenger"; public const string FallbackOverflowJobName = "job-name-passenger";
// TODO network.
// Probably most useful for replays, round end info, and probably things like lobby menus.
[ViewVariables]
public int RoundId { get; protected set; }
public override void Initialize()
{
base.Initialize();
_replay.RecordingStarted += OnRecordingStart;
}
public override void Shutdown()
{
_replay.RecordingStarted -= OnRecordingStart;
}
private void OnRecordingStart(MappingDataNode metadata, List<object> events)
{
metadata["roundId"] = new ValueDataNode(RoundId.ToString());
}
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]

View File

@@ -228,12 +228,6 @@
layer: layer:
- MobLayer - MobLayer
- type: Appearance - type: Appearance
rotate: true
states:
Alive:
Base: onestar_boss
Dead:
Base: onestar_boss_wrecked
- type: CombatMode - type: CombatMode
- type: Tag - type: Tag
tags: tags:

View File

@@ -31,6 +31,16 @@
- cvar - cvar
- fuckrules - fuckrules
- midipanic - midipanic
- replay_recording_start
- replay_recording_stop
- replay_recording_stats
- replay_play
- replay_pause
- replay_toggle
- replay_skip
- replay_set_time
- replay_stop
- replay_load
- Flags: DEBUG - Flags: DEBUG
Commands: Commands:

View File

@@ -64,6 +64,7 @@
- tilelookup - tilelookup
- net_entityreport - net_entityreport
- scene - scene
- replay_recording_stats
- Flags: MAPPING - Flags: MAPPING
@@ -120,6 +121,8 @@
- gcf - gcf
- getcomponentregistration - getcomponentregistration
- fuck - fuck
- replay_recording_start
- replay_recording_stop
- Flags: QUERY - Flags: QUERY
Commands: Commands: