#nullable enable using System.Collections.Generic; using Content.Server.GameObjects.EntitySystems.DoAfter; using Content.Shared.GameObjects.Components; using Robust.Server.GameObjects; using Robust.Server.Interfaces.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Network; namespace Content.Server.GameObjects.Components { [RegisterComponent] public sealed class DoAfterComponent : SharedDoAfterComponent { public IReadOnlyCollection DoAfters => _doAfters.Keys; private readonly Dictionary _doAfters = new Dictionary(); // So the client knows which one to update (and so we don't send all of the do_afters every time 1 updates) // we'll just send them the index. Doesn't matter if it wraps around. private byte _runningIndex; public override void HandleMessage(ComponentMessage message, IComponent? component) { base.HandleMessage(message, component); switch (message) { case PlayerAttachedMsg _: UpdateClient(); break; } } // Only sending data to the relevant client (at least, other clients don't need to know about do_after for now). private void UpdateClient() { if (!TryGetConnectedClient(out var connectedClient)) { return; } foreach (var (doAfter, id) in _doAfters) { // THE ALMIGHTY PYRAMID var message = new DoAfterMessage( id, doAfter.UserGrid, doAfter.TargetGrid, doAfter.StartTime, doAfter.EventArgs.Delay, doAfter.EventArgs.BreakOnUserMove, doAfter.EventArgs.BreakOnTargetMove, doAfter.EventArgs.Target?.Uid ?? EntityUid.Invalid); SendNetworkMessage(message, connectedClient); } } private bool TryGetConnectedClient(out INetChannel? connectedClient) { connectedClient = null; if (!Owner.TryGetComponent(out IActorComponent? actorComponent)) { return false; } connectedClient = actorComponent.playerSession.ConnectedClient; if (!connectedClient.IsConnected) { return false; } return true; } public void Add(DoAfter doAfter) { _doAfters.Add(doAfter, _runningIndex); if (TryGetConnectedClient(out var connectedClient)) { var message = new DoAfterMessage( _runningIndex, doAfter.UserGrid, doAfter.TargetGrid, doAfter.StartTime, doAfter.EventArgs.Delay, doAfter.EventArgs.BreakOnUserMove, doAfter.EventArgs.BreakOnTargetMove, doAfter.EventArgs.Target?.Uid ?? EntityUid.Invalid); SendNetworkMessage(message, connectedClient); } _runningIndex++; } public void Cancelled(DoAfter doAfter) { if (!_doAfters.TryGetValue(doAfter, out var index)) { return; } if (TryGetConnectedClient(out var connectedClient)) { var message = new CancelledDoAfterMessage(index); SendNetworkMessage(message, connectedClient); } _doAfters.Remove(doAfter); } /// /// Call when the particular DoAfter is finished. /// Client should be tracking this independently. /// /// public void Finished(DoAfter doAfter) { if (!_doAfters.ContainsKey(doAfter)) { return; } _doAfters.Remove(doAfter); } } }