diff --git a/Content.Client/Explosion/ExplosionOverlay.cs b/Content.Client/Explosion/ExplosionOverlay.cs index 7344164ff7..630efa9be2 100644 --- a/Content.Client/Explosion/ExplosionOverlay.cs +++ b/Content.Client/Explosion/ExplosionOverlay.cs @@ -1,3 +1,4 @@ +using Content.Shared.Explosion; using JetBrains.Annotations; using Robust.Client.Graphics; using Robust.Shared.Enums; @@ -10,26 +11,10 @@ namespace Content.Client.Explosion; [UsedImplicitly] public sealed class ExplosionOverlay : Overlay { - /// - /// The explosion that needs to be drawn. This explosion is currently being processed by the server and - /// expanding outwards. - /// - internal Explosion? ActiveExplosion; - - /// - /// This index specifies what parts of the currently expanding explosion should be drawn. - /// - public int Index; - - /// - /// These explosions have finished expanding, but we will draw for a few more frames. This is important for - /// small explosions, as otherwise they disappear far too quickly. - /// - internal List CompletedExplosions = new (); - [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IEntityManager _entMan = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV; @@ -38,7 +23,7 @@ public sealed class ExplosionOverlay : Overlay public ExplosionOverlay() { IoCManager.InjectDependencies(this); - _shader = IoCManager.Resolve().Index("unshaded").Instance(); + _shader = _proto.Index("unshaded").Instance(); } protected override void Draw(in OverlayDrawArgs args) @@ -48,22 +33,20 @@ public sealed class ExplosionOverlay : Overlay var xforms = _entMan.GetEntityQuery(); - if (ActiveExplosion != null && ActiveExplosion.Map == args.Viewport.Eye?.Position.MapId) + foreach (var (comp, appearance) in _entMan.EntityQuery(true)) { - DrawExplosion(drawHandle, args.WorldBounds, ActiveExplosion, Index, xforms); - } + if (!appearance.TryGetData(ExplosionAppearanceData.Progress, out int index)) + continue; - foreach (var exp in CompletedExplosions) - { - if (exp.Map == args.Viewport.Eye?.Position.MapId) - DrawExplosion(drawHandle, args.WorldBounds, exp, exp.Intensity.Count, xforms); + index = Math.Min(index, comp.Intensity.Count - 1); + DrawExplosion(drawHandle, args.WorldBounds, comp, index, xforms); } drawHandle.SetTransform(Matrix3.Identity); drawHandle.UseShader(null); } - private void DrawExplosion(DrawingHandleWorld drawHandle, Box2Rotated worldBounds, Explosion exp, int index, EntityQuery xforms) + private void DrawExplosion(DrawingHandleWorld drawHandle, Box2Rotated worldBounds, ExplosionVisualsComponent exp, int index, EntityQuery xforms) { Box2 gridBounds; foreach (var (gridId, tiles) in exp.Tiles) @@ -94,10 +77,10 @@ public sealed class ExplosionOverlay : Overlay Box2 gridBounds, int index, Dictionary> tileSets, - Explosion exp, + ExplosionVisualsComponent exp, ushort tileSize) { - for (var j = 0; j < index; j++) + for (var j = 0; j <= index; j++) { if (!tileSets.TryGetValue(j, out var tiles)) continue; diff --git a/Content.Client/Explosion/ExplosionOverlaySystem.cs b/Content.Client/Explosion/ExplosionOverlaySystem.cs index 81004167d3..6c7fafc463 100644 --- a/Content.Client/Explosion/ExplosionOverlaySystem.cs +++ b/Content.Client/Explosion/ExplosionOverlaySystem.cs @@ -1,11 +1,8 @@ -using Content.Shared.CCVar; using Content.Shared.Explosion; -using Content.Shared.GameTicking; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.ResourceManagement; -using Robust.Shared.Configuration; -using Robust.Shared.Map; +using Robust.Shared.GameStates; using Robust.Shared.Prototypes; namespace Content.Client.Explosion; @@ -16,12 +13,9 @@ namespace Content.Client.Explosion; /// public sealed class ExplosionOverlaySystem : EntitySystem { - private ExplosionOverlay _overlay = default!; - [Dependency] private readonly IPrototypeManager _protoMan = default!; - [Dependency] private readonly IMapManager _mapMan = default!; [Dependency] private readonly IResourceCache _resCache = default!; - [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly IOverlayManager _overlayMan = default!; /// /// For how many seconds should an explosion stay on-screen once it has finished expanding? @@ -32,209 +26,57 @@ public sealed class ExplosionOverlaySystem : EntitySystem { base.Initialize(); - SubscribeNetworkEvent(OnExplosion); - SubscribeNetworkEvent(HandleExplosionUpdate); - SubscribeLocalEvent(OnMapChanged); - SubscribeAllEvent(OnReset); - - _cfg.OnValueChanged(CCVars.ExplosionPersistence, SetExplosionPersistence, true); - - var overlayManager = IoCManager.Resolve(); - _overlay = new ExplosionOverlay(); - if (!overlayManager.HasOverlay()) - overlayManager.AddOverlay(_overlay); + SubscribeLocalEvent(OnExplosionInit); + SubscribeLocalEvent(OnCompRemove); + SubscribeLocalEvent(OnExplosionHandleState); + _overlayMan.AddOverlay(new ExplosionOverlay()); } - private void OnReset(RoundRestartCleanupEvent ev) + private void OnExplosionHandleState(EntityUid uid, ExplosionVisualsComponent component, ref ComponentHandleState args) { - // Not sure if round restart cleans up client-side entities, but better safe than sorry. - foreach (var exp in _overlay.CompletedExplosions) - { - QueueDel(exp.LightEntity); - } - if (_overlay.ActiveExplosion != null) - QueueDel(_overlay.ActiveExplosion.LightEntity); - - _overlay.CompletedExplosions.Clear(); - _overlay.ActiveExplosion = null; - _overlay.Index = 0; + if (args.Current is not ExplosionVisualsState state) + return; + + component.Epicenter = state.Epicenter; + component.SpaceTiles = state.SpaceTiles; + component.Tiles = state.Tiles; + component.Intensity = state.Intensity; + component.ExplosionType = state.ExplosionType; + component.SpaceMatrix = state.SpaceMatrix; + component.SpaceTileSize = state.SpaceTileSize; } - private void OnMapChanged(MapChangedEvent ev) + private void OnCompRemove(EntityUid uid, ExplosionVisualsComponent component, ComponentRemove args) { - if (ev.Created) - return; - - if (_overlay.ActiveExplosion?.Map == ev.Map) - _overlay.ActiveExplosion = null; - - _overlay.CompletedExplosions.RemoveAll(exp => exp.Map == ev.Map); + QueueDel(component.LightEntity); } - private void SetExplosionPersistence(float value) => ExplosionPersistence = value; - - public override void FrameUpdate(float frameTime) + private void OnExplosionInit(EntityUid uid, ExplosionVisualsComponent component, ComponentInit args) { - base.FrameUpdate(frameTime); - - // increment the lifetime of completed explosions, and remove them if they have been ons screen for more - // than ExplosionPersistence seconds - for (int i = _overlay.CompletedExplosions.Count - 1; i>= 0; i--) - { - var explosion = _overlay.CompletedExplosions[i]; - - if (_mapMan.IsMapPaused(explosion.Map)) - continue; - - explosion.Lifetime += frameTime; - - if (explosion.Lifetime >= ExplosionPersistence) - { - EntityManager.QueueDeleteEntity(explosion.LightEntity); - - // Remove-swap - _overlay.CompletedExplosions[i] = _overlay.CompletedExplosions[^1]; - _overlay.CompletedExplosions.RemoveAt(_overlay.CompletedExplosions.Count - 1); - } - } - } - - /// - /// The server has processed some explosion. This updates the client-side overlay so that the area covered - /// by the fire-visual matches up with the area that the explosion has affected. - /// - private void HandleExplosionUpdate(ExplosionOverlayUpdateEvent args) - { - if (args.ExplosionId != _overlay.ActiveExplosion?.Explosionid && !IsNewer(args.ExplosionId)) - { - // out of order events. Ignore. - return; - } - - _overlay.Index = args.Index; - - if (_overlay.ActiveExplosion == null) - { - // no active explosion... events out of order? - return; - } - - if (args.Index != int.MaxValue) + if (!_protoMan.TryIndex(component.ExplosionType, out ExplosionPrototype? type)) return; - // the explosion has finished expanding - _overlay.Index = 0; - _overlay.CompletedExplosions.Add(_overlay.ActiveExplosion); - _overlay.ActiveExplosion = null; - } - - /// - /// A new explosion occurred. This prepares the client-side light entity and stores the - /// explosion/fire-effect overlay data. - /// - private void OnExplosion(ExplosionEvent args) - { - if (!_protoMan.TryIndex(args.TypeID, out ExplosionPrototype? type)) - return; - - // spawn in a light source at the epicenter - var lightEntity = Spawn("ExplosionLight", args.Epicenter); + // spawn in a client-side light source at the epicenter + var lightEntity = Spawn("ExplosionLight", component.Epicenter); var light = EnsureComp(lightEntity); - light.Energy = light.Radius = args.Intensity.Count; + light.Energy = light.Radius = component.Intensity.Count; light.Color = type.LightColor; + component.LightEntity = lightEntity; + component.FireColor = type.FireColor; + component.IntensityPerState = type.IntensityPerState; - if (_overlay.ActiveExplosion == null) + var fireRsi = _resCache.GetResource(type.TexturePath).RSI; + foreach (var state in fireRsi) { - _overlay.ActiveExplosion = new(args, type, lightEntity, _resCache); - return; + component.FireFrames.Add(state.GetFrames(RSI.State.Direction.South)); + if (component.FireFrames.Count == type.FireStates) + break; } - - // we have a currently active explosion. Can happen when events are received out of order. either multiple - // explosions are happening in one tick, or a new explosion was received before the event telling us the old one - // finished got through. - - if (IsNewer(args.ExplosionId)) - { - // This is a newer explosion. Add the old-currently-active explosions to the completed list - _overlay.CompletedExplosions.Add(_overlay.ActiveExplosion); - _overlay.ActiveExplosion = new(args, type, lightEntity, _resCache); - } - else - { - // explosions were out of order. keep the active one, and directly add the received one to the completed - // list. - _overlay.CompletedExplosions.Add(new(args, type, lightEntity, _resCache)); - return; - } - } - - public bool IsNewer(int explosionId) - { - if (_overlay.ActiveExplosion == null) - return true; - - // If we ever get servers stable enough to live this long, the explosion Id int might overflow. - return _overlay.ActiveExplosion.Explosionid < explosionId - || _overlay.ActiveExplosion.Explosionid > int.MaxValue/2 && explosionId < int.MinValue/2; } public override void Shutdown() { base.Shutdown(); - - _cfg.UnsubValueChanged(CCVars.ExplosionPersistence, SetExplosionPersistence); - - var overlayManager = IoCManager.Resolve(); - if (overlayManager.HasOverlay()) - overlayManager.RemoveOverlay(); - } -} - -internal sealed class Explosion -{ - public readonly Dictionary>? SpaceTiles; - public readonly Dictionary>> Tiles; - public readonly List Intensity; - public readonly EntityUid LightEntity; - public readonly MapId Map; - public readonly int Explosionid; - public readonly ushort SpaceTileSize; - public readonly float IntensityPerState; - - public readonly Matrix3 SpaceMatrix; - - /// - /// How long have we been drawing this explosion, starting from the time the explosion was fully drawn. - /// - public float Lifetime; - - /// - /// The textures used for the explosion fire effect. Each fire-state is associated with an explosion - /// intensity range, and each stat itself has several textures. - /// - public readonly List FireFrames = new(); - - public readonly Color? FireColor; - - internal Explosion(ExplosionEvent args, ExplosionPrototype type, EntityUid lightEntity, IResourceCache resCache) - { - Map = args.Epicenter.MapId; - SpaceTiles = args.SpaceTiles; - Tiles = args.Tiles; - Intensity = args.Intensity; - SpaceMatrix = args.SpaceMatrix; - Explosionid = args.ExplosionId; - FireColor = type.FireColor; - LightEntity = lightEntity; - SpaceTileSize = args.SpaceTileSize; - IntensityPerState = type.IntensityPerState; - - var fireRsi = resCache.GetResource(type.TexturePath).RSI; - foreach (var state in fireRsi) - { - FireFrames.Add(state.GetFrames(RSI.State.Direction.South)); - if (FireFrames.Count == type.FireStates) - break; - } + _overlayMan.RemoveOverlay(); } } diff --git a/Content.Client/Explosion/ExplosionVisualsComponent.cs b/Content.Client/Explosion/ExplosionVisualsComponent.cs new file mode 100644 index 0000000000..953934b975 --- /dev/null +++ b/Content.Client/Explosion/ExplosionVisualsComponent.cs @@ -0,0 +1,25 @@ +using Content.Shared.Explosion; +using Robust.Client.Graphics; + +namespace Content.Client.Explosion; + +[RegisterComponent] +[ComponentReference(typeof(SharedExplosionVisualsComponent))] +public sealed class ExplosionVisualsComponent : SharedExplosionVisualsComponent +{ + public EntityUid LightEntity; + /// + /// How long have we been drawing this explosion, starting from the time the explosion was fully drawn. + /// + public float Lifetime; + + public float IntensityPerState; + + /// + /// The textures used for the explosion fire effect. Each fire-state is associated with an explosion + /// intensity range, and each stat itself has several textures. + /// + public List FireFrames = new(); + + public Color? FireColor; +} diff --git a/Content.Server/Explosion/Components/ExplosionVisualsComponent.cs b/Content.Server/Explosion/Components/ExplosionVisualsComponent.cs new file mode 100644 index 0000000000..eba85ca8f6 --- /dev/null +++ b/Content.Server/Explosion/Components/ExplosionVisualsComponent.cs @@ -0,0 +1,9 @@ +using Content.Shared.Explosion; + +namespace Content.Server.Explosion; + +[RegisterComponent] +[ComponentReference(typeof(SharedExplosionVisualsComponent))] +public sealed class ExplosionVisualsComponent : SharedExplosionVisualsComponent +{ +} diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs index 9fdf3cab55..0497790775 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs @@ -1,8 +1,10 @@ using System.Linq; +using Content.Shared.CCVar; using Content.Shared.Damage; using Content.Shared.Explosion; using Content.Shared.Maps; using Content.Shared.Physics; +using Content.Shared.Spawners.Components; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics; @@ -15,16 +17,6 @@ namespace Content.Server.Explosion.EntitySystems; public sealed partial class ExplosionSystem : EntitySystem { - /// - /// Used to identify explosions when communicating with the client. Might be needed if more than one explosion is spawned in a single tick. - /// - /// - /// Overflowing back to 0 should cause no issue, as long as you don't have more than 256 explosions happening in a single tick. - /// - private int _explosionCounter = 0; - // maybe should just use a UID/explosion-entity and a state to convey information? - // but then need to ignore PVS? Eeehh this works well enough for now. - /// /// Used to limit explosion processing time. See . /// @@ -63,6 +55,7 @@ public sealed partial class ExplosionSystem : EntitySystem if (_activeExplosion?.Epicenter.MapId != ev.Map) return; + QueueDel(_activeExplosion.VisualEnt); _activeExplosion = null; _nodeGroupSystem.PauseUpdating = false; _pathfindingSystem.PauseUpdating = false; @@ -103,7 +96,6 @@ public sealed partial class ExplosionSystem : EntitySystem if (_activeExplosion == null) continue; - _explosionCounter++; _previousTileIteration = 0; // just a lil nap @@ -131,14 +123,20 @@ public sealed partial class ExplosionSystem : EntitySystem // has the explosion finished processing? if (_activeExplosion.FinishedProcessing) - _activeExplosion = null; + { + var comp = EnsureComp(_activeExplosion.VisualEnt); + comp.Lifetime = _cfg.GetCVar(CCVars.ExplosionPersistence); + _appearance.SetData(_activeExplosion.VisualEnt, ExplosionAppearanceData.Progress, int.MaxValue); + _activeExplosion = null; + } #if EXCEPTION_TOLERANCE } catch (Exception e) { // Ensure the system does not get stuck in an error-loop. + if (_activeExplosion != null) + QueueDel(_activeExplosion.VisualEnt); _activeExplosion = null; - RaiseNetworkEvent(new ExplosionOverlayUpdateEvent(_explosionCounter, int.MaxValue)); _nodeGroupSystem.PauseUpdating = false; _pathfindingSystem.PauseUpdating = false; throw; @@ -151,21 +149,13 @@ public sealed partial class ExplosionSystem : EntitySystem // we have finished processing our tiles. Is there still an ongoing explosion? if (_activeExplosion != null) { - // update the client explosion overlays. This ensures that the fire-effects sync up with the entities currently being damaged. - if (_previousTileIteration == _activeExplosion.CurrentIteration) - return; - - _previousTileIteration = _activeExplosion.CurrentIteration; - RaiseNetworkEvent(new ExplosionOverlayUpdateEvent(_explosionCounter, _previousTileIteration + 1)); + _appearance.SetData(_activeExplosion.VisualEnt, ExplosionAppearanceData.Progress, _activeExplosion.CurrentIteration + 1); return; } if (_explosionQueue.Count > 0) return; - // We have finished processing all explosions. Clear client explosion overlays - RaiseNetworkEvent(new ExplosionOverlayUpdateEvent(_explosionCounter, int.MaxValue)); - //wakey wakey _nodeGroupSystem.PauseUpdating = false; _pathfindingSystem.PauseUpdating = false; @@ -586,6 +576,8 @@ sealed class Explosion private readonly IEntityManager _entMan; private readonly ExplosionSystem _system; + public readonly EntityUid VisualEnt; + /// /// Initialize a new instance for processing /// @@ -601,8 +593,10 @@ sealed class Explosion int maxTileBreak, bool canCreateVacuum, IEntityManager entMan, - IMapManager mapMan) + IMapManager mapMan, + EntityUid visualEnt) { + VisualEnt = visualEnt; _system = system; ExplosionType = explosionType; _tileSetIntensity = tileSetIntensity; diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs index c4586d7004..881bfe2a4e 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs @@ -313,7 +313,7 @@ public sealed partial class ExplosionSystem : EntitySystem return (grids, referenceGrid, radius); } - public ExplosionEvent? GenerateExplosionPreview(SpawnExplosionEuiMsg.PreviewRequest request) + public ExplosionVisualsState? GenerateExplosionPreview(SpawnExplosionEuiMsg.PreviewRequest request) { var stopwatch = new Stopwatch(); stopwatch.Start(); @@ -332,7 +332,19 @@ public sealed partial class ExplosionSystem : EntitySystem Logger.Info($"Generated explosion preview with {area} tiles in {stopwatch.Elapsed.TotalMilliseconds}ms"); - // the explosion event that **would** be sent to all clients, if it were a real explosion. - return GetExplosionEvent(request.Epicenter, request.TypeId, spaceMatrix, spaceData, gridData.Values, iterationIntensity); + Dictionary>> tileLists = new(); + foreach (var (grid, data) in gridData) + { + tileLists.Add(grid, data.TileLists); + } + + return new ExplosionVisualsState( + request.Epicenter, + request.TypeId, + iterationIntensity, + spaceData?.TileLists, + tileLists, spaceMatrix, + spaceData?.TileSize ?? DefaultTileSize + ); } } diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Visuals.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Visuals.cs new file mode 100644 index 0000000000..9892b36896 --- /dev/null +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Visuals.cs @@ -0,0 +1,58 @@ +using Content.Shared.Explosion; +using Robust.Server.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.Map; + +namespace Content.Server.Explosion.EntitySystems; + +// This part of the system handled send visual / overlay data to clients. +public sealed partial class ExplosionSystem : EntitySystem +{ + public void InitVisuals() + { + SubscribeLocalEvent(OnGetState); + } + + private void OnGetState(EntityUid uid, ExplosionVisualsComponent component, ref ComponentGetState args) + { + args.State = new ExplosionVisualsState( + component.Epicenter, + component.ExplosionType, + component.Intensity, + component.SpaceTiles, + component.Tiles, + component.SpaceMatrix, + component.SpaceTileSize); + } + + /// + /// Constructor for the shared using the server-exclusive explosion classes. + /// + private EntityUid CreateExplosionVisualEntity(MapCoordinates epicenter, string prototype, Matrix3 spaceMatrix, ExplosionSpaceTileFlood? spaceData, IEnumerable gridData, List iterationIntensity) + { + var explosionEntity = Spawn(null, MapCoordinates.Nullspace); + var comp = AddComp(explosionEntity); + + foreach (var grid in gridData) + { + comp.Tiles.Add(grid.Grid.Owner, grid.TileLists); + } + + comp.SpaceTiles = spaceData?.TileLists; + comp.Epicenter = epicenter; + comp.ExplosionType = prototype; + comp.Intensity = iterationIntensity; + comp.SpaceMatrix = spaceMatrix; + comp.SpaceTileSize = spaceData?.TileSize ?? DefaultTileSize; + Dirty(comp); + + // Light, sound & visuals may extend well beyond normal PVS range. In principle, this should probably still be + // restricted to something like the same map, but whatever. + _pvsSys.AddGlobalOverride(explosionEntity); + + var appearance = AddComp(explosionEntity); + _appearance.SetData(explosionEntity, ExplosionAppearanceData.Progress, 1, appearance); + + return explosionEntity; + } +} diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs index d1cd0e5723..5b9eef5686 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs @@ -11,6 +11,7 @@ using Content.Shared.Explosion; using Content.Shared.GameTicking; using Content.Shared.Inventory; using Content.Shared.Throwing; +using Robust.Server.GameStates; using Robust.Server.Player; using Robust.Shared.Audio; using Robust.Shared.Configuration; @@ -31,12 +32,14 @@ public sealed partial class ExplosionSystem : EntitySystem [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly NodeGroupSystem _nodeGroupSystem = default!; [Dependency] private readonly PathfindingSystem _pathfindingSystem = default!; [Dependency] private readonly SharedCameraRecoilSystem _recoilSystem = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly ThrowingSystem _throwingSystem = default!; + [Dependency] private readonly PVSOverrideSystem _pvsSys = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; /// @@ -81,11 +84,14 @@ public sealed partial class ExplosionSystem : EntitySystem SubscribeLocalEvent(OnAirtightDamaged); SubscribeCvars(); InitAirtightMap(); + InitVisuals(); } private void OnReset(RoundRestartCleanupEvent ev) { _explosionQueue.Clear(); + if (_activeExplosion !=null) + QueueDel(_activeExplosion.VisualEnt); _activeExplosion = null; _nodeGroupSystem.PauseUpdating = false; _pathfindingSystem.PauseUpdating = false; @@ -281,7 +287,7 @@ public sealed partial class ExplosionSystem : EntitySystem var (area, iterationIntensity, spaceData, gridData, spaceMatrix) = results.Value; - RaiseNetworkEvent(GetExplosionEvent(epicenter, type.ID, spaceMatrix, spaceData, gridData.Values, iterationIntensity)); + var visualEnt = CreateExplosionVisualEntity(epicenter, type.ID, spaceMatrix, spaceData, gridData.Values, iterationIntensity); // camera shake CameraShake(iterationIntensity.Count * 2.5f, epicenter, totalIntensity); @@ -306,23 +312,8 @@ public sealed partial class ExplosionSystem : EntitySystem maxTileBreak, canCreateVacuum, EntityManager, - _mapManager); - } - - /// - /// Constructor for the shared using the server-exclusive explosion classes. - /// - internal ExplosionEvent GetExplosionEvent(MapCoordinates epicenter, string id, Matrix3 spaceMatrix, ExplosionSpaceTileFlood? spaceData, IEnumerable gridData, List iterationIntensity) - { - var spaceTiles = spaceData?.TileLists; - - Dictionary>> tileLists = new(); - foreach (var grid in gridData) - { - tileLists.Add(grid.Grid.Owner, grid.TileLists); - } - - return new ExplosionEvent(_explosionCounter, epicenter, id, iterationIntensity, spaceTiles, tileLists, spaceMatrix, spaceData?.TileSize ?? DefaultTileSize); + _mapManager, + visualEnt); } private void CameraShake(float range, MapCoordinates epicenter, float totalIntensity) diff --git a/Content.Shared/Administration/SpawnExplosionEuiMsg.cs b/Content.Shared/Administration/SpawnExplosionEuiMsg.cs index f9011bda30..38fd820996 100644 --- a/Content.Shared/Administration/SpawnExplosionEuiMsg.cs +++ b/Content.Shared/Administration/SpawnExplosionEuiMsg.cs @@ -40,9 +40,9 @@ public static class SpawnExplosionEuiMsg { public readonly float Slope; public readonly float TotalIntensity; - public readonly ExplosionEvent Explosion; + public readonly ExplosionVisualsState Explosion; - public PreviewData(ExplosionEvent explosion, float slope, float totalIntensity) + public PreviewData(ExplosionVisualsState explosion, float slope, float totalIntensity) { Slope = slope; TotalIntensity = totalIntensity; diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 0b897425b5..e74e1559b1 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -630,11 +630,10 @@ namespace Content.Shared.CCVar CVarDef.Create("explosion.incremental_tile", false, CVar.SERVERONLY); /// - /// Client-side explosion visuals: for how many seconds should an explosion stay on-screen once it has - /// finished expanding? + /// This determines for how many seconds an explosion should stay visible once it has finished expanding. /// public static readonly CVarDef ExplosionPersistence = - CVarDef.Create("explosion.persistence", 0.3f, CVar.REPLICATED); + CVarDef.Create("explosion.persistence", 0.3f, CVar.SERVERONLY); /// /// If an explosion covers a larger area than this number, the damaging/processing will always start during diff --git a/Content.Shared/Explosion/ExplosionEvents.cs b/Content.Shared/Explosion/ExplosionEvents.cs index dc7ed58dcb..5cd9d2e201 100644 --- a/Content.Shared/Explosion/ExplosionEvents.cs +++ b/Content.Shared/Explosion/ExplosionEvents.cs @@ -24,61 +24,3 @@ public sealed class GetExplosionResistanceEvent : EntityEventArgs, IInventoryRel ExplotionPrototype = id; } } - -/// -/// An explosion event. Used for client side rendering. -/// -[Serializable, NetSerializable] -public sealed class ExplosionEvent : EntityEventArgs -{ - public MapCoordinates Epicenter; - - public Dictionary>? SpaceTiles; - public Dictionary>> Tiles; - - public List Intensity; - - public string TypeID; - - public Matrix3 SpaceMatrix; - - public int ExplosionId; - - public ushort SpaceTileSize; - - public ExplosionEvent( - int explosionId, - MapCoordinates epicenter, - string typeID, - List intensity, - Dictionary>? spaceTiles, - Dictionary>> tiles, - Matrix3 spaceMatrix, - ushort spaceTileSize) - { - Epicenter = epicenter; - SpaceTiles = spaceTiles; - Tiles = tiles; - Intensity = intensity; - TypeID = typeID; - SpaceMatrix = spaceMatrix; - ExplosionId = explosionId; - SpaceTileSize = spaceTileSize; - } -} - -/// -/// Update visual rendering of the explosion to correspond to the servers processing of it. -/// -[Serializable, NetSerializable] -public sealed class ExplosionOverlayUpdateEvent : EntityEventArgs -{ - public int Index; - public int ExplosionId; - - public ExplosionOverlayUpdateEvent(int explosionId, int index) - { - Index = index; - ExplosionId = explosionId; - } -} diff --git a/Content.Shared/Explosion/SharedExplosionVisualsComponent.cs b/Content.Shared/Explosion/SharedExplosionVisualsComponent.cs new file mode 100644 index 0000000000..f30f1767e5 --- /dev/null +++ b/Content.Shared/Explosion/SharedExplosionVisualsComponent.cs @@ -0,0 +1,56 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Map; +using Robust.Shared.Serialization; + +namespace Content.Shared.Explosion; + +/// +/// Component that is used to send explosion overlay/visual data to an abstract explosion entity. +/// +[NetworkedComponent] +public abstract class SharedExplosionVisualsComponent : Component +{ + public MapCoordinates Epicenter; + public Dictionary>? SpaceTiles; + public Dictionary>> Tiles = new(); + public List Intensity = new(); + public string ExplosionType = string.Empty; + public Matrix3 SpaceMatrix; + public ushort SpaceTileSize; +} + +[Serializable, NetSerializable] +public sealed class ExplosionVisualsState : ComponentState +{ + public MapCoordinates Epicenter; + public Dictionary>? SpaceTiles; + public Dictionary>> Tiles; + public List Intensity; + public string ExplosionType = string.Empty; + public Matrix3 SpaceMatrix; + public ushort SpaceTileSize; + + public ExplosionVisualsState( + MapCoordinates epicenter, + string typeID, + List intensity, + Dictionary>? spaceTiles, + Dictionary>> tiles, + Matrix3 spaceMatrix, + ushort spaceTileSize) + { + Epicenter = epicenter; + SpaceTiles = spaceTiles; + Tiles = tiles; + Intensity = intensity; + ExplosionType = typeID; + SpaceMatrix = spaceMatrix; + SpaceTileSize = spaceTileSize; + } +} + +[Serializable, NetSerializable] +public enum ExplosionAppearanceData +{ + Progress, // iteration index tracker for explosions that are still expanding outwards, +}