From 0cd715925ccab1ef64b2285eb00d7eec214f2aa2 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Tue, 28 May 2024 18:33:57 +0100 Subject: [PATCH 01/54] well it compiles... --- OpenDreamRuntime/AtomManager.cs | 17 ++++++-- .../Objects/Types/DreamObjectImage.cs | 25 ++++++++--- .../Objects/Types/DreamObjectMovable.cs | 8 +--- .../Objects/Types/DreamObjectTurf.cs | 15 ++++++- .../Rendering/DMISpriteComponent.cs | 33 +++++--------- OpenDreamRuntime/Rendering/DMISpriteSystem.cs | 20 ++++++++- .../Rendering/ServerAppearanceSystem.cs | 43 ++++++++++++++++++- .../Rendering/SharedAppearanceSystem.cs | 4 ++ 8 files changed, 123 insertions(+), 42 deletions(-) diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index a5cd5e34e4..a5c0ef22da 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -36,6 +36,8 @@ public sealed class AtomManager { private ServerVerbSystem VerbSystem => _verbSystem ??= _entitySystemManager.GetEntitySystem(); private ServerAppearanceSystem? _appearanceSystem; private ServerVerbSystem? _verbSystem; + private DMISpriteSystem DMISpriteSystem => _dmiSpriteSystem ??= _entitySystemManager.GetEntitySystem(); + private DMISpriteSystem? _dmiSpriteSystem; // ReSharper disable ForCanBeConvertedToForeach (the collections could be added to) public IEnumerable EnumerateAtoms(TreeEntry? filterType = null) { @@ -191,7 +193,7 @@ public EntityUid CreateMovableEntity(DreamObjectMovable movable) { var entity = _entityManager.SpawnEntity(null, new MapCoordinates(0, 0, MapId.Nullspace)); DMISpriteComponent sprite = _entityManager.AddComponent(entity); - sprite.SetAppearance(GetAppearanceFromDefinition(movable.ObjectDefinition)); + DMISpriteSystem.SetSpriteAppearance(new(entity, sprite), GetAppearanceFromDefinition(movable.ObjectDefinition)); _entityToAtom.Add(entity, movable); return entity; @@ -473,7 +475,6 @@ public bool TryGetAppearance(DreamObject atom, [NotNullWhen(true)] out IconAppea public void UpdateAppearance(DreamObject atom, Action update) { var appearance = MustGetAppearance(atom); appearance = (appearance != null) ? new(appearance) : new(); // Clone the appearance - update(appearance); SetAtomAppearance(atom, appearance); } @@ -482,12 +483,20 @@ public void SetAtomAppearance(DreamObject atom, IconAppearance appearance) { if (atom is DreamObjectTurf turf) { _dreamMapManager.SetTurfAppearance(turf, appearance); } else if (atom is DreamObjectMovable movable) { - movable.SpriteComponent.SetAppearance(appearance); + DMISpriteSystem.SetSpriteAppearance(new(movable.Entity, movable.SpriteComponent), appearance); } else if (atom is DreamObjectImage image) { image.Appearance = appearance; } } + public void SetMovableScreenLoc(DreamObjectMovable movable, ScreenLocation screenLocation) { + DMISpriteSystem.SetSpriteScreenLocation(new(movable.Entity, movable.SpriteComponent), screenLocation); + } + + public void SetSpriteAppearance(Entity ent, IconAppearance appearance) { + DMISpriteSystem.SetSpriteAppearance(ent, appearance); + } + public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasing easing, int loop, AnimationFlags flags, int delay, bool chainAnim, Action animate) { if (atom is not DreamObjectMovable movable) return; //Animating non-movables is unimplemented TODO: should handle images and maybe filters @@ -497,7 +506,7 @@ public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasi animate(appearance); // Don't send the updated appearance to clients, they will animate it - movable.SpriteComponent.SetAppearance(appearance, dirty: false); + DMISpriteSystem.SetSpriteAppearance(new(movable.Entity, movable.SpriteComponent), appearance, dirty: false); NetEntity ent = _entityManager.GetNetEntity(movable.Entity); diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index 5fa7741fe2..263f56cefa 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -6,7 +6,8 @@ namespace OpenDreamRuntime.Objects.Types; public sealed class DreamObjectImage : DreamObject { - public IconAppearance? Appearance; + private IconAppearance? _appearance = null; + public IconAppearance? Appearance { get => _appearance; set => SetAppearance(value); } private DreamObject? _loc; private DreamList _overlays; @@ -42,13 +43,17 @@ public override void Initialize(DreamProcArguments args) { base.Initialize(args); DreamValue icon = args.GetArgument(0); - if (icon.IsNull || !AtomManager.TryCreateAppearanceFrom(icon, out Appearance)) { + IconAppearance? iconAppearance = null; + if (icon.IsNull || !AtomManager.TryCreateAppearanceFrom(icon, out iconAppearance)) { // Use a default appearance, but log a warning about it if icon wasn't null Appearance = new(AtomManager.GetAppearanceFromDefinition(ObjectDefinition)); if (!icon.IsNull) Logger.GetSawmill("opendream.image") .Warning($"Attempted to create an /image from {icon}. This is invalid and a default image was created instead."); } + if(iconAppearance is not null) + Appearance = iconAppearance; + int argIndex = 1; DreamValue loc = args.GetArgument(1); @@ -111,7 +116,7 @@ protected override void SetVar(string varName, DreamValue value) { Appearance = newAppearance; if(_entity != EntityUid.Invalid) { DMISpriteComponent sprite = EntityManager.GetComponent(_entity); - sprite.SetAppearance(Appearance!); + AtomManager.SetSpriteAppearance(new(_entity, sprite), Appearance); } break; case "loc": @@ -209,7 +214,7 @@ protected override void SetVar(string varName, DreamValue value) { AtomManager.SetAppearanceVar(Appearance!, varName, value); if(_entity != EntityUid.Invalid) { DMISpriteComponent sprite = EntityManager.GetComponent(_entity); - sprite.SetAppearance(Appearance!); + AtomManager.SetSpriteAppearance(new(_entity, sprite), Appearance); } break; } @@ -231,12 +236,22 @@ public EntityUid GetEntity() { if(_entity == EntityUid.Invalid) { _entity = EntityManager.SpawnEntity(null, new MapCoordinates(0, 0, MapId.Nullspace)); DMISpriteComponent sprite = EntityManager.AddComponent(_entity); - sprite.SetAppearance(Appearance!); + AtomManager.SetSpriteAppearance(new(_entity, sprite), Appearance); } return _entity; } + public void SetAppearance(IconAppearance? appearance) { + if(_appearance is not null) + AppearanceSystem!.DecreaseAppearanceRefCount(_appearance); + if(appearance is not null) + AppearanceSystem!.IncreaseAppearanceRefCount(appearance); + _appearance = appearance; + } + protected override void HandleDeletion() { + if(_appearance is not null) + AppearanceSystem!.DecreaseAppearanceRefCount(_appearance); if(_entity != EntityUid.Invalid) { EntityManager.DeleteEntity(_entity); } diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs b/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs index 36c3ee56ab..0762162664 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs @@ -23,13 +23,7 @@ public class DreamObjectMovable : DreamObjectAtom { private string? ScreenLoc { get => _screenLoc; set { - _screenLoc = value; - if (!EntityManager.TryGetComponent(Entity, out var sprite)) - return; - - sprite.ScreenLocation = !string.IsNullOrEmpty(value) ? - new ScreenLocation(value) : - new ScreenLocation(0, 0, 0, 0); + AtomManager.SetMovableScreenLoc(this, !string.IsNullOrEmpty(value) ? new ScreenLocation(value) : new ScreenLocation(0, 0, 0, 0)); } } diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs index 3ceb6e995a..4b4a1e5293 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs @@ -4,7 +4,9 @@ public sealed class DreamObjectTurf : DreamObjectAtom { public readonly int X, Y, Z; public readonly IDreamMapManager.Cell Cell; public readonly TurfContentsList Contents; - public int AppearanceId; + public int AppearanceId { get => _appearanceId; set => SetAppearanceId(value); } + + private int _appearanceId = -1; public DreamObjectTurf(DreamObjectDefinition objectDefinition, int x, int y, int z, IDreamMapManager.Cell cell) : base(objectDefinition) { X = x; @@ -63,4 +65,15 @@ protected override void SetVar(string varName, DreamValue value) { break; } } + + public void SetAppearanceId(int appearanceId) { + if (_appearanceId != -1) { + AppearanceSystem!.DecreaseAppearanceRefCount(_appearanceId); + } + + _appearanceId = appearanceId; + + AppearanceSystem!.IncreaseAppearanceRefCount(_appearanceId); + + } } diff --git a/OpenDreamRuntime/Rendering/DMISpriteComponent.cs b/OpenDreamRuntime/Rendering/DMISpriteComponent.cs index f89e0b9047..3976d36e41 100644 --- a/OpenDreamRuntime/Rendering/DMISpriteComponent.cs +++ b/OpenDreamRuntime/Rendering/DMISpriteComponent.cs @@ -1,27 +1,14 @@ using OpenDreamShared.Dream; using OpenDreamShared.Rendering; -namespace OpenDreamRuntime.Rendering { - [RegisterComponent] - public sealed partial class DMISpriteComponent : SharedDMISpriteComponent { - [ViewVariables] - public ScreenLocation? ScreenLocation { - get => _screenLocation; - set { - _screenLocation = value; - Dirty(); - } - } - private ScreenLocation? _screenLocation; - - [ViewVariables] public IconAppearance? Appearance { get; private set; } - - public void SetAppearance(IconAppearance? appearance, bool dirty = true) { - Appearance = appearance; - - if (dirty) { - Dirty(); - } - } - } +namespace OpenDreamRuntime.Rendering; +[RegisterComponent] +public sealed partial class DMISpriteComponent : SharedDMISpriteComponent { + [ViewVariables] + [Access(typeof(DMISpriteSystem))] + public ScreenLocation ScreenLocation; + + [Access(typeof(DMISpriteSystem))] + [ViewVariables] public IconAppearance? Appearance; } + diff --git a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs index 7d7d7c865c..9e18d42ce5 100644 --- a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs +++ b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs @@ -1,4 +1,5 @@ -using OpenDreamShared.Rendering; +using OpenDreamShared.Dream; +using OpenDreamShared.Rendering; using Robust.Shared.GameStates; namespace OpenDreamRuntime.Rendering; @@ -18,4 +19,21 @@ private void GetComponentState(EntityUid uid, DMISpriteComponent component, ref args.State = new SharedDMISpriteComponent.DMISpriteComponentState(appearanceId, component.ScreenLocation); } + + public void SetSpriteAppearance(Entity ent, IconAppearance appearance, bool dirty = true) { + DMISpriteComponent component = ent.Comp; + _appearance.IncreaseAppearanceRefCount(appearance); + if(component.Appearance is not null) + _appearance.DecreaseAppearanceRefCount(component.Appearance); + + component.Appearance = appearance; + if(dirty) + Dirty(ent, component); + } + + public void SetSpriteScreenLocation(Entity ent, ScreenLocation screenLocation) { + DMISpriteComponent component = ent.Comp; + component.ScreenLocation = screenLocation; + Dirty(ent, component); + } } diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index c1242c3f60..dc3588a007 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -10,6 +10,7 @@ namespace OpenDreamRuntime.Rendering; public sealed class ServerAppearanceSystem : SharedAppearanceSystem { private readonly Dictionary _appearanceToId = new(); private readonly Dictionary _idToAppearance = new(); + private readonly Dictionary _appearanceRefCounts = new(); private int _appearanceIdCounter; /// @@ -37,6 +38,47 @@ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { } } + public void IncreaseAppearanceRefCount(IconAppearance appearance) { + lock (_lock) { + if (!_appearanceRefCounts.TryGetValue(appearance, out int count)) { + count = 0; + } + _appearanceRefCounts[appearance] = count + 1; + } + } + public void IncreaseAppearanceRefCount(int appearanceId) { + if (!_idToAppearance.TryGetValue(appearanceId, out IconAppearance? appearance)) { + return; + } + + IncreaseAppearanceRefCount(appearance); + } + + public void DecreaseAppearanceRefCount(IconAppearance appearance) { + lock (_lock) { + if (!_appearanceRefCounts.TryGetValue(appearance, out int count)) { + throw new InvalidOperationException("Appearance ref count is already 0"); + } + + if (count == 1) { + _appearanceRefCounts.Remove(appearance); + _appearanceToId.Remove(appearance); + if(_appearanceToId.TryGetValue(appearance, out int id)) + _idToAppearance.Remove(id); + RaiseNetworkEvent(new RemoveAppearanceEvent(id)); + //let the GC sort out the rest + } else { + _appearanceRefCounts[appearance] = count - 1; + } + } + } + public void DecreaseAppearanceRefCount(int appearanceId) { + if (!_idToAppearance.TryGetValue(appearanceId, out IconAppearance? appearance)) { + return; + } + + DecreaseAppearanceRefCount(appearance); + } public int AddAppearance(IconAppearance appearance) { lock (_lock) { if (!_appearanceToId.TryGetValue(appearance, out int appearanceId)) { @@ -45,7 +87,6 @@ public int AddAppearance(IconAppearance appearance) { _idToAppearance.Add(appearanceId, appearance); RaiseNetworkEvent(new NewAppearanceEvent(appearanceId, appearance)); } - return appearanceId; } } diff --git a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs index 389d7943de..034eb5c715 100644 --- a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs +++ b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs @@ -17,6 +17,10 @@ public sealed class NewAppearanceEvent(int appearanceId, IconAppearance appearan public int AppearanceId { get; } = appearanceId; public IconAppearance Appearance { get; } = appearance; } + [Serializable, NetSerializable] + public sealed class RemoveAppearanceEvent(int appearanceId) : EntityEventArgs { + public int AppearanceId { get; } = appearanceId; + } [Serializable, NetSerializable] public sealed class AnimationEvent(NetEntity entity, int targetAppearanceId, TimeSpan duration, AnimationEasing easing, int loop, AnimationFlags flags, int delay, bool chainAnim) From 8dd14ad60975b427316caa5d2dbaaff34d6b6c2a Mon Sep 17 00:00:00 2001 From: amylizzle Date: Tue, 28 May 2024 18:43:00 +0100 Subject: [PATCH 02/54] plus one for you --- OpenDreamRuntime/AtomManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index a5c0ef22da..416251ee35 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -600,6 +600,7 @@ public IconAppearance GetAppearanceFromDefinition(DreamObjectDefinition def) { } _definitionAppearanceCache.Add(def, appearance); + AppearanceSystem.IncreaseAppearanceRefCount(appearance); return appearance; } From b985a606a4b6cc1ff1ab57b354e245ec3f12420e Mon Sep 17 00:00:00 2001 From: amylizzle Date: Wed, 29 May 2024 11:29:45 +0100 Subject: [PATCH 03/54] work you fucker --- OpenDreamClient/Rendering/ClientAppearanceSystem.cs | 1 + OpenDreamRuntime/DreamManager.cs | 1 + OpenDreamRuntime/Objects/Types/DreamObjectImage.cs | 11 +++++++---- OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs | 3 ++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs index 2facd89202..849357404f 100644 --- a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs +++ b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs @@ -23,6 +23,7 @@ internal sealed class ClientAppearanceSystem : SharedAppearanceSystem { public override void Initialize() { SubscribeNetworkEvent(OnAllAppearances); SubscribeNetworkEvent(OnNewAppearance); + SubscribeNetworkEvent(e => _appearances.Remove(e.AppearanceId)); SubscribeNetworkEvent(OnAnimation); SubscribeLocalEvent(OnWorldAABB); } diff --git a/OpenDreamRuntime/DreamManager.cs b/OpenDreamRuntime/DreamManager.cs index 46ee16f661..466c8a3c3c 100644 --- a/OpenDreamRuntime/DreamManager.cs +++ b/OpenDreamRuntime/DreamManager.cs @@ -224,6 +224,7 @@ public string CreateRef(DreamValue value) { refType = RefType.DreamAppearance; _appearanceSystem ??= _entitySystemManager.GetEntitySystem(); idx = (int)_appearanceSystem.AddAppearance(appearance); + _appearanceSystem.IncreaseAppearanceRefCount(appearance); } else if (value.TryGetValueAsDreamResource(out var refRsc)) { refType = RefType.DreamResource; idx = refRsc.Id; diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index 263f56cefa..a4fc3006b5 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -61,19 +61,22 @@ public override void Initialize(DreamProcArguments args) { argIndex = 2; } + iconAppearance = Appearance!; //mutate the appearance and then set it at the end to handle ref counts and client update foreach (string argName in IconCreationArgs) { var arg = args.GetArgument(argIndex++); if (arg.IsNull) continue; - AtomManager.SetAppearanceVar(Appearance, argName, arg); + AtomManager.SetAppearanceVar(iconAppearance, argName, arg); if (argName == "dir") { // If a dir is explicitly given in the constructor then overlays using this won't use their owner's dir // Setting dir after construction does not affect this // This is undocumented and I hate it - Appearance.InheritsDirection = false; + iconAppearance.InheritsDirection = false; } } + AppearanceSystem!.AddAppearance(iconAppearance); // this is a no-op if the appearance is already in the system + Appearance = iconAppearance; } protected override bool TryGetVar(string varName, out DreamValue value) { @@ -242,10 +245,10 @@ public EntityUid GetEntity() { } public void SetAppearance(IconAppearance? appearance) { - if(_appearance is not null) - AppearanceSystem!.DecreaseAppearanceRefCount(_appearance); if(appearance is not null) AppearanceSystem!.IncreaseAppearanceRefCount(appearance); + if(_appearance is not null) + AppearanceSystem!.DecreaseAppearanceRefCount(_appearance); _appearance = appearance; } diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs index 4b4a1e5293..941c463289 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs @@ -67,13 +67,14 @@ protected override void SetVar(string varName, DreamValue value) { } public void SetAppearanceId(int appearanceId) { + AppearanceSystem!.IncreaseAppearanceRefCount(appearanceId); if (_appearanceId != -1) { AppearanceSystem!.DecreaseAppearanceRefCount(_appearanceId); } _appearanceId = appearanceId; - AppearanceSystem!.IncreaseAppearanceRefCount(_appearanceId); + } } From f2bf4b1537706d9a159ad50397edce13d590091a Mon Sep 17 00:00:00 2001 From: amylizzle Date: Wed, 29 May 2024 12:18:51 +0100 Subject: [PATCH 04/54] close, but no cigar --- .../Objects/Types/DreamObjectImage.cs | 10 +++++--- .../Objects/Types/DreamObjectTurf.cs | 8 +++---- .../Rendering/ServerAppearanceSystem.cs | 23 +++++++++++++++---- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index a4fc3006b5..1f764771cd 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -61,11 +61,13 @@ public override void Initialize(DreamProcArguments args) { argIndex = 2; } - iconAppearance = Appearance!; //mutate the appearance and then set it at the end to handle ref counts and client update + iconAppearance = null; //mutate the appearance and then set it at the end to handle ref counts and client update foreach (string argName in IconCreationArgs) { var arg = args.GetArgument(argIndex++); if (arg.IsNull) continue; + if (iconAppearance is null) + iconAppearance = new(Appearance!); AtomManager.SetAppearanceVar(iconAppearance, argName, arg); if (argName == "dir") { @@ -75,8 +77,10 @@ public override void Initialize(DreamProcArguments args) { iconAppearance.InheritsDirection = false; } } - AppearanceSystem!.AddAppearance(iconAppearance); // this is a no-op if the appearance is already in the system - Appearance = iconAppearance; + if (iconAppearance is not null) { + AppearanceSystem!.AddAppearance(iconAppearance); // this is a no-op if the appearance is already in the system + Appearance = iconAppearance; + } } protected override bool TryGetVar(string varName, out DreamValue value) { diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs index 941c463289..5566181fd9 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs @@ -4,9 +4,9 @@ public sealed class DreamObjectTurf : DreamObjectAtom { public readonly int X, Y, Z; public readonly IDreamMapManager.Cell Cell; public readonly TurfContentsList Contents; - public int AppearanceId { get => _appearanceId; set => SetAppearanceId(value); } + public int AppearanceId { get => _appearanceId!.Value; set => SetAppearanceId(value); } - private int _appearanceId = -1; + private int? _appearanceId = null; public DreamObjectTurf(DreamObjectDefinition objectDefinition, int x, int y, int z, IDreamMapManager.Cell cell) : base(objectDefinition) { X = x; @@ -68,8 +68,8 @@ protected override void SetVar(string varName, DreamValue value) { public void SetAppearanceId(int appearanceId) { AppearanceSystem!.IncreaseAppearanceRefCount(appearanceId); - if (_appearanceId != -1) { - AppearanceSystem!.DecreaseAppearanceRefCount(_appearanceId); + if (_appearanceId is not null) { + AppearanceSystem!.DecreaseAppearanceRefCount(_appearanceId.Value); } _appearanceId = appearanceId; diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index dc3588a007..852fd74d97 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -43,11 +43,18 @@ public void IncreaseAppearanceRefCount(IconAppearance appearance) { if (!_appearanceRefCounts.TryGetValue(appearance, out int count)) { count = 0; } + foreach(var overlayid in appearance.Overlays) { + IncreaseAppearanceRefCount(overlayid); + } + foreach(var underlayid in appearance.Underlays) { + IncreaseAppearanceRefCount(underlayid); + } _appearanceRefCounts[appearance] = count + 1; } } public void IncreaseAppearanceRefCount(int appearanceId) { if (!_idToAppearance.TryGetValue(appearanceId, out IconAppearance? appearance)) { + Logger.Debug($"Appearance id:{appearanceId} not found in _idToAppearance, skipping add."); return; } @@ -57,15 +64,22 @@ public void IncreaseAppearanceRefCount(int appearanceId) { public void DecreaseAppearanceRefCount(IconAppearance appearance) { lock (_lock) { if (!_appearanceRefCounts.TryGetValue(appearance, out int count)) { - throw new InvalidOperationException("Appearance ref count is already 0"); + throw new InvalidOperationException($"Appearance {appearance.GetHashCode()} ref count is already 0. You might be trying to remove an appearance that was never added."); } if (count == 1) { + foreach(var overlayid in appearance.Overlays) { + DecreaseAppearanceRefCount(overlayid); + } + foreach(var underlayid in appearance.Underlays) { + DecreaseAppearanceRefCount(underlayid); + } + if(_appearanceToId.TryGetValue(appearance, out int id)) { + _idToAppearance.Remove(id); + RaiseNetworkEvent(new RemoveAppearanceEvent(id)); + } _appearanceRefCounts.Remove(appearance); _appearanceToId.Remove(appearance); - if(_appearanceToId.TryGetValue(appearance, out int id)) - _idToAppearance.Remove(id); - RaiseNetworkEvent(new RemoveAppearanceEvent(id)); //let the GC sort out the rest } else { _appearanceRefCounts[appearance] = count - 1; @@ -74,6 +88,7 @@ public void DecreaseAppearanceRefCount(IconAppearance appearance) { } public void DecreaseAppearanceRefCount(int appearanceId) { if (!_idToAppearance.TryGetValue(appearanceId, out IconAppearance? appearance)) { + Logger.Debug($"Appearance id:{appearanceId} not found in _idToAppearance, skipping removal."); return; } From a3d195da69582ffa247ce09d333835d1ec2fdc26 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Wed, 29 May 2024 12:37:35 +0100 Subject: [PATCH 05/54] gotcha --- OpenDreamRuntime/Objects/Types/DreamObjectImage.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index 1f764771cd..1616107a1d 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -218,7 +218,9 @@ protected override void SetVar(string varName, DreamValue value) { } default: if (AtomManager.IsValidAppearanceVar(varName)) { - AtomManager.SetAppearanceVar(Appearance!, varName, value); + IconAppearance iconAppearance = new(Appearance!); + AtomManager.SetAppearanceVar(iconAppearance, varName, value); + Appearance = iconAppearance; if(_entity != EntityUid.Invalid) { DMISpriteComponent sprite = EntityManager.GetComponent(_entity); AtomManager.SetSpriteAppearance(new(_entity, sprite), Appearance); From 68c058e90a330484c900ca5576131dc10922fc36 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Wed, 29 May 2024 13:29:29 +0100 Subject: [PATCH 06/54] linter and cleanup --- .../Objects/Types/DreamObjectImage.cs | 5 ++--- .../Objects/Types/DreamObjectMovable.cs | 9 ++++++--- .../Objects/Types/DreamObjectTurf.cs | 2 +- .../Rendering/ServerAppearanceSystem.cs | 16 ++++++++-------- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index 1616107a1d..2de9a421b0 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -6,7 +6,7 @@ namespace OpenDreamRuntime.Objects.Types; public sealed class DreamObjectImage : DreamObject { - private IconAppearance? _appearance = null; + private IconAppearance? _appearance; public IconAppearance? Appearance { get => _appearance; set => SetAppearance(value); } private DreamObject? _loc; @@ -66,8 +66,7 @@ public override void Initialize(DreamProcArguments args) { var arg = args.GetArgument(argIndex++); if (arg.IsNull) continue; - if (iconAppearance is null) - iconAppearance = new(Appearance!); + iconAppearance ??= new(Appearance!); AtomManager.SetAppearanceVar(iconAppearance, argName, arg); if (argName == "dir") { diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs b/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs index 0762162664..4dacc228cc 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs @@ -22,9 +22,7 @@ public class DreamObjectMovable : DreamObjectAtom { private string? ScreenLoc { get => _screenLoc; - set { - AtomManager.SetMovableScreenLoc(this, !string.IsNullOrEmpty(value) ? new ScreenLocation(value) : new ScreenLocation(0, 0, 0, 0)); - } + set => SetScreenLoc(value); } private string? _screenLoc; @@ -192,4 +190,9 @@ private void SetLoc(DreamObjectAtom? loc) { throw new ArgumentException($"Invalid loc {loc}"); } } + + private void SetScreenLoc(string? screenLoc) { + _screenLoc = screenLoc; + AtomManager.SetMovableScreenLoc(this, !string.IsNullOrEmpty(screenLoc) ? new ScreenLocation(screenLoc) : new ScreenLocation(0, 0, 0, 0)); + } } diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs index 5566181fd9..7d0419f518 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs @@ -6,7 +6,7 @@ public sealed class DreamObjectTurf : DreamObjectAtom { public readonly TurfContentsList Contents; public int AppearanceId { get => _appearanceId!.Value; set => SetAppearanceId(value); } - private int? _appearanceId = null; + private int? _appearanceId; public DreamObjectTurf(DreamObjectDefinition objectDefinition, int x, int y, int z, IDreamMapManager.Cell cell) : base(objectDefinition) { X = x; diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 852fd74d97..0deea23266 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -13,6 +13,8 @@ public sealed class ServerAppearanceSystem : SharedAppearanceSystem { private readonly Dictionary _appearanceRefCounts = new(); private int _appearanceIdCounter; + private ISawmill _sawmill = default!; + /// /// This system is used by the PVS thread, we need to be thread-safe /// @@ -22,6 +24,7 @@ public sealed class ServerAppearanceSystem : SharedAppearanceSystem { public override void Initialize() { _playerManager.PlayerStatusChanged += OnPlayerStatusChanged; + _sawmill ??= Logger.GetSawmill("ServerAppearanceSystem"); } public override void Shutdown() { @@ -34,15 +37,14 @@ public override void Shutdown() { private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { if (e.NewStatus == SessionStatus.InGame) { - RaiseNetworkEvent(new AllAppearancesEvent(_idToAppearance), e.Session.ConnectedClient); + _sawmill.Debug($"Sending {_idToAppearance.Count} appearances to {e.Session.Channel.UserName}"); + RaiseNetworkEvent(new AllAppearancesEvent(_idToAppearance), e.Session.Channel); } } public void IncreaseAppearanceRefCount(IconAppearance appearance) { lock (_lock) { - if (!_appearanceRefCounts.TryGetValue(appearance, out int count)) { - count = 0; - } + int count = _appearanceRefCounts.GetValueOrDefault(appearance, 0); foreach(var overlayid in appearance.Overlays) { IncreaseAppearanceRefCount(overlayid); } @@ -54,8 +56,7 @@ public void IncreaseAppearanceRefCount(IconAppearance appearance) { } public void IncreaseAppearanceRefCount(int appearanceId) { if (!_idToAppearance.TryGetValue(appearanceId, out IconAppearance? appearance)) { - Logger.Debug($"Appearance id:{appearanceId} not found in _idToAppearance, skipping add."); - return; + throw new InvalidOperationException("Trying to increase ref count of an appearance that doesn't exist."); } IncreaseAppearanceRefCount(appearance); @@ -88,8 +89,7 @@ public void DecreaseAppearanceRefCount(IconAppearance appearance) { } public void DecreaseAppearanceRefCount(int appearanceId) { if (!_idToAppearance.TryGetValue(appearanceId, out IconAppearance? appearance)) { - Logger.Debug($"Appearance id:{appearanceId} not found in _idToAppearance, skipping removal."); - return; + throw new InvalidOperationException("Trying to decrease ref count of an appearance that doesn't exist."); } DecreaseAppearanceRefCount(appearance); From 24651fbdb57a550ed86e984301fa37442260196c Mon Sep 17 00:00:00 2001 From: amylizzle Date: Wed, 29 May 2024 13:38:12 +0100 Subject: [PATCH 07/54] fine, be like that --- OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 0deea23266..d4cef78c40 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -13,7 +13,7 @@ public sealed class ServerAppearanceSystem : SharedAppearanceSystem { private readonly Dictionary _appearanceRefCounts = new(); private int _appearanceIdCounter; - private ISawmill _sawmill = default!; + private ISawmill? _sawmill; /// /// This system is used by the PVS thread, we need to be thread-safe @@ -24,7 +24,6 @@ public sealed class ServerAppearanceSystem : SharedAppearanceSystem { public override void Initialize() { _playerManager.PlayerStatusChanged += OnPlayerStatusChanged; - _sawmill ??= Logger.GetSawmill("ServerAppearanceSystem"); } public override void Shutdown() { @@ -37,6 +36,7 @@ public override void Shutdown() { private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { if (e.NewStatus == SessionStatus.InGame) { + _sawmill ??= Logger.GetSawmill("ServerAppearanceSystem"); _sawmill.Debug($"Sending {_idToAppearance.Count} appearances to {e.Session.Channel.UserName}"); RaiseNetworkEvent(new AllAppearancesEvent(_idToAppearance), e.Session.Channel); } From f32412a49f6b4f830710dd10f8f19dba663e59ce Mon Sep 17 00:00:00 2001 From: amylizzle Date: Wed, 29 May 2024 15:09:29 +0100 Subject: [PATCH 08/54] break everything --- OpenDreamRuntime/Objects/Types/DreamObjectImage.cs | 3 ++- OpenDreamRuntime/ServerContentIoC.cs | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index 2de9a421b0..c24242d613 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -244,7 +244,8 @@ public EntityUid GetEntity() { if(_entity == EntityUid.Invalid) { _entity = EntityManager.SpawnEntity(null, new MapCoordinates(0, 0, MapId.Nullspace)); DMISpriteComponent sprite = EntityManager.AddComponent(_entity); - AtomManager.SetSpriteAppearance(new(_entity, sprite), Appearance); + if(Appearance is not null) + AtomManager.SetSpriteAppearance(new(_entity, sprite), Appearance); } return _entity; } diff --git a/OpenDreamRuntime/ServerContentIoC.cs b/OpenDreamRuntime/ServerContentIoC.cs index 77ec1706bf..2673ade59f 100644 --- a/OpenDreamRuntime/ServerContentIoC.cs +++ b/OpenDreamRuntime/ServerContentIoC.cs @@ -1,6 +1,7 @@ using OpenDreamRuntime.Objects; using OpenDreamRuntime.Procs; using OpenDreamRuntime.Procs.DebugAdapter; +using OpenDreamRuntime.Rendering; using OpenDreamRuntime.Resources; namespace OpenDreamRuntime { @@ -14,6 +15,7 @@ public static void Register(bool unitTests = false) { IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); #if DEBUG IoCManager.Register(); From 8487c9356801d56c1d05cd718cfd235970542ed5 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Wed, 29 May 2024 15:53:43 +0100 Subject: [PATCH 09/54] I am confused and bewlidered --- OpenDreamRuntime/Rendering/DMISpriteSystem.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs index 9e18d42ce5..192122291b 100644 --- a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs +++ b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs @@ -5,16 +5,18 @@ namespace OpenDreamRuntime.Rendering; public sealed class DMISpriteSystem : EntitySystem { - [Dependency] private readonly ServerAppearanceSystem _appearance = default!; + private ServerAppearanceSystem? _appearance; + [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!; public override void Initialize() { SubscribeLocalEvent(GetComponentState); + _appearance = _entitySystemManager.GetEntitySystem(); } private void GetComponentState(EntityUid uid, DMISpriteComponent component, ref ComponentGetState args) { int? appearanceId = null; if (component.Appearance != null) { - appearanceId = _appearance.AddAppearance(component.Appearance); + appearanceId = _appearance?.AddAppearance(component.Appearance); } args.State = new SharedDMISpriteComponent.DMISpriteComponentState(appearanceId, component.ScreenLocation); @@ -22,9 +24,9 @@ private void GetComponentState(EntityUid uid, DMISpriteComponent component, ref public void SetSpriteAppearance(Entity ent, IconAppearance appearance, bool dirty = true) { DMISpriteComponent component = ent.Comp; - _appearance.IncreaseAppearanceRefCount(appearance); + _appearance?.IncreaseAppearanceRefCount(appearance); if(component.Appearance is not null) - _appearance.DecreaseAppearanceRefCount(component.Appearance); + _appearance?.DecreaseAppearanceRefCount(component.Appearance); component.Appearance = appearance; if(dirty) From b9fcfc95d7f08314ffc27262ea77414ed06f0523 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Fri, 7 Jun 2024 14:18:01 +0100 Subject: [PATCH 10/54] lint --- OpenDreamRuntime/Objects/Types/DreamObjectImage.cs | 3 ++- OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs | 3 --- OpenDreamRuntime/Rendering/DMISpriteComponent.cs | 1 + OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs | 6 ++++++ OpenDreamShared/Rendering/SharedAppearanceSystem.cs | 1 + 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index c24242d613..c2372b577b 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -51,6 +51,7 @@ public override void Initialize(DreamProcArguments args) { Logger.GetSawmill("opendream.image") .Warning($"Attempted to create an /image from {icon}. This is invalid and a default image was created instead."); } + if(iconAppearance is not null) Appearance = iconAppearance; @@ -222,7 +223,7 @@ protected override void SetVar(string varName, DreamValue value) { Appearance = iconAppearance; if(_entity != EntityUid.Invalid) { DMISpriteComponent sprite = EntityManager.GetComponent(_entity); - AtomManager.SetSpriteAppearance(new(_entity, sprite), Appearance); + AtomManager.SetSpriteAppearance(new(_entity, sprite), Appearance!); } break; } diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs index 7d0419f518..33e1a1079f 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs @@ -73,8 +73,5 @@ public void SetAppearanceId(int appearanceId) { } _appearanceId = appearanceId; - - - } } diff --git a/OpenDreamRuntime/Rendering/DMISpriteComponent.cs b/OpenDreamRuntime/Rendering/DMISpriteComponent.cs index 3976d36e41..2a774470bb 100644 --- a/OpenDreamRuntime/Rendering/DMISpriteComponent.cs +++ b/OpenDreamRuntime/Rendering/DMISpriteComponent.cs @@ -2,6 +2,7 @@ using OpenDreamShared.Rendering; namespace OpenDreamRuntime.Rendering; + [RegisterComponent] public sealed partial class DMISpriteComponent : SharedDMISpriteComponent { [ViewVariables] diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index d4cef78c40..89a10b158f 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -48,12 +48,15 @@ public void IncreaseAppearanceRefCount(IconAppearance appearance) { foreach(var overlayid in appearance.Overlays) { IncreaseAppearanceRefCount(overlayid); } + foreach(var underlayid in appearance.Underlays) { IncreaseAppearanceRefCount(underlayid); } + _appearanceRefCounts[appearance] = count + 1; } } + public void IncreaseAppearanceRefCount(int appearanceId) { if (!_idToAppearance.TryGetValue(appearanceId, out IconAppearance? appearance)) { throw new InvalidOperationException("Trying to increase ref count of an appearance that doesn't exist."); @@ -72,13 +75,16 @@ public void DecreaseAppearanceRefCount(IconAppearance appearance) { foreach(var overlayid in appearance.Overlays) { DecreaseAppearanceRefCount(overlayid); } + foreach(var underlayid in appearance.Underlays) { DecreaseAppearanceRefCount(underlayid); } + if(_appearanceToId.TryGetValue(appearance, out int id)) { _idToAppearance.Remove(id); RaiseNetworkEvent(new RemoveAppearanceEvent(id)); } + _appearanceRefCounts.Remove(appearance); _appearanceToId.Remove(appearance); //let the GC sort out the rest diff --git a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs index 034eb5c715..d497286b4b 100644 --- a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs +++ b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs @@ -17,6 +17,7 @@ public sealed class NewAppearanceEvent(int appearanceId, IconAppearance appearan public int AppearanceId { get; } = appearanceId; public IconAppearance Appearance { get; } = appearance; } + [Serializable, NetSerializable] public sealed class RemoveAppearanceEvent(int appearanceId) : EntityEventArgs { public int AppearanceId { get; } = appearanceId; From 189d3ef6e6488f53dc25ccec2a441fcdf9c9a651 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Fri, 7 Jun 2024 14:34:15 +0100 Subject: [PATCH 11/54] more lint --- OpenDreamRuntime/Objects/Types/DreamObjectImage.cs | 1 + OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index c2372b577b..0477551208 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -77,6 +77,7 @@ public override void Initialize(DreamProcArguments args) { iconAppearance.InheritsDirection = false; } } + if (iconAppearance is not null) { AppearanceSystem!.AddAppearance(iconAppearance); // this is a no-op if the appearance is already in the system Appearance = iconAppearance; diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 89a10b158f..c4ea582f3a 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -93,6 +93,7 @@ public void DecreaseAppearanceRefCount(IconAppearance appearance) { } } } + public void DecreaseAppearanceRefCount(int appearanceId) { if (!_idToAppearance.TryGetValue(appearanceId, out IconAppearance? appearance)) { throw new InvalidOperationException("Trying to decrease ref count of an appearance that doesn't exist."); From 1b03b2dceee2dee68e39a6a9f2add3ef50445bab Mon Sep 17 00:00:00 2001 From: amylizzle Date: Mon, 10 Jun 2024 10:31:36 +0100 Subject: [PATCH 12/54] gross --- OpenDreamRuntime/AtomManager.cs | 16 +++++++++++----- OpenDreamRuntime/Rendering/DMISpriteSystem.cs | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index 416251ee35..cdc33bde0a 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -32,7 +32,13 @@ public sealed class AtomManager { private readonly Dictionary _entityToAtom = new(); private readonly Dictionary _definitionAppearanceCache = new(); - private ServerAppearanceSystem AppearanceSystem => _appearanceSystem ??= _entitySystemManager.GetEntitySystem(); + private ServerAppearanceSystem? AppearanceSystem{ + get { + if(_appearanceSystem is null) + _entitySystemManager.TryGetEntitySystem(out _appearanceSystem); + return _appearanceSystem; + } + } private ServerVerbSystem VerbSystem => _verbSystem ??= _entitySystemManager.GetEntitySystem(); private ServerAppearanceSystem? _appearanceSystem; private ServerVerbSystem? _verbSystem; @@ -448,7 +454,7 @@ public DreamValue GetAppearanceVar(IconAppearance appearance, string varName) { /// The atom to find the appearance of. public IconAppearance? MustGetAppearance(DreamObject atom) { return atom switch { - DreamObjectTurf turf => AppearanceSystem.MustGetAppearance(turf.AppearanceId), + DreamObjectTurf turf => AppearanceSystem?.MustGetAppearance(turf.AppearanceId), DreamObjectMovable movable => movable.SpriteComponent.Appearance, DreamObjectArea => new IconAppearance(), DreamObjectImage image => image.Appearance, @@ -461,7 +467,7 @@ public DreamValue GetAppearanceVar(IconAppearance appearance, string varName) { /// public bool TryGetAppearance(DreamObject atom, [NotNullWhen(true)] out IconAppearance? appearance) { if (atom is DreamObjectTurf turf) - appearance = AppearanceSystem.MustGetAppearance(turf.AppearanceId); + appearance = AppearanceSystem?.MustGetAppearance(turf.AppearanceId); else if (atom is DreamObjectMovable movable) appearance = movable.SpriteComponent.Appearance; else if (atom is DreamObjectImage image) @@ -510,7 +516,7 @@ public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasi NetEntity ent = _entityManager.GetNetEntity(movable.Entity); - AppearanceSystem.Animate(ent, appearance, duration, easing, loop, flags, delay, chainAnim); + AppearanceSystem?.Animate(ent, appearance, duration, easing, loop, flags, delay, chainAnim); } public bool TryCreateAppearanceFrom(DreamValue value, [NotNullWhen(true)] out IconAppearance? appearance) { @@ -600,7 +606,7 @@ public IconAppearance GetAppearanceFromDefinition(DreamObjectDefinition def) { } _definitionAppearanceCache.Add(def, appearance); - AppearanceSystem.IncreaseAppearanceRefCount(appearance); + AppearanceSystem?.IncreaseAppearanceRefCount(appearance); return appearance; } diff --git a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs index 192122291b..7afc4d00ed 100644 --- a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs +++ b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs @@ -10,7 +10,7 @@ public sealed class DMISpriteSystem : EntitySystem { public override void Initialize() { SubscribeLocalEvent(GetComponentState); - _appearance = _entitySystemManager.GetEntitySystem(); + _entitySystemManager.TryGetEntitySystem(out _appearance); } private void GetComponentState(EntityUid uid, DMISpriteComponent component, ref ComponentGetState args) { From 1fb5f03e44677d0db8de7a83b57456932e06e016 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Mon, 10 Jun 2024 10:38:23 +0100 Subject: [PATCH 13/54] move a couple broken tests --- Content.Tests/DMProject/{Tests => Broken Tests}/Image/Image.dm | 0 Content.Tests/DMProject/{Tests => Broken Tests}/Image/subclass.dm | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Content.Tests/DMProject/{Tests => Broken Tests}/Image/Image.dm (100%) rename Content.Tests/DMProject/{Tests => Broken Tests}/Image/subclass.dm (100%) diff --git a/Content.Tests/DMProject/Tests/Image/Image.dm b/Content.Tests/DMProject/Broken Tests/Image/Image.dm similarity index 100% rename from Content.Tests/DMProject/Tests/Image/Image.dm rename to Content.Tests/DMProject/Broken Tests/Image/Image.dm diff --git a/Content.Tests/DMProject/Tests/Image/subclass.dm b/Content.Tests/DMProject/Broken Tests/Image/subclass.dm similarity index 100% rename from Content.Tests/DMProject/Tests/Image/subclass.dm rename to Content.Tests/DMProject/Broken Tests/Image/subclass.dm From b2bb950210a7754308e8361c18682df7a333e804 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Mon, 10 Jun 2024 10:39:54 +0100 Subject: [PATCH 14/54] lint --- OpenDreamRuntime/AtomManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index cdc33bde0a..5f2b7b1d3c 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -35,7 +35,7 @@ public sealed class AtomManager { private ServerAppearanceSystem? AppearanceSystem{ get { if(_appearanceSystem is null) - _entitySystemManager.TryGetEntitySystem(out _appearanceSystem); + _entitySystemManager.TryGetEntitySystem(out _appearanceSystem); return _appearanceSystem; } } From 93df5d4c9cf4db1573b63183095d27309ce93177 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Mon, 10 Jun 2024 10:54:30 +0100 Subject: [PATCH 15/54] more lint --- OpenDreamRuntime/Rendering/DMISpriteSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs index 7afc4d00ed..0c561b4c77 100644 --- a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs +++ b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs @@ -10,7 +10,7 @@ public sealed class DMISpriteSystem : EntitySystem { public override void Initialize() { SubscribeLocalEvent(GetComponentState); - _entitySystemManager.TryGetEntitySystem(out _appearance); + _entitySystemManager.TryGetEntitySystem(out _appearance); } private void GetComponentState(EntityUid uid, DMISpriteComponent component, ref ComponentGetState args) { From 6e1a02daae77bd7f5e54244b72a8bdaa484d7e46 Mon Sep 17 00:00:00 2001 From: Amy <3855802+amylizzle@users.noreply.github.com> Date: Fri, 14 Jun 2024 09:03:37 +0100 Subject: [PATCH 16/54] Update OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs --- OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 48ed515c6b..c8cb76af7e 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -14,7 +14,6 @@ public sealed class ServerAppearanceSystem : SharedAppearanceSystem { private readonly Dictionary _appearanceRefCounts = new(); private int _appearanceIdCounter; - private ISawmill? _sawmill; /// /// This system is used by the PVS thread, we need to be thread-safe From a5e76dd5a4403b1334ae99a8650035ed4ab0bbdd Mon Sep 17 00:00:00 2001 From: amylizzle Date: Tue, 9 Jul 2024 12:17:16 +0100 Subject: [PATCH 17/54] area appearance, but memory churn --- OpenDreamRuntime/DreamMapManager.cs | 4 +++- OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/OpenDreamRuntime/DreamMapManager.cs b/OpenDreamRuntime/DreamMapManager.cs index 5f8ee2fbd3..dc20b6e526 100644 --- a/OpenDreamRuntime/DreamMapManager.cs +++ b/OpenDreamRuntime/DreamMapManager.cs @@ -216,8 +216,10 @@ public void SetAreaAppearance(DreamObjectArea area, IconAppearance appearance) { _turfAreaLookup.Clear(); int oldAppearance = area.AppearanceId; area.AppearanceId = _appearanceSystem.AddAppearance(appearance); + _appearanceSystem.DecreaseAppearanceRefCount(oldAppearance); + _appearanceSystem.IncreaseAppearanceRefCount(area.AppearanceId); foreach (var turf in area.Contents.GetTurfs()) { - var turfAppearance = _atomManager.MustGetAppearance(turf); + IconAppearance? turfAppearance = new(_atomManager.MustGetAppearance(turf)); if(turfAppearance is null) continue; diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 1e03ffd853..41510c6f65 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -26,6 +26,7 @@ public override void Initialize() { //register empty appearance as ID 0 _appearanceToId.Add(IconAppearance.Default, 0); _idToAppearance.Add(0, IconAppearance.Default); + _appearanceRefCounts.Add(IconAppearance.Default, 1); _appearanceIdCounter = 1; _playerManager.PlayerStatusChanged += OnPlayerStatusChanged; } @@ -83,6 +84,8 @@ public void DecreaseAppearanceRefCount(IconAppearance appearance) { } if(_appearanceToId.TryGetValue(appearance, out int id)) { + if(id==0) //don't ever remove the default appearance + return; _idToAppearance.Remove(id); RaiseNetworkEvent(new RemoveAppearanceEvent(id)); } From be94ae07d7fbecf5db5fbce081281df06561f954 Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sun, 6 Oct 2024 16:59:51 +0100 Subject: [PATCH 18/54] immutable start --- OpenDreamRuntime/AtomManager.cs | 6 +- .../Rendering/ServerAppearanceSystem.cs | 72 +--- OpenDreamShared/Dream/IconAppearance.cs | 10 +- .../Dream/ImmutableIconAppearance.cs | 321 ++++++++++++++++++ .../Network/Messages/MsgAllAppearances.cs | 26 +- 5 files changed, 350 insertions(+), 85 deletions(-) create mode 100644 OpenDreamShared/Dream/ImmutableIconAppearance.cs diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index 271eeac195..3cad5f5592 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -468,14 +468,14 @@ public DreamValue GetAppearanceVar(IconAppearance appearance, string varName) { } /// - /// Gets an atom's appearance. + /// Gets an atom's appearance. Will throw if the appearance system is not available. /// /// The atom to find the appearance of. public IconAppearance? MustGetAppearance(DreamObject atom) { return atom switch { - DreamObjectTurf turf => AppearanceSystem?.MustGetAppearance(turf.AppearanceId), + DreamObjectTurf turf => AppearanceSystem!.MustGetAppearance(turf.AppearanceId), DreamObjectMovable movable => movable.SpriteComponent.Appearance, - DreamObjectArea area => AppearanceSystem.MustGetAppearance(area.AppearanceId), + DreamObjectArea area => AppearanceSystem!.MustGetAppearance(area.AppearanceId), DreamObjectImage image => image.Appearance, _ => throw new Exception($"Cannot get appearance of {atom}") }; diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 41510c6f65..7a0da2996f 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -9,9 +9,9 @@ namespace OpenDreamRuntime.Rendering; public sealed class ServerAppearanceSystem : SharedAppearanceSystem { - private readonly Dictionary _appearanceToId = new(); - private readonly Dictionary _idToAppearance = new(); - private readonly Dictionary _appearanceRefCounts = new(); + private readonly Dictionary _appearanceToId = new(); + private readonly Dictionary _idToAppearance = new(); + private readonly Dictionary _appearanceRefCounts = new(); private int _appearanceIdCounter; @@ -24,9 +24,9 @@ public sealed class ServerAppearanceSystem : SharedAppearanceSystem { public override void Initialize() { //register empty appearance as ID 0 - _appearanceToId.Add(IconAppearance.Default, 0); - _idToAppearance.Add(0, IconAppearance.Default); - _appearanceRefCounts.Add(IconAppearance.Default, 1); + _appearanceToId.Add(ImmutableIconAppearance.Default, 0); + _idToAppearance.Add(0, ImmutableIconAppearance.Default); + _appearanceRefCounts.Add(ImmutableIconAppearance.Default, 1); _appearanceIdCounter = 1; _playerManager.PlayerStatusChanged += OnPlayerStatusChanged; } @@ -45,67 +45,7 @@ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { } } - public void IncreaseAppearanceRefCount(IconAppearance appearance) { - lock (_lock) { - int count = _appearanceRefCounts.GetValueOrDefault(appearance, 0); - foreach(var overlayid in appearance.Overlays) { - IncreaseAppearanceRefCount(overlayid); - } - - foreach(var underlayid in appearance.Underlays) { - IncreaseAppearanceRefCount(underlayid); - } - - _appearanceRefCounts[appearance] = count + 1; - } - } - - public void IncreaseAppearanceRefCount(int appearanceId) { - if (!_idToAppearance.TryGetValue(appearanceId, out IconAppearance? appearance)) { - throw new InvalidOperationException("Trying to increase ref count of an appearance that doesn't exist."); - } - - IncreaseAppearanceRefCount(appearance); - } - - public void DecreaseAppearanceRefCount(IconAppearance appearance) { - lock (_lock) { - if (!_appearanceRefCounts.TryGetValue(appearance, out int count)) { - throw new InvalidOperationException($"Appearance {appearance.GetHashCode()} ref count is already 0. You might be trying to remove an appearance that was never added."); - } - - if (count == 1) { - foreach(var overlayid in appearance.Overlays) { - DecreaseAppearanceRefCount(overlayid); - } - - foreach(var underlayid in appearance.Underlays) { - DecreaseAppearanceRefCount(underlayid); - } - - if(_appearanceToId.TryGetValue(appearance, out int id)) { - if(id==0) //don't ever remove the default appearance - return; - _idToAppearance.Remove(id); - RaiseNetworkEvent(new RemoveAppearanceEvent(id)); - } - - _appearanceRefCounts.Remove(appearance); - _appearanceToId.Remove(appearance); - //let the GC sort out the rest - } else { - _appearanceRefCounts[appearance] = count - 1; - } - } - } - public void DecreaseAppearanceRefCount(int appearanceId) { - if (!_idToAppearance.TryGetValue(appearanceId, out IconAppearance? appearance)) { - throw new InvalidOperationException("Trying to decrease ref count of an appearance that doesn't exist."); - } - - DecreaseAppearanceRefCount(appearance); - } public int AddAppearance(IconAppearance appearance) { lock (_lock) { if (!_appearanceToId.TryGetValue(appearance, out int appearanceId)) { diff --git a/OpenDreamShared/Dream/IconAppearance.cs b/OpenDreamShared/Dream/IconAppearance.cs index 78e38af359..5d4d11e17b 100644 --- a/OpenDreamShared/Dream/IconAppearance.cs +++ b/OpenDreamShared/Dream/IconAppearance.cs @@ -12,6 +12,7 @@ namespace OpenDreamShared.Dream; * Woe, weary traveler, modifying this class is not for the faint of heart. * If you modify IconAppearance, be sure to update the following places: * - All of the methods on IconAppearance itself + * - ImmutableIconAppearance * - IconAppearance methods in AtomManager * - MsgAllAppearances * - IconDebugWindow @@ -19,8 +20,7 @@ namespace OpenDreamShared.Dream; */ // TODO: Wow this is huge! Probably look into splitting this by most used/least used to reduce the size of these -[Serializable, NetSerializable] -public sealed class IconAppearance : IEquatable { +public sealed class IconAppearance : IEquatable, IEquatable { public static readonly IconAppearance Default = new(); [ViewVariables] public string Name = string.Empty; @@ -113,7 +113,11 @@ public IconAppearance(IconAppearance appearance) { } } - public override bool Equals(object? obj) => obj is IconAppearance appearance && Equals(appearance); + public override bool Equals(object? obj) => (obj is IconAppearance appearance && Equals(appearance)) || (obj is ImmutableIconAppearance immutableIconAppearance && Equals(immutableIconAppearance)); + + public bool Equals(ImmutableIconAppearance? immutableIconAppearance) { + return immutableIconAppearance is null ? false : immutableIconAppearance.Equals(this); + } public bool Equals(IconAppearance? appearance) { if (appearance == null) return false; diff --git a/OpenDreamShared/Dream/ImmutableIconAppearance.cs b/OpenDreamShared/Dream/ImmutableIconAppearance.cs new file mode 100644 index 0000000000..41a5db3a87 --- /dev/null +++ b/OpenDreamShared/Dream/ImmutableIconAppearance.cs @@ -0,0 +1,321 @@ +using Robust.Shared.Maths; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Robust.Shared.GameObjects; + +namespace OpenDreamShared.Dream; + +/* + * Woe, weary traveler, modifying this class is not for the faint of heart. + * If you modify IconAppearance, be sure to update the following places: + * - All of the methods on IconAppearance itself + * - IconAppearance methods in AtomManager + * - MsgAllAppearances + * - IconDebugWindow + * - There may be others + */ + +// TODO: Wow this is huge! Probably look into splitting this by most used/least used to reduce the size of these +[Serializable, NetSerializable] +public sealed class ImmutableIconAppearance : IEquatable, IEquatable { + public static readonly ImmutableIconAppearance Default = new(); + + [ViewVariables] public readonly string Name = string.Empty; + [ViewVariables] public readonly int? Icon; + [ViewVariables] public readonly string? IconState; + [ViewVariables] public readonly AtomDirection Direction = AtomDirection.South; + [ViewVariables] public readonly bool InheritsDirection = true; // Inherits direction when used as an overlay + [ViewVariables] public readonly Vector2i PixelOffset; // pixel_x and pixel_y + [ViewVariables] public readonly Vector2i PixelOffset2; // pixel_w and pixel_z + [ViewVariables] public readonly Color Color = Color.White; + [ViewVariables] public readonly byte Alpha = 255; + [ViewVariables] public readonly float GlideSize; + [ViewVariables] public readonly float Layer = -1f; + [ViewVariables] public int Plane = -32767; + [ViewVariables] public readonly BlendMode BlendMode = BlendMode.Default; + [ViewVariables] public readonly AppearanceFlags AppearanceFlags = AppearanceFlags.None; + [ViewVariables] public readonly sbyte Invisibility; + [ViewVariables] public readonly bool Opacity; + [ViewVariables] public readonly bool Override; + [ViewVariables] public readonly string? RenderSource; + [ViewVariables] public readonly string? RenderTarget; + [ViewVariables] public readonly MouseOpacity MouseOpacity = MouseOpacity.PixelOpaque; + [ViewVariables] public readonly int[] Overlays; + [ViewVariables] public readonly int[] Underlays; + [ViewVariables] public readonly NetEntity[] VisContents; + [ViewVariables] public readonly DreamFilter[] Filters; + [ViewVariables] public readonly int[] Verbs; + + /// + /// An appearance can gain a color matrix filter by two possible forces:
+ /// 1. the /atom.color var is modified.
+ /// 2. the /atom.filters var gets a new filter of type "color".
+ /// DM crashes in some circumstances of this but we, as an extension :^), should try not to.
+ /// So, this exists as a way for the appearance to remember whether it's coloured by .color, specifically. + ///
+ /// + /// The reason we don't just take the slow path and always use this filter is not just for optimization,
+ /// it's also for parity! See for more. + ///
+ [ViewVariables] public readonly ColorMatrix ColorMatrix = ColorMatrix.Identity; + + /// The Transform property of this appearance, in [a,d,b,e,c,f] order + [ViewVariables] public readonly float[] Transform = [ + 1, 0, // a d + 0, 1, // b e + 0, 0 // c f + ]; + + // PixelOffset2 behaves the same as PixelOffset in top-down mode, so this is used + public Vector2i TotalPixelOffset => PixelOffset + PixelOffset2; + + private int? storedHashCode; + + public ImmutableIconAppearance() { + Overlays = []; + Underlays = []; + VisContents = []; + Filters = []; + Verbs = []; + } + + public ImmutableIconAppearance(IconAppearance appearance) { + Name = appearance.Name; + Icon = appearance.Icon; + IconState = appearance.IconState; + Direction = appearance.Direction; + InheritsDirection = appearance.InheritsDirection; + PixelOffset = appearance.PixelOffset; + PixelOffset2 = appearance.PixelOffset2; + Color = appearance.Color; + Alpha = appearance.Alpha; + GlideSize = appearance.GlideSize; + ColorMatrix = appearance.ColorMatrix; + Layer = appearance.Layer; + Plane = appearance.Plane; + RenderSource = appearance.RenderSource; + RenderTarget = appearance.RenderTarget; + BlendMode = appearance.BlendMode; + AppearanceFlags = appearance.AppearanceFlags; + Invisibility = appearance.Invisibility; + Opacity = appearance.Opacity; + MouseOpacity = appearance.MouseOpacity; + Overlays = appearance.Overlays.ToArray(); + Underlays = appearance.Underlays.ToArray(); + VisContents = appearance.VisContents.ToArray(); + Filters = appearance.Filters.ToArray(); + Verbs = appearance.Verbs.ToArray(); + Override = appearance.Override; + + for (int i = 0; i < 6; i++) { + Transform[i] = appearance.Transform[i]; + } + } + + public override bool Equals(object? obj) => (obj is IconAppearance appearance && Equals(appearance)) || (obj is ImmutableIconAppearance immutable && Equals(immutable)); + + public bool Equals(ImmutableIconAppearance? immutableIconAppearance) { + if (immutableIconAppearance == null) return false; + + if (immutableIconAppearance.Name != Name) return false; + if (immutableIconAppearance.Icon != Icon) return false; + if (immutableIconAppearance.IconState != IconState) return false; + if (immutableIconAppearance.Direction != Direction) return false; + if (immutableIconAppearance.InheritsDirection != InheritsDirection) return false; + if (immutableIconAppearance.PixelOffset != PixelOffset) return false; + if (immutableIconAppearance.PixelOffset2 != PixelOffset2) return false; + if (immutableIconAppearance.Color != Color) return false; + if (immutableIconAppearance.Alpha != Alpha) return false; + if (immutableIconAppearance.GlideSize != GlideSize) return false; + if (!immutableIconAppearance.ColorMatrix.Equals(ColorMatrix)) return false; + if (immutableIconAppearance.Layer != Layer) return false; + if (immutableIconAppearance.Plane != Plane) return false; + if (immutableIconAppearance.RenderSource != RenderSource) return false; + if (immutableIconAppearance.RenderTarget != RenderTarget) return false; + if (immutableIconAppearance.BlendMode != BlendMode) return false; + if (immutableIconAppearance.AppearanceFlags != AppearanceFlags) return false; + if (immutableIconAppearance.Invisibility != Invisibility) return false; + if (immutableIconAppearance.Opacity != Opacity) return false; + if (immutableIconAppearance.MouseOpacity != MouseOpacity) return false; + if (immutableIconAppearance.Overlays.Length != Overlays.Length) return false; + if (immutableIconAppearance.Underlays.Length != Underlays.Length) return false; + if (immutableIconAppearance.VisContents.Length != VisContents.Length) return false; + if (immutableIconAppearance.Filters.Length != Filters.Length) return false; + if (immutableIconAppearance.Verbs.Length != Verbs.Length) return false; + if (immutableIconAppearance.Override != Override) return false; + + for (int i = 0; i < Filters.Length; i++) { + if (immutableIconAppearance.Filters[i] != Filters[i]) return false; + } + + for (int i = 0; i < Overlays.Length; i++) { + if (immutableIconAppearance.Overlays[i] != Overlays[i]) return false; + } + + for (int i = 0; i < Underlays.Length; i++) { + if (immutableIconAppearance.Underlays[i] != Underlays[i]) return false; + } + + for (int i = 0; i < VisContents.Length; i++) { + if (immutableIconAppearance.VisContents[i] != VisContents[i]) return false; + } + + for (int i = 0; i < Verbs.Length; i++) { + if (immutableIconAppearance.Verbs[i] != Verbs[i]) return false; + } + + for (int i = 0; i < 6; i++) { + if (!immutableIconAppearance.Transform[i].Equals(Transform[i])) return false; + } + + return true; + } + + public bool Equals(IconAppearance? appearance) { + if (appearance == null) return false; + + if (appearance.Name != Name) return false; + if (appearance.Icon != Icon) return false; + if (appearance.IconState != IconState) return false; + if (appearance.Direction != Direction) return false; + if (appearance.InheritsDirection != InheritsDirection) return false; + if (appearance.PixelOffset != PixelOffset) return false; + if (appearance.PixelOffset2 != PixelOffset2) return false; + if (appearance.Color != Color) return false; + if (appearance.Alpha != Alpha) return false; + if (appearance.GlideSize != GlideSize) return false; + if (!appearance.ColorMatrix.Equals(ColorMatrix)) return false; + if (appearance.Layer != Layer) return false; + if (appearance.Plane != Plane) return false; + if (appearance.RenderSource != RenderSource) return false; + if (appearance.RenderTarget != RenderTarget) return false; + if (appearance.BlendMode != BlendMode) return false; + if (appearance.AppearanceFlags != AppearanceFlags) return false; + if (appearance.Invisibility != Invisibility) return false; + if (appearance.Opacity != Opacity) return false; + if (appearance.MouseOpacity != MouseOpacity) return false; + if (appearance.Overlays.Count != Overlays.Length) return false; + if (appearance.Underlays.Count != Underlays.Length) return false; + if (appearance.VisContents.Count != VisContents.Length) return false; + if (appearance.Filters.Count != Filters.Length) return false; + if (appearance.Verbs.Count != Verbs.Length) return false; + if (appearance.Override != Override) return false; + + for (int i = 0; i < Filters.Length; i++) { + if (appearance.Filters[i] != Filters[i]) return false; + } + + for (int i = 0; i < Overlays.Length; i++) { + if (appearance.Overlays[i] != Overlays[i]) return false; + } + + for (int i = 0; i < Underlays.Length; i++) { + if (appearance.Underlays[i] != Underlays[i]) return false; + } + + for (int i = 0; i < VisContents.Length; i++) { + if (appearance.VisContents[i] != VisContents[i]) return false; + } + + for (int i = 0; i < Verbs.Length; i++) { + if (appearance.Verbs[i] != Verbs[i]) return false; + } + + for (int i = 0; i < 6; i++) { + if (!appearance.Transform[i].Equals(Transform[i])) return false; + } + + return true; + } + + /// + /// This is a helper used for both optimization and parity.
+ /// In BYOND, if a color matrix is representable as an RGBA color string,
+ /// then it is coerced into one internally before being saved onto some appearance.
+ /// This does the linear algebra madness necessary to determine whether this is the case or not. + ///
+ private static bool TryRepresentMatrixAsRgbaColor(in ColorMatrix matrix, [NotNullWhen(true)] out Color? maybeColor) { + maybeColor = null; + + // The R G B A values need to be bounded [0,1] for a color conversion to work; + // anything higher implies trying to render "superblue" or something. + float diagonalSum = 0f; + foreach (float diagonalValue in matrix.GetDiagonal()) { + if (diagonalValue < 0 || diagonalValue > 1) + return false; + diagonalSum += diagonalValue; + } + + // and then all of the other values need to be zero, including the offset vector. + float sum = 0f; + foreach (float value in matrix.GetValues()) { + if (value < 0f) // To avoid situations like negatives and positives cancelling out this checksum. + return false; + sum += value; + } + + if (sum - diagonalSum == 0) // PREEETTY sure I can trust the floating-point math here. Not 100% though + maybeColor = new Color(matrix.c11, matrix.c22, matrix.c33, matrix.c44); + return maybeColor is not null; + } + + public override int GetHashCode() { + if(storedHashCode is not null) //because everything is readonly, this only needs to be done once + return (int)storedHashCode; + + HashCode hashCode = new HashCode(); + + hashCode.Add(Name); + hashCode.Add(Icon); + hashCode.Add(IconState); + hashCode.Add(Direction); + hashCode.Add(InheritsDirection); + hashCode.Add(PixelOffset); + hashCode.Add(PixelOffset2); + hashCode.Add(Color); + hashCode.Add(ColorMatrix); + hashCode.Add(Layer); + hashCode.Add(Invisibility); + hashCode.Add(Opacity); + hashCode.Add(MouseOpacity); + hashCode.Add(Alpha); + hashCode.Add(GlideSize); + hashCode.Add(Plane); + hashCode.Add(RenderSource); + hashCode.Add(RenderTarget); + hashCode.Add(BlendMode); + hashCode.Add(AppearanceFlags); + + foreach (int overlay in Overlays) { + hashCode.Add(overlay); + } + + foreach (int underlay in Underlays) { + hashCode.Add(underlay); + } + + foreach (int visContent in VisContents) { + hashCode.Add(visContent); + } + + foreach (DreamFilter filter in Filters) { + hashCode.Add(filter); + } + + foreach (int verb in Verbs) { + hashCode.Add(verb); + } + + for (int i = 0; i < 6; i++) { + hashCode.Add(Transform[i]); + } + + storedHashCode = hashCode.ToHashCode(); + return (int)storedHashCode; + } + +} + diff --git a/OpenDreamShared/Network/Messages/MsgAllAppearances.cs b/OpenDreamShared/Network/Messages/MsgAllAppearances.cs index 4377d00aa6..311276934c 100644 --- a/OpenDreamShared/Network/Messages/MsgAllAppearances.cs +++ b/OpenDreamShared/Network/Messages/MsgAllAppearances.cs @@ -9,7 +9,7 @@ namespace OpenDreamShared.Network.Messages; -public sealed class MsgAllAppearances(Dictionary allAppearances) : NetMessage { +public sealed class MsgAllAppearances(Dictionary allAppearances) : NetMessage { public override MsgGroups MsgGroup => MsgGroups.EntityEvent; private enum Property : byte { @@ -45,7 +45,7 @@ private enum Property : byte { End } - public Dictionary AllAppearances = allAppearances; + public Dictionary AllAppearances = allAppearances; public MsgAllAppearances() : this(new()) { } @@ -207,7 +207,7 @@ public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer property = (Property)buffer.ReadByte(); } - AllAppearances.Add(appearanceId, appearance); + AllAppearances.Add(appearanceId, new(appearance)); } } @@ -333,37 +333,37 @@ public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer buffer.Write((byte)appearance.MouseOpacity); } - if (appearance.Overlays.Count != 0) { + if (appearance.Overlays.Length != 0) { buffer.Write((byte)Property.Overlays); - buffer.WriteVariableInt32(appearance.Overlays.Count); + buffer.WriteVariableInt32(appearance.Overlays.Length); foreach (var overlay in appearance.Overlays) { buffer.WriteVariableInt32(overlay); } } - if (appearance.Underlays.Count != 0) { + if (appearance.Underlays.Length != 0) { buffer.Write((byte)Property.Underlays); - buffer.WriteVariableInt32(appearance.Underlays.Count); + buffer.WriteVariableInt32(appearance.Underlays.Length); foreach (var underlay in appearance.Underlays) { buffer.WriteVariableInt32(underlay); } } - if (appearance.VisContents.Count != 0) { + if (appearance.VisContents.Length != 0) { buffer.Write((byte)Property.VisContents); - buffer.WriteVariableInt32(appearance.VisContents.Count); + buffer.WriteVariableInt32(appearance.VisContents.Length); foreach (var item in appearance.VisContents) { buffer.Write(item); } } - if (appearance.Filters.Count != 0) { + if (appearance.Filters.Length != 0) { buffer.Write((byte)Property.Filters); - buffer.Write(appearance.Filters.Count); + buffer.Write(appearance.Filters.Length); foreach (var filter in appearance.Filters) { using var filterStream = new MemoryStream(); @@ -374,10 +374,10 @@ public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer } } - if (appearance.Verbs.Count != 0) { + if (appearance.Verbs.Length != 0) { buffer.Write((byte)Property.Verbs); - buffer.WriteVariableInt32(appearance.Verbs.Count); + buffer.WriteVariableInt32(appearance.Verbs.Length); foreach (var verb in appearance.Verbs) { buffer.WriteVariableInt32(verb); } From 45a66c6c1ed58c2697b840684d121bf0a9e7cd17 Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sun, 6 Oct 2024 21:12:55 +0100 Subject: [PATCH 19/54] nobody keeps iconappearances anymore, only ids --- OpenDreamRuntime/AtomManager.cs | 16 ++++---- .../Objects/Types/DreamObjectImage.cs | 7 ++-- .../Rendering/DMISpriteComponent.cs | 2 +- OpenDreamRuntime/Rendering/DMISpriteSystem.cs | 13 +------ .../Rendering/ServerAppearanceSystem.cs | 20 ++++------ .../Dream/ImmutableIconAppearance.cs | 37 +++++++++++++++++++ .../Rendering/SharedAppearanceSystem.cs | 4 +- 7 files changed, 60 insertions(+), 39 deletions(-) diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index 3cad5f5592..f0e4dd0c31 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -471,12 +471,12 @@ public DreamValue GetAppearanceVar(IconAppearance appearance, string varName) { /// Gets an atom's appearance. Will throw if the appearance system is not available. ///
/// The atom to find the appearance of. - public IconAppearance? MustGetAppearance(DreamObject atom) { + public ImmutableIconAppearance? MustGetAppearance(DreamObject atom) { return atom switch { DreamObjectTurf turf => AppearanceSystem!.MustGetAppearance(turf.AppearanceId), - DreamObjectMovable movable => movable.SpriteComponent.Appearance, + DreamObjectMovable movable => AppearanceSystem!.MustGetAppearance(movable.SpriteComponent.AppearanceId.Value), DreamObjectArea area => AppearanceSystem!.MustGetAppearance(area.AppearanceId), - DreamObjectImage image => image.Appearance, + DreamObjectImage image => AppearanceSystem!.MustGetAppearance(image.SpriteComponent.AppearanceId.Value), _ => throw new Exception($"Cannot get appearance of {atom}") }; } @@ -484,15 +484,15 @@ public DreamValue GetAppearanceVar(IconAppearance appearance, string varName) { /// /// Optionally looks up for an appearance. Does not try to create a new one when one is not found for this atom. /// - public bool TryGetAppearance(DreamObject atom, [NotNullWhen(true)] out IconAppearance? appearance) { + public bool TryGetAppearance(DreamObject atom, [NotNullWhen(true)] out ImmutableIconAppearance? appearance) { if (atom is DreamObjectTurf turf) appearance = AppearanceSystem?.MustGetAppearance(turf.AppearanceId); else if (atom is DreamObjectMovable movable) - appearance = movable.SpriteComponent.Appearance; + appearance = AppearanceSystem?.MustGetAppearance(movable.SpriteComponent.AppearanceId.Value); else if (atom is DreamObjectImage image) - appearance = image.Appearance; + appearance = AppearanceSystem?.MustGetAppearance(image.SpriteComponent.AppearanceId.Value); else if (atom is DreamObjectArea area) - appearance = AppearanceSystem.MustGetAppearance(area.AppearanceId); + appearance = AppearanceSystem?.MustGetAppearance(area.AppearanceId); else appearance = null; @@ -512,7 +512,7 @@ public void SetAtomAppearance(DreamObject atom, IconAppearance appearance) { } else if (atom is DreamObjectMovable movable) { DMISpriteSystem.SetSpriteAppearance(new(movable.Entity, movable.SpriteComponent), appearance); } else if (atom is DreamObjectImage image) { - image.Appearance = appearance; + DMISpriteSystem.SetSpriteAppearance(new(image.Entity, image.SpriteComponent), appearance); } else if (atom is DreamObjectArea area) { _dreamMapManager.SetAreaAppearance(area, appearance); } diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index b0c0ca3c14..9acf7f0c86 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -6,9 +6,8 @@ namespace OpenDreamRuntime.Objects.Types; public sealed class DreamObjectImage : DreamObject { - private IconAppearance? _appearance; - public IconAppearance? Appearance { get => _appearance; set => SetAppearance(value); } - + public EntityUid Entity; + public readonly DMISpriteComponent SpriteComponent; private DreamObject? _loc; private DreamList _overlays; private DreamList _underlays; @@ -272,7 +271,7 @@ protected override void HandleDeletion(bool possiblyThreaded) { if(_appearance is not null) AppearanceSystem!.DecreaseAppearanceRefCount(_appearance); - + if(_entity != EntityUid.Invalid) { EntityManager.DeleteEntity(_entity); } diff --git a/OpenDreamRuntime/Rendering/DMISpriteComponent.cs b/OpenDreamRuntime/Rendering/DMISpriteComponent.cs index 2a774470bb..81c08be0b6 100644 --- a/OpenDreamRuntime/Rendering/DMISpriteComponent.cs +++ b/OpenDreamRuntime/Rendering/DMISpriteComponent.cs @@ -10,6 +10,6 @@ public sealed partial class DMISpriteComponent : SharedDMISpriteComponent { public ScreenLocation ScreenLocation; [Access(typeof(DMISpriteSystem))] - [ViewVariables] public IconAppearance? Appearance; + [ViewVariables] public int? AppearanceId; } diff --git a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs index 0c561b4c77..33b03eaaf2 100644 --- a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs +++ b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs @@ -14,21 +14,12 @@ public override void Initialize() { } private void GetComponentState(EntityUid uid, DMISpriteComponent component, ref ComponentGetState args) { - int? appearanceId = null; - if (component.Appearance != null) { - appearanceId = _appearance?.AddAppearance(component.Appearance); - } - - args.State = new SharedDMISpriteComponent.DMISpriteComponentState(appearanceId, component.ScreenLocation); + args.State = new SharedDMISpriteComponent.DMISpriteComponentState(component.AppearanceId, component.ScreenLocation); } public void SetSpriteAppearance(Entity ent, IconAppearance appearance, bool dirty = true) { DMISpriteComponent component = ent.Comp; - _appearance?.IncreaseAppearanceRefCount(appearance); - if(component.Appearance is not null) - _appearance?.DecreaseAppearanceRefCount(component.Appearance); - - component.Appearance = appearance; + component.AppearanceId = _appearance?.AddAppearance(appearance); if(dirty) Dirty(ent, component); } diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 7a0da2996f..40a7b62474 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -45,37 +45,31 @@ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { } } - public int AddAppearance(IconAppearance appearance) { + ImmutableIconAppearance immutableAppearance = new(appearance); lock (_lock) { - if (!_appearanceToId.TryGetValue(appearance, out int appearanceId)) { + if (!_appearanceToId.TryGetValue(immutableAppearance, out int appearanceId)) { appearanceId = _appearanceIdCounter++; - _appearanceToId.Add(appearance, appearanceId); - _idToAppearance.Add(appearanceId, appearance); - RaiseNetworkEvent(new NewAppearanceEvent(appearanceId, appearance)); + _appearanceToId.Add(immutableAppearance, appearanceId); + _idToAppearance.Add(appearanceId, immutableAppearance); + RaiseNetworkEvent(new NewAppearanceEvent(appearanceId, immutableAppearance)); } return appearanceId; } } - public IconAppearance MustGetAppearance(int appearanceId) { + public ImmutableIconAppearance MustGetAppearance(int appearanceId) { lock (_lock) { return _idToAppearance[appearanceId]; } } - public bool TryGetAppearance(int appearanceId, [NotNullWhen(true)] out IconAppearance? appearance) { + public bool TryGetAppearance(int appearanceId, [NotNullWhen(true)] out ImmutableIconAppearance? appearance) { lock (_lock) { return _idToAppearance.TryGetValue(appearanceId, out appearance); } } - public bool TryGetAppearanceId(IconAppearance appearance, out int appearanceId) { - lock (_lock) { - return _appearanceToId.TryGetValue(appearance, out appearanceId); - } - } - public void Animate(NetEntity entity, IconAppearance targetAppearance, TimeSpan duration, AnimationEasing easing, int loop, AnimationFlags flags, int delay, bool chainAnim) { int appearanceId = AddAppearance(targetAppearance); diff --git a/OpenDreamShared/Dream/ImmutableIconAppearance.cs b/OpenDreamShared/Dream/ImmutableIconAppearance.cs index 41a5db3a87..a5ba9a41bb 100644 --- a/OpenDreamShared/Dream/ImmutableIconAppearance.cs +++ b/OpenDreamShared/Dream/ImmutableIconAppearance.cs @@ -317,5 +317,42 @@ public override int GetHashCode() { return (int)storedHashCode; } + //Creates an editable *copy* of this appearance, which must be added to the ServerAppearanceSystem to be used. + public IconAppearance ToMutable() { + IconAppearance result = new IconAppearance() { + Name = this.Name, + Icon = this.Icon, + IconState = this.IconState, + Direction = this.Direction, + InheritsDirection = this.InheritsDirection, + PixelOffset = this.PixelOffset, + PixelOffset2 = this.PixelOffset2, + Color = this.Color, + Alpha = this.Alpha, + GlideSize = this.GlideSize, + ColorMatrix = this.ColorMatrix, + Layer = this.Layer, + Plane = this.Plane, + RenderSource = this.RenderSource, + RenderTarget = this.RenderTarget, + BlendMode = this.BlendMode, + AppearanceFlags = this.AppearanceFlags, + Invisibility = this.Invisibility, + Opacity = this.Opacity, + MouseOpacity = this.MouseOpacity, + Overlays = new(this.Overlays), + Underlays = new(this.Underlays), + VisContents = new(this.VisContents), + Filters = new(this.Filters), + Verbs = new(this.Verbs), + Override = this.Override, + }; + + for (int i = 0; i < 6; i++) { + result.Transform[i] = Transform[i]; + } + return result; + } + } diff --git a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs index d7e95b465e..5eb6daf26a 100644 --- a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs +++ b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs @@ -7,9 +7,9 @@ namespace OpenDreamShared.Rendering; public abstract class SharedAppearanceSystem : EntitySystem { [Serializable, NetSerializable] - public sealed class NewAppearanceEvent(int appearanceId, IconAppearance appearance) : EntityEventArgs { + public sealed class NewAppearanceEvent(int appearanceId, ImmutableIconAppearance appearance) : EntityEventArgs { public int AppearanceId { get; } = appearanceId; - public IconAppearance Appearance { get; } = appearance; + public ImmutableIconAppearance Appearance { get; } = appearance; } [Serializable, NetSerializable] From ab2ecfc81dd8a222350cea0b99a58766ac506f1b Mon Sep 17 00:00:00 2001 From: amylizzle Date: Mon, 7 Oct 2024 11:44:34 +0100 Subject: [PATCH 20/54] moreeee --- OpenDreamRuntime/AtomManager.cs | 31 +++++++++++++++++++---------- OpenDreamRuntime/DreamManager.cs | 5 ++--- OpenDreamRuntime/DreamMapManager.cs | 4 ++-- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index f0e4dd0c31..6c170e8b8d 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -500,8 +500,8 @@ public bool TryGetAppearance(DreamObject atom, [NotNullWhen(true)] out Immutable } public void UpdateAppearance(DreamObject atom, Action update) { - var appearance = MustGetAppearance(atom); - appearance = (appearance != null) ? new(appearance) : new(); // Clone the appearance + ImmutableIconAppearance immutableAppearance = MustGetAppearance(atom); + IconAppearance appearance = (immutableAppearance != null) ? immutableAppearance.ToMutable() : new(); // Clone the appearance update(appearance); SetAtomAppearance(atom, appearance); } @@ -527,17 +527,27 @@ public void SetSpriteAppearance(Entity ent, IconAppearance a } public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasing easing, int loop, AnimationFlags flags, int delay, bool chainAnim, Action animate) { - if (atom is not DreamObjectMovable movable) - return; //Animating non-movables is unimplemented TODO: should handle images and maybe filters - - IconAppearance appearance = new IconAppearance(movable.SpriteComponent.Appearance); + //TODO: should handle filters + IconAppearance appearance; + EntityUid targetEntity; + DMISpriteComponent targetComponent; + if (atom is DreamObjectMovable movable) { + targetEntity = movable.Entity; + targetComponent = movable.SpriteComponent; + appearance = AppearanceSystem!.MustGetAppearance(targetComponent.AppearanceId!.Value).ToMutable(); + } else if (atom is DreamObjectImage image){ + targetEntity = image.Entity; + targetComponent = image.SpriteComponent; + appearance = AppearanceSystem!.MustGetAppearance(targetComponent.AppearanceId!.Value).ToMutable(); + } else + throw new ArgumentException($"Cannot animate appearance of {atom}"); animate(appearance); // Don't send the updated appearance to clients, they will animate it - DMISpriteSystem.SetSpriteAppearance(new(movable.Entity, movable.SpriteComponent), appearance, dirty: false); + DMISpriteSystem.SetSpriteAppearance(new(targetEntity, targetComponent), appearance, dirty: false); - NetEntity ent = _entityManager.GetNetEntity(movable.Entity); + NetEntity ent = _entityManager.GetNetEntity(targetEntity); AppearanceSystem?.Animate(ent, appearance, duration, easing, loop, flags, delay, chainAnim); } @@ -549,7 +559,7 @@ public bool TryCreateAppearanceFrom(DreamValue value, [NotNullWhen(true)] out Ic } if (value.TryGetValueAsDreamObject(out var copyFromImage)) { - appearance = new(copyFromImage.Appearance!); + appearance = MustGetAppearance(copyFromImage)!.ToMutable(); return true; } @@ -559,7 +569,7 @@ public bool TryCreateAppearanceFrom(DreamValue value, [NotNullWhen(true)] out Ic } if (value.TryGetValueAsDreamObject(out var copyFromAtom)) { - appearance = new(MustGetAppearance(copyFromAtom)); + appearance = MustGetAppearance(copyFromAtom)?.ToMutable(); return true; } @@ -631,7 +641,6 @@ public IconAppearance GetAppearanceFromDefinition(DreamObjectDefinition def) { } _definitionAppearanceCache.Add(def, appearance); - AppearanceSystem?.IncreaseAppearanceRefCount(appearance); return appearance; } diff --git a/OpenDreamRuntime/DreamManager.cs b/OpenDreamRuntime/DreamManager.cs index 4b33eb2faa..179d38118c 100644 --- a/OpenDreamRuntime/DreamManager.cs +++ b/OpenDreamRuntime/DreamManager.cs @@ -243,7 +243,6 @@ public string CreateRef(DreamValue value) { refType = RefType.DreamAppearance; _appearanceSystem ??= _entitySystemManager.GetEntitySystem(); idx = (int)_appearanceSystem.AddAppearance(appearance); - _appearanceSystem.IncreaseAppearanceRefCount(appearance); } else if (value.TryGetValueAsDreamResource(out var refRsc)) { refType = RefType.DreamResource; idx = refRsc.Id; @@ -325,8 +324,8 @@ public DreamValue LocateRef(string refString) { return new DreamValue(resource); case RefType.DreamAppearance: _appearanceSystem ??= _entitySystemManager.GetEntitySystem(); - return _appearanceSystem.TryGetAppearance(refId, out IconAppearance? appearance) - ? new DreamValue(appearance) + return _appearanceSystem.TryGetAppearance(refId, out ImmutableIconAppearance? appearance) + ? new DreamValue(appearance.ToMutable()) : DreamValue.Null; case RefType.Proc: return new(_objectTree.Procs[refId]); diff --git a/OpenDreamRuntime/DreamMapManager.cs b/OpenDreamRuntime/DreamMapManager.cs index dc20b6e526..6bfb880626 100644 --- a/OpenDreamRuntime/DreamMapManager.cs +++ b/OpenDreamRuntime/DreamMapManager.cs @@ -216,8 +216,8 @@ public void SetAreaAppearance(DreamObjectArea area, IconAppearance appearance) { _turfAreaLookup.Clear(); int oldAppearance = area.AppearanceId; area.AppearanceId = _appearanceSystem.AddAppearance(appearance); - _appearanceSystem.DecreaseAppearanceRefCount(oldAppearance); - _appearanceSystem.IncreaseAppearanceRefCount(area.AppearanceId); + //_appearanceSystem.DecreaseAppearanceRefCount(oldAppearance); + //_appearanceSystem.IncreaseAppearanceRefCount(area.AppearanceId); foreach (var turf in area.Contents.GetTurfs()) { IconAppearance? turfAppearance = new(_atomManager.MustGetAppearance(turf)); From faa731d4def0dc3c92921fd1d909e8c76b7936c9 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Mon, 7 Oct 2024 12:21:06 +0100 Subject: [PATCH 21/54] faster area appearance editing --- OpenDreamRuntime/DreamMapManager.cs | 30 ++++++++++++++----- OpenDreamRuntime/Objects/Types/DreamList.cs | 10 +++---- .../Objects/Types/DreamObjectAtom.cs | 2 +- .../Objects/Types/DreamObjectTurf.cs | 5 ---- 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/OpenDreamRuntime/DreamMapManager.cs b/OpenDreamRuntime/DreamMapManager.cs index 6bfb880626..b63f84d2a4 100644 --- a/OpenDreamRuntime/DreamMapManager.cs +++ b/OpenDreamRuntime/DreamMapManager.cs @@ -12,6 +12,7 @@ using Robust.Shared.Utility; using Level = OpenDreamRuntime.IDreamMapManager.Level; using Cell = OpenDreamRuntime.IDreamMapManager.Cell; +using System.Collections; namespace OpenDreamRuntime; @@ -216,15 +217,30 @@ public void SetAreaAppearance(DreamObjectArea area, IconAppearance appearance) { _turfAreaLookup.Clear(); int oldAppearance = area.AppearanceId; area.AppearanceId = _appearanceSystem.AddAppearance(appearance); - //_appearanceSystem.DecreaseAppearanceRefCount(oldAppearance); - //_appearanceSystem.IncreaseAppearanceRefCount(area.AppearanceId); - foreach (var turf in area.Contents.GetTurfs()) { - IconAppearance? turfAppearance = new(_atomManager.MustGetAppearance(turf)); - if(turfAppearance is null) continue; + //get all unique turf appearances + //create the new version of each of those appearances + //for each turf, update the appropriate ID + + Dictionary oldToNewAppearanceID = new(); + foreach (var turf in area.Contents.GetTurfs()) { + if(oldToNewAppearanceID.TryGetValue(turf.AppearanceId, out int newID)) + turf.AppearanceId = newID; + else { + IconAppearance? turfAppearance = _atomManager.MustGetAppearance(turf)?.ToMutable(); + + if(turfAppearance is null) continue; + + turfAppearance.Overlays.Remove(oldAppearance); + turfAppearance.Overlays.Add(area.AppearanceId); + newID = _appearanceSystem.AddAppearance(appearance); + oldToNewAppearanceID.Add(turf.AppearanceId, newID); + turf.AppearanceId = newID; + } - turfAppearance.Overlays.Remove(oldAppearance); - SetTurfAppearance(turf, turfAppearance); + var level = _levels[turf.Z - 1]; + int turfId = (newID + 1); // +1 because 0 is used for empty turfs + level.QueuedTileUpdates[(turf.X, turf.Y)] = new Tile(turfId); } } diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index ad403b651f..6209c6aa40 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -579,7 +579,7 @@ public override DreamValue GetValue(DreamValue key) { throw new Exception($"Invalid index into verbs list: {key}"); var verbs = GetVerbs(); - if (index < 1 || index > verbs.Count) + if (index < 1 || index > verbs.Length) throw new Exception($"Out of bounds index on verbs list: {index}"); return new DreamValue(verbSystem.GetVerb(verbs[index - 1])); @@ -590,7 +590,7 @@ public override List GetValues() { if (appearance == null || verbSystem == null) return new List(); - var values = new List(appearance.Verbs.Count); + var values = new List(appearance.Verbs.Length); foreach (var verbId in appearance.Verbs) { var verb = verbSystem.GetVerb(verbId); @@ -629,11 +629,11 @@ public override void Cut(int start = 1, int end = 0) { } public override int GetLength() { - return GetVerbs().Count; + return GetVerbs().Length; } - private List GetVerbs() { - IconAppearance? appearance = atomManager.MustGetAppearance(atom); + private int[] GetVerbs() { + var appearance = atomManager.MustGetAppearance(atom); if (appearance == null) throw new Exception("Atom has no appearance"); diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs b/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs index df8754eb48..1501b3eb4d 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs @@ -58,7 +58,7 @@ protected override bool TryGetVar(string varName, out DreamValue value) { value = (Desc != null) ? new(Desc) : DreamValue.Null; return true; case "appearance": - var appearanceCopy = new IconAppearance(AtomManager.MustGetAppearance(this)!); + var appearanceCopy = AtomManager.MustGetAppearance(this)!.ToMutable(); value = new(appearanceCopy); return true; diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs index 33e1a1079f..4cab00e3b3 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs @@ -67,11 +67,6 @@ protected override void SetVar(string varName, DreamValue value) { } public void SetAppearanceId(int appearanceId) { - AppearanceSystem!.IncreaseAppearanceRefCount(appearanceId); - if (_appearanceId is not null) { - AppearanceSystem!.DecreaseAppearanceRefCount(_appearanceId.Value); - } - _appearanceId = appearanceId; } } From d414565cf04dc9ebdfe68eacee1d67030292b8bc Mon Sep 17 00:00:00 2001 From: amylizzle Date: Mon, 7 Oct 2024 12:53:47 +0100 Subject: [PATCH 22/54] bits --- OpenDreamRuntime/Objects/Types/DreamObjectImage.cs | 10 +--------- OpenDreamRuntime/ServerVerbSystem.cs | 4 ++-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index 9acf7f0c86..1a40c964d2 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -6,13 +6,12 @@ namespace OpenDreamRuntime.Objects.Types; public sealed class DreamObjectImage : DreamObject { - public EntityUid Entity; + public EntityUid Entity = EntityUid.Invalid; public readonly DMISpriteComponent SpriteComponent; private DreamObject? _loc; private DreamList _overlays; private DreamList _underlays; private readonly DreamList _filters; - private EntityUid _entity = EntityUid.Invalid; /// /// All the args in /image/New() after "icon" and "loc", in their correct order @@ -255,10 +254,6 @@ public EntityUid GetEntity() { } public void SetAppearance(IconAppearance? appearance) { - if(appearance is not null) - AppearanceSystem!.IncreaseAppearanceRefCount(appearance); - if(_appearance is not null) - AppearanceSystem!.DecreaseAppearanceRefCount(_appearance); _appearance = appearance; } @@ -269,9 +264,6 @@ protected override void HandleDeletion(bool possiblyThreaded) { return; } - if(_appearance is not null) - AppearanceSystem!.DecreaseAppearanceRefCount(_appearance); - if(_entity != EntityUid.Invalid) { EntityManager.DeleteEntity(_entity); } diff --git a/OpenDreamRuntime/ServerVerbSystem.cs b/OpenDreamRuntime/ServerVerbSystem.cs index a618d35ba1..6da5de48e6 100644 --- a/OpenDreamRuntime/ServerVerbSystem.cs +++ b/OpenDreamRuntime/ServerVerbSystem.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.Linq; using DMCompiler.DM; using OpenDreamRuntime.Objects; using OpenDreamRuntime.Objects.Types; @@ -180,8 +181,7 @@ private bool CanExecute(DreamConnection connection, DreamObject src, DreamProc v return true; } else if (src is DreamObjectAtom atom) { var appearance = _atomManager.MustGetAppearance(atom); - - if (appearance?.Verbs.Contains(verb.VerbId.Value) is not true) // Inside atom.verbs? + if (appearance.Verbs.Contains(verb.VerbId.Value) is not true) // Inside atom.verbs? return false; } From 294e84b59a271b060dfa4aca1bf7aed36faad4a8 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Mon, 7 Oct 2024 14:03:49 +0100 Subject: [PATCH 23/54] most of lists --- OpenDreamRuntime/AtomManager.cs | 4 +- OpenDreamRuntime/Objects/Types/DreamList.cs | 56 +++++++++++-------- .../Objects/Types/DreamObjectAtom.cs | 4 +- 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index 6c170e8b8d..3d0a65574d 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -385,7 +385,7 @@ public void SetAppearanceVar(IconAppearance appearance, string varName, DreamVal } } - public DreamValue GetAppearanceVar(IconAppearance appearance, string varName) { + public DreamValue GetAppearanceVar(ImmutableIconAppearance appearance, string varName) { switch (varName) { case "name": return new(appearance.Name); @@ -458,7 +458,7 @@ public DreamValue GetAppearanceVar(IconAppearance appearance, string varName) { return new(matrix); case "appearance": - IconAppearance appearanceCopy = new IconAppearance(appearance); // Return a copy + IconAppearance appearanceCopy = appearance.ToMutable(); // Return a copy return new(appearanceCopy); // TODO: overlays, underlays, filters // Those are handled separately by whatever is calling GetAppearanceVar currently diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index 6209c6aa40..989b395574 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -663,13 +663,13 @@ public override List GetValues() { if (appearance == null || _appearanceSystem == null) return new List(); - var overlays = GetOverlaysList(appearance); - var values = new List(overlays.Count); + var overlays = GetOverlaysArray(appearance); + var values = new List(overlays.Length); foreach (var overlay in overlays) { var overlayAppearance = _appearanceSystem.MustGetAppearance(overlay); - values.Add(new(overlayAppearance)); + values.Add(new(overlayAppearance.ToMutable())); } return values; @@ -689,17 +689,17 @@ public override DreamValue GetValue(DreamValue key) { if (!key.TryGetValueAsInteger(out var overlayIndex) || overlayIndex < 1) throw new Exception($"Invalid index into {(_isUnderlays ? "underlays" : "overlays")} list: {key}"); - IconAppearance appearance = GetAppearance(); - List overlaysList = GetOverlaysList(appearance); - if (overlayIndex > overlaysList.Count) - throw new Exception($"Atom only has {overlaysList.Count} {(_isUnderlays ? "underlay" : "overlay")}(s), cannot index {overlayIndex}"); + ImmutableIconAppearance appearance = GetAppearance(); + var overlaysList = GetOverlaysArray(appearance); + if (overlayIndex > overlaysList.Length) + throw new Exception($"Atom only has {overlaysList.Length} {(_isUnderlays ? "underlay" : "overlay")}(s), cannot index {overlayIndex}"); if (_appearanceSystem == null) return DreamValue.Null; - int overlayId = GetOverlaysList(appearance)[overlayIndex - 1]; - IconAppearance overlayAppearance = _appearanceSystem.MustGetAppearance(overlayId); - return new DreamValue(overlayAppearance); + int overlayId = overlaysList[overlayIndex - 1]; + ImmutableIconAppearance overlayAppearance = _appearanceSystem.MustGetAppearance(overlayId); + return new DreamValue(overlayAppearance.ToMutable()); } public override void SetValue(DreamValue key, DreamValue value, bool allowGrowth = false) { @@ -732,15 +732,18 @@ public override void RemoveValue(DreamValue value) { } public override int GetLength() { - return GetOverlaysList(GetAppearance()).Count; + return GetOverlaysArray(GetAppearance()).Length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private List GetOverlaysList(IconAppearance appearance) => _isUnderlays ? appearance.Underlays : appearance.Overlays; - private IconAppearance GetAppearance() { - IconAppearance? appearance = _atomManager.MustGetAppearance(_owner); + private int[] GetOverlaysArray(ImmutableIconAppearance appearance) => + _isUnderlays ? appearance.Underlays : appearance.Overlays; + + private ImmutableIconAppearance GetAppearance() { + var appearance = _atomManager.MustGetAppearance(_owner); if (appearance == null) throw new Exception("Atom has no appearance"); @@ -881,9 +884,14 @@ public override void Cut(int start = 1, int end = 0) { } public int GetIndexOfFilter(DreamFilter filter) { - IconAppearance appearance = GetAppearance(); - - return appearance.Filters.IndexOf(filter) + 1; + ImmutableIconAppearance appearance = GetAppearance(); + int i = 0; + while(i < appearance.Filters.Length) { + if(appearance.Filters[i] == filter) + return i; + i++; + } + return -1; } public void SetFilter(int index, DreamFilter? filter) { @@ -908,9 +916,9 @@ public override DreamValue GetValue(DreamValue key) { if (!key.TryGetValueAsInteger(out var filterIndex) || filterIndex < 1) throw new Exception($"Invalid index into filter list: {key}"); - IconAppearance appearance = GetAppearance(); - if (filterIndex > appearance.Filters.Count) - throw new Exception($"Atom only has {appearance.Filters.Count} filter(s), cannot index {filterIndex}"); + ImmutableIconAppearance appearance = GetAppearance(); + if (filterIndex > appearance.Filters.Length) + throw new Exception($"Atom only has {appearance.Filters.Length} filter(s), cannot index {filterIndex}"); DreamFilter filter = appearance.Filters[filterIndex - 1]; DreamObjectFilter filterObject = ObjectTree.CreateObject(ObjectTree.Filter); @@ -919,8 +927,8 @@ public override DreamValue GetValue(DreamValue key) { } public override List GetValues() { - IconAppearance appearance = GetAppearance(); - List filterList = new List(appearance.Filters.Count); + ImmutableIconAppearance appearance = GetAppearance(); + List filterList = new List(appearance.Filters.Length); foreach (var filter in appearance.Filters) { DreamObjectFilter filterObject = ObjectTree.CreateObject(ObjectTree.Filter); @@ -958,11 +966,11 @@ public override void AddValue(DreamValue value) { } public override int GetLength() { - return GetAppearance().Filters.Count; + return GetAppearance().Filters.Length; } - private IconAppearance GetAppearance() { - IconAppearance? appearance = _atomManager.MustGetAppearance(_owner); + private ImmutableIconAppearance GetAppearance() { + ImmutableIconAppearance? appearance = _atomManager.MustGetAppearance(_owner); if (appearance == null) throw new Exception("Atom has no appearance"); diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs b/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs index 1501b3eb4d..8bc708435b 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs @@ -178,11 +178,11 @@ protected override void SetVar(string varName, DreamValue value) { default: if (AtomManager.IsValidAppearanceVar(varName)) { // Basically AtomManager.UpdateAppearance() but without the performance impact of using actions - var appearance = AtomManager.MustGetAppearance(this); + var immutableAppearance = AtomManager.MustGetAppearance(this); // Clone the appearance // TODO: We can probably avoid cloning while the DMISpriteComponent is dirty - appearance = (appearance != null) ? new(appearance) : new(); + IconAppearance appearance = (immutableAppearance != null) ? immutableAppearance.ToMutable() : new(); AtomManager.SetAppearanceVar(appearance, varName, value); AtomManager.SetAtomAppearance(this, appearance); From e6ea8e3e8bde32e04dab98d2af2db9a2a9f77721 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Mon, 7 Oct 2024 15:00:25 +0100 Subject: [PATCH 24/54] server compiles --- .../Objects/Types/DreamObjectImage.cs | 70 +++++-------------- OpenDreamRuntime/Procs/DMProc.cs | 2 +- .../Rendering/ServerAppearanceSystem.cs | 6 ++ .../Rendering/ServerClientImagesSystem.cs | 6 +- 4 files changed, 28 insertions(+), 56 deletions(-) diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index 1a40c964d2..a333c768af 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -35,37 +35,33 @@ public DreamObjectImage(DreamObjectDefinition objectDefinition) : base(objectDef _underlays = new DreamOverlaysList(ObjectTree.List.ObjectDefinition, this, AppearanceSystem, true); _filters = new DreamFilterList(ObjectTree.List.ObjectDefinition, this); } + Entity = EntityManager.SpawnEntity(null, new MapCoordinates(0, 0, MapId.Nullspace)); //spawning an entity in nullspace means it never actually gets sent to any clients until it's placed on the map, or it gets a PVS override + SpriteComponent = EntityManager.AddComponent(Entity); } public override void Initialize(DreamProcArguments args) { base.Initialize(args); DreamValue icon = args.GetArgument(0); - IconAppearance? iconAppearance = null; + IconAppearance? iconAppearance; if (icon.IsNull || !AtomManager.TryCreateAppearanceFrom(icon, out iconAppearance)) { // Use a default appearance, but log a warning about it if icon wasn't null - Appearance = new(AtomManager.GetAppearanceFromDefinition(ObjectDefinition)); + iconAppearance = AtomManager.GetAppearanceFromDefinition(ObjectDefinition); if (!icon.IsNull) Logger.GetSawmill("opendream.image") .Warning($"Attempted to create an /image from {icon}. This is invalid and a default image was created instead."); } - if(iconAppearance is not null) - Appearance = iconAppearance; - - int argIndex = 1; DreamValue loc = args.GetArgument(1); if (loc.TryGetValueAsDreamObject(out _loc)) { // If it's not a DreamObject, it's actually icon_state and not loc argIndex = 2; } - iconAppearance = null; //mutate the appearance and then set it at the end to handle ref counts and client update foreach (string argName in IconCreationArgs) { var arg = args.GetArgument(argIndex++); if (arg.IsNull) continue; - iconAppearance ??= new(Appearance!); AtomManager.SetAppearanceVar(iconAppearance, argName, arg); if (argName == "dir" && arg.TryGetValueAsInteger(out var argDir) && argDir > 0) { @@ -76,10 +72,7 @@ public override void Initialize(DreamProcArguments args) { } } - if (iconAppearance is not null) { - AppearanceSystem!.AddAppearance(iconAppearance); // this is a no-op if the appearance is already in the system - Appearance = iconAppearance; - } + AtomManager.SetAtomAppearance(this, iconAppearance); } protected override bool TryGetVar(string varName, out DreamValue value) { @@ -100,7 +93,7 @@ protected override bool TryGetVar(string varName, out DreamValue value) { return true; default: { if (AtomManager.IsValidAppearanceVar(varName)) { - value = AtomManager.GetAppearanceVar(Appearance!, varName); + value = AtomManager.GetAppearanceVar(AtomManager.MustGetAppearance(this)!, varName); return true; } else { return base.TryGetVar(varName, out value); @@ -116,14 +109,9 @@ protected override void SetVar(string varName, DreamValue value) { return; // Ignore attempts to set an invalid appearance // The dir does not get changed - newAppearance.Direction = Appearance!.Direction; - - Appearance = newAppearance; - if(_entity != EntityUid.Invalid) { - DMISpriteComponent sprite = EntityManager.GetComponent(_entity); - AtomManager.SetSpriteAppearance(new(_entity, sprite), Appearance); - } - + var originalAppearance = AtomManager.MustGetAppearance(this); + newAppearance.Direction = originalAppearance!.Direction; + AtomManager.SetAtomAppearance(this, newAppearance); break; case "loc": value.TryGetValueAsDreamObject(out _loc); @@ -139,7 +127,7 @@ protected override void SetVar(string varName, DreamValue value) { if (valueList != null) { _overlays = valueList.CreateCopy(); } else { - var overlay = DreamOverlaysList.CreateOverlayAppearance(AtomManager, value, Appearance?.Icon); + var overlay = DreamOverlaysList.CreateOverlayAppearance(AtomManager, value, AtomManager.MustGetAppearance(this)?.Icon); if (overlay == null) return; @@ -171,7 +159,7 @@ protected override void SetVar(string varName, DreamValue value) { if (valueList != null) { _underlays = valueList.CreateCopy(); } else { - var underlay = DreamOverlaysList.CreateOverlayAppearance(AtomManager, value, Appearance?.Icon); + var underlay = DreamOverlaysList.CreateOverlayAppearance(AtomManager, value, AtomManager.MustGetAppearance(this)?.Icon); if (underlay == null) return; @@ -213,19 +201,16 @@ protected override void SetVar(string varName, DreamValue value) { break; } case "override": { - Appearance!.Override = value.IsTruthy(); + IconAppearance iconAppearance = AtomManager.MustGetAppearance(this)!.ToMutable(); + iconAppearance.Override = value.IsTruthy(); + AtomManager.SetAtomAppearance(this, iconAppearance); break; } default: if (AtomManager.IsValidAppearanceVar(varName)) { - IconAppearance iconAppearance = new(Appearance!); + IconAppearance iconAppearance = AtomManager.MustGetAppearance(this)!.ToMutable(); AtomManager.SetAppearanceVar(iconAppearance, varName, value); - Appearance = iconAppearance; - if(_entity != EntityUid.Invalid) { - DMISpriteComponent sprite = EntityManager.GetComponent(_entity); - AtomManager.SetSpriteAppearance(new(_entity, sprite), Appearance!); - } - + AtomManager.SetAtomAppearance(this, iconAppearance); break; } @@ -238,25 +223,6 @@ protected override void SetVar(string varName, DreamValue value) { return this._loc; } - /// - /// Get or create the entity associated with this image. Used for putting this image in the world ie, with vis_contents - /// The associated entity is deleted when the image is. - /// - public EntityUid GetEntity() { - if(_entity == EntityUid.Invalid) { - _entity = EntityManager.SpawnEntity(null, new MapCoordinates(0, 0, MapId.Nullspace)); - DMISpriteComponent sprite = EntityManager.AddComponent(_entity); - if(Appearance is not null) - AtomManager.SetSpriteAppearance(new(_entity, sprite), Appearance); - } - - return _entity; - } - - public void SetAppearance(IconAppearance? appearance) { - _appearance = appearance; - } - protected override void HandleDeletion(bool possiblyThreaded) { // SAFETY: Deleting entities is not threadsafe. if (possiblyThreaded) { @@ -264,8 +230,8 @@ protected override void HandleDeletion(bool possiblyThreaded) { return; } - if(_entity != EntityUid.Invalid) { - EntityManager.DeleteEntity(_entity); + if(Entity != EntityUid.Invalid) { + EntityManager.DeleteEntity(Entity); } base.HandleDeletion(possiblyThreaded); diff --git a/OpenDreamRuntime/Procs/DMProc.cs b/OpenDreamRuntime/Procs/DMProc.cs index c454766cab..64c013aa8b 100644 --- a/OpenDreamRuntime/Procs/DMProc.cs +++ b/OpenDreamRuntime/Procs/DMProc.cs @@ -892,7 +892,7 @@ public DreamValue DereferenceField(DreamValue owner, string field) { if (!Proc.AtomManager.IsValidAppearanceVar(field)) ThrowInvalidAppearanceVar(field); - return Proc.AtomManager.GetAppearanceVar(appearance, field); + return Proc.AtomManager.GetAppearanceVar(new ImmutableIconAppearance(appearance), field); //TODO gross, bad } else if (owner.TryGetValueAsType(out var ownerType) && ownerType.ObjectDefinition.Variables.TryGetValue(field, out var val)) { return val; // equivalent to initial() } diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 40a7b62474..2e950dc80d 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -70,6 +70,12 @@ public bool TryGetAppearance(int appearanceId, [NotNullWhen(true)] out Immutable } } + public bool TryGetAppearanceId(IconAppearance appearance,out int appearanceId) { + lock (_lock) { + return _appearanceToId.TryGetValue(new(appearance), out appearanceId); + } + } + public void Animate(NetEntity entity, IconAppearance targetAppearance, TimeSpan duration, AnimationEasing easing, int loop, AnimationFlags flags, int delay, bool chainAnim) { int appearanceId = AddAppearance(targetAppearance); diff --git a/OpenDreamRuntime/Rendering/ServerClientImagesSystem.cs b/OpenDreamRuntime/Rendering/ServerClientImagesSystem.cs index 5faef9180d..8e088b7bb2 100644 --- a/OpenDreamRuntime/Rendering/ServerClientImagesSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerClientImagesSystem.cs @@ -22,7 +22,7 @@ public void AddImageObject(DreamConnection connection, DreamObjectImage imageObj turfCoords = new Vector3(turf.X, turf.Y, turf.Z); NetEntity ent = GetNetEntity(locEntity); - EntityUid imageObjectEntity = imageObject.GetEntity(); + EntityUid imageObjectEntity = imageObject.Entity; NetEntity imageObjectNetEntity = GetNetEntity(imageObjectEntity); if (imageObjectEntity != EntityUid.Invalid) _pvsOverrideSystem.AddSessionOverride(imageObjectEntity, connection.Session!); @@ -44,10 +44,10 @@ public void RemoveImageObject(DreamConnection connection, DreamObjectImage image NetEntity ent = GetNetEntity(locEntity); - EntityUid imageObjectEntity = imageObject.GetEntity(); + EntityUid imageObjectEntity = imageObject.Entity; if (imageObjectEntity != EntityUid.Invalid) _pvsOverrideSystem.RemoveSessionOverride(imageObjectEntity, connection.Session!); - NetEntity imageObjectNetEntity = GetNetEntity(imageObject.GetEntity()); + NetEntity imageObjectNetEntity = GetNetEntity(imageObject.Entity); RaiseNetworkEvent(new RemoveClientImageEvent(ent, turfCoords, imageObjectNetEntity), connection.Session!.Channel); } } From 8b32fec3ea039a3862b57b48ed814e560d7d57b5 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Mon, 7 Oct 2024 15:15:05 +0100 Subject: [PATCH 25/54] that is surprisingly not that broken --- OpenDreamClient/EntryPoint.cs | 2 +- OpenDreamClient/Rendering/ClientAppearanceSystem.cs | 4 ++-- OpenDreamRuntime/Objects/Types/DreamObjectImage.cs | 1 + OpenDreamShared/Network/Messages/MsgAllAppearances.cs | 6 ++++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/OpenDreamClient/EntryPoint.cs b/OpenDreamClient/EntryPoint.cs index b9f70af63c..e9ca3f83e6 100644 --- a/OpenDreamClient/EntryPoint.cs +++ b/OpenDreamClient/EntryPoint.cs @@ -110,7 +110,7 @@ private void RxAllAppearances(MsgAllAppearances message) { return; } - clientAppearanceSystem.SetAllAppearances(message.AllAppearances); + clientAppearanceSystem.SetAllAppearances(message.AllAppearancesMutable!); } // As of RobustToolbox v0.90.0.0 there's a TileEdgeOverlay that breaks our rendering diff --git a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs index af9e23480f..1b451ed29c 100644 --- a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs +++ b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs @@ -69,10 +69,10 @@ public DreamIcon GetTurfIcon(int turfId) { } private void OnNewAppearance(NewAppearanceEvent e) { - _appearances[e.AppearanceId] = e.Appearance; + _appearances[e.AppearanceId] = e.Appearance.ToMutable(); if (_appearanceLoadCallbacks.TryGetValue(e.AppearanceId, out var callbacks)) { - foreach (var callback in callbacks) callback(e.Appearance); + foreach (var callback in callbacks) callback(_appearances[e.AppearanceId]); } } diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index a333c768af..8962890116 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -35,6 +35,7 @@ public DreamObjectImage(DreamObjectDefinition objectDefinition) : base(objectDef _underlays = new DreamOverlaysList(ObjectTree.List.ObjectDefinition, this, AppearanceSystem, true); _filters = new DreamFilterList(ObjectTree.List.ObjectDefinition, this); } + //TODO this means we send all mutable appearances to clients, even though we don't send the entity they're attached to Entity = EntityManager.SpawnEntity(null, new MapCoordinates(0, 0, MapId.Nullspace)); //spawning an entity in nullspace means it never actually gets sent to any clients until it's placed on the map, or it gets a PVS override SpriteComponent = EntityManager.AddComponent(Entity); } diff --git a/OpenDreamShared/Network/Messages/MsgAllAppearances.cs b/OpenDreamShared/Network/Messages/MsgAllAppearances.cs index 311276934c..ccb982cb56 100644 --- a/OpenDreamShared/Network/Messages/MsgAllAppearances.cs +++ b/OpenDreamShared/Network/Messages/MsgAllAppearances.cs @@ -45,7 +45,9 @@ private enum Property : byte { End } + //write them as immutable, read them as mutable (client wants them mutable) public Dictionary AllAppearances = allAppearances; + public Dictionary? AllAppearancesMutable = null; public MsgAllAppearances() : this(new()) { } @@ -53,7 +55,7 @@ public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer var count = buffer.ReadInt32(); var appearanceId = -1; - AllAppearances = new(count); + AllAppearancesMutable = new(count); for (int i = 0; i < count; i++) { var appearance = new IconAppearance(); @@ -207,7 +209,7 @@ public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer property = (Property)buffer.ReadByte(); } - AllAppearances.Add(appearanceId, new(appearance)); + AllAppearancesMutable.Add(appearanceId, appearance); } } From 2f9a7e6edb40d75db045cc72cbc3c5c3917ebe2c Mon Sep 17 00:00:00 2001 From: amylizzle Date: Mon, 7 Oct 2024 16:00:28 +0100 Subject: [PATCH 26/54] fix order of ops on sprite creation --- OpenDreamRuntime/AtomManager.cs | 10 +++++----- OpenDreamRuntime/Objects/Types/DreamObjectImage.cs | 3 ++- OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index 3d0a65574d..4b7a81bff6 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -474,22 +474,22 @@ public DreamValue GetAppearanceVar(ImmutableIconAppearance appearance, string va public ImmutableIconAppearance? MustGetAppearance(DreamObject atom) { return atom switch { DreamObjectTurf turf => AppearanceSystem!.MustGetAppearance(turf.AppearanceId), - DreamObjectMovable movable => AppearanceSystem!.MustGetAppearance(movable.SpriteComponent.AppearanceId.Value), + DreamObjectMovable movable => AppearanceSystem!.MustGetAppearance(movable.SpriteComponent.AppearanceId!.Value), DreamObjectArea area => AppearanceSystem!.MustGetAppearance(area.AppearanceId), - DreamObjectImage image => AppearanceSystem!.MustGetAppearance(image.SpriteComponent.AppearanceId.Value), + DreamObjectImage image => AppearanceSystem!.MustGetAppearance(image.SpriteComponent.AppearanceId!.Value), _ => throw new Exception($"Cannot get appearance of {atom}") }; } /// - /// Optionally looks up for an appearance. Does not try to create a new one when one is not found for this atom. + /// Optionally looks up for an appearance. Does not try to create a new one when one is not found for this atom. An invalid AppearanceId is an error, but null is fine. /// public bool TryGetAppearance(DreamObject atom, [NotNullWhen(true)] out ImmutableIconAppearance? appearance) { if (atom is DreamObjectTurf turf) appearance = AppearanceSystem?.MustGetAppearance(turf.AppearanceId); - else if (atom is DreamObjectMovable movable) + else if (atom is DreamObjectMovable movable && movable.SpriteComponent.AppearanceId is not null) appearance = AppearanceSystem?.MustGetAppearance(movable.SpriteComponent.AppearanceId.Value); - else if (atom is DreamObjectImage image) + else if (atom is DreamObjectImage image && image.SpriteComponent.AppearanceId is not null) appearance = AppearanceSystem?.MustGetAppearance(image.SpriteComponent.AppearanceId.Value); else if (atom is DreamObjectArea area) appearance = AppearanceSystem?.MustGetAppearance(area.AppearanceId); diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index 8962890116..c36fe96571 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -38,6 +38,7 @@ public DreamObjectImage(DreamObjectDefinition objectDefinition) : base(objectDef //TODO this means we send all mutable appearances to clients, even though we don't send the entity they're attached to Entity = EntityManager.SpawnEntity(null, new MapCoordinates(0, 0, MapId.Nullspace)); //spawning an entity in nullspace means it never actually gets sent to any clients until it's placed on the map, or it gets a PVS override SpriteComponent = EntityManager.AddComponent(Entity); + AtomManager.SetSpriteAppearance((Entity, SpriteComponent), AtomManager.GetAppearanceFromDefinition(ObjectDefinition)); } public override void Initialize(DreamProcArguments args) { @@ -47,7 +48,7 @@ public override void Initialize(DreamProcArguments args) { IconAppearance? iconAppearance; if (icon.IsNull || !AtomManager.TryCreateAppearanceFrom(icon, out iconAppearance)) { // Use a default appearance, but log a warning about it if icon wasn't null - iconAppearance = AtomManager.GetAppearanceFromDefinition(ObjectDefinition); + iconAppearance = AtomManager.MustGetAppearance(this)!.ToMutable(); //object def appearance is created in the constructor if (!icon.IsNull) Logger.GetSawmill("opendream.image") .Warning($"Attempted to create an /image from {icon}. This is invalid and a default image was created instead."); diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs b/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs index 487f3c4c24..53688032b6 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs @@ -30,6 +30,7 @@ private string? ScreenLoc { public DreamObjectMovable(DreamObjectDefinition objectDefinition) : base(objectDefinition) { Entity = AtomManager.CreateMovableEntity(this); SpriteComponent = EntityManager.GetComponent(Entity); + AtomManager.SetSpriteAppearance((Entity, SpriteComponent), AtomManager.GetAppearanceFromDefinition(ObjectDefinition)); _transformComponent = EntityManager.GetComponent(Entity); } From cd5268b5f507dc90c4e65340676b62294c7e7433 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Mon, 7 Oct 2024 16:24:48 +0100 Subject: [PATCH 27/54] fix turfs --- OpenDreamRuntime/DreamMapManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenDreamRuntime/DreamMapManager.cs b/OpenDreamRuntime/DreamMapManager.cs index b63f84d2a4..ab69ce7ee9 100644 --- a/OpenDreamRuntime/DreamMapManager.cs +++ b/OpenDreamRuntime/DreamMapManager.cs @@ -233,7 +233,7 @@ public void SetAreaAppearance(DreamObjectArea area, IconAppearance appearance) { turfAppearance.Overlays.Remove(oldAppearance); turfAppearance.Overlays.Add(area.AppearanceId); - newID = _appearanceSystem.AddAppearance(appearance); + newID = _appearanceSystem.AddAppearance(turfAppearance); oldToNewAppearanceID.Add(turf.AppearanceId, newID); turf.AppearanceId = newID; } From 9eb2e54f642e382b7b3e04bc793df104cb49caa5 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Mon, 7 Oct 2024 16:56:38 +0100 Subject: [PATCH 28/54] fix one test --- Content.Tests/DMProject/Tests/Savefile/ExportText.dm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Tests/DMProject/Tests/Savefile/ExportText.dm b/Content.Tests/DMProject/Tests/Savefile/ExportText.dm index b99c840a7c..74de97ecbf 100644 --- a/Content.Tests/DMProject/Tests/Savefile/ExportText.dm +++ b/Content.Tests/DMProject/Tests/Savefile/ExportText.dm @@ -1,8 +1,8 @@ -/obj/savetest - var/obj/savetest/recurse = null +/datum/savetest + var/datum/savetest/recurse = null /proc/RunTest() - var/obj/savetest/O = new() //create a test object + var/datum/savetest/O = new() //create a test object O.name = "test" //O.recurse = O //TODO From d84c87534dcf8fd9df8a44ac66dd3059078e9bb7 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Mon, 7 Oct 2024 17:22:09 +0100 Subject: [PATCH 29/54] oops --- Content.Tests/DMProject/Tests/Savefile/ExportText.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Tests/DMProject/Tests/Savefile/ExportText.dm b/Content.Tests/DMProject/Tests/Savefile/ExportText.dm index 74de97ecbf..7ecb792a14 100644 --- a/Content.Tests/DMProject/Tests/Savefile/ExportText.dm +++ b/Content.Tests/DMProject/Tests/Savefile/ExportText.dm @@ -1,4 +1,5 @@ /datum/savetest + var/name var/datum/savetest/recurse = null /proc/RunTest() From 85ffe1c90bd4f8ac12e829577b246c1844c959b1 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Fri, 11 Oct 2024 16:26:02 +0100 Subject: [PATCH 30/54] weakrefs motherfucker --- OpenDreamClient/EntryPoint.cs | 2 +- .../Rendering/ClientAppearanceSystem.cs | 2 +- OpenDreamRuntime/AtomManager.cs | 29 +++-- OpenDreamRuntime/DreamManager.cs | 4 +- OpenDreamRuntime/DreamMapManager.cs | 38 +++--- .../ImmutableIconAppearance.cs | 121 ++++++------------ OpenDreamRuntime/Objects/Types/DreamList.cs | 15 +-- .../Objects/Types/DreamObjectArea.cs | 7 +- .../Objects/Types/DreamObjectTurf.cs | 14 +- OpenDreamRuntime/Procs/DMProc.cs | 4 +- .../Rendering/DMISpriteComponent.cs | 2 +- OpenDreamRuntime/Rendering/DMISpriteSystem.cs | 4 +- .../Rendering/ServerAppearanceSystem.cs | 64 ++++----- OpenDreamShared/Dream/IconAppearance.cs | 9 +- .../Network/Messages/MsgAllAppearances.cs | 30 ++--- .../Rendering/SharedAppearanceSystem.cs | 4 +- .../Rendering/SharedDMISpriteComponent.cs | 21 ++- 17 files changed, 164 insertions(+), 206 deletions(-) rename {OpenDreamShared/Dream => OpenDreamRuntime}/ImmutableIconAppearance.cs (76%) diff --git a/OpenDreamClient/EntryPoint.cs b/OpenDreamClient/EntryPoint.cs index e9ca3f83e6..b9f70af63c 100644 --- a/OpenDreamClient/EntryPoint.cs +++ b/OpenDreamClient/EntryPoint.cs @@ -110,7 +110,7 @@ private void RxAllAppearances(MsgAllAppearances message) { return; } - clientAppearanceSystem.SetAllAppearances(message.AllAppearancesMutable!); + clientAppearanceSystem.SetAllAppearances(message.AllAppearances); } // As of RobustToolbox v0.90.0.0 there's a TileEdgeOverlay that breaks our rendering diff --git a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs index 1b451ed29c..8a6af8b511 100644 --- a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs +++ b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs @@ -69,7 +69,7 @@ public DreamIcon GetTurfIcon(int turfId) { } private void OnNewAppearance(NewAppearanceEvent e) { - _appearances[e.AppearanceId] = e.Appearance.ToMutable(); + _appearances[e.AppearanceId] = e.Appearance; if (_appearanceLoadCallbacks.TryGetValue(e.AppearanceId, out var callbacks)) { foreach (var callback in callbacks) callback(_appearances[e.AppearanceId]); diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index 4b7a81bff6..c66d2e6f22 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -385,6 +385,11 @@ public void SetAppearanceVar(IconAppearance appearance, string varName, DreamVal } } + //TODO THIS IS A SUPER NASTY HACK + public DreamValue GetAppearanceVar(IconAppearance appearance, string varName) { + return GetAppearanceVar(AppearanceSystem.AddAppearance(appearance), varName); + } + public DreamValue GetAppearanceVar(ImmutableIconAppearance appearance, string varName) { switch (varName) { case "name": @@ -473,10 +478,10 @@ public DreamValue GetAppearanceVar(ImmutableIconAppearance appearance, string va /// The atom to find the appearance of. public ImmutableIconAppearance? MustGetAppearance(DreamObject atom) { return atom switch { - DreamObjectTurf turf => AppearanceSystem!.MustGetAppearance(turf.AppearanceId), - DreamObjectMovable movable => AppearanceSystem!.MustGetAppearance(movable.SpriteComponent.AppearanceId!.Value), - DreamObjectArea area => AppearanceSystem!.MustGetAppearance(area.AppearanceId), - DreamObjectImage image => AppearanceSystem!.MustGetAppearance(image.SpriteComponent.AppearanceId!.Value), + DreamObjectTurf turf => turf.Appearance, + DreamObjectMovable movable => movable.SpriteComponent.Appearance!, + DreamObjectArea area => area.Appearance, + DreamObjectImage image => image.SpriteComponent.Appearance!, _ => throw new Exception($"Cannot get appearance of {atom}") }; } @@ -486,13 +491,13 @@ public DreamValue GetAppearanceVar(ImmutableIconAppearance appearance, string va /// public bool TryGetAppearance(DreamObject atom, [NotNullWhen(true)] out ImmutableIconAppearance? appearance) { if (atom is DreamObjectTurf turf) - appearance = AppearanceSystem?.MustGetAppearance(turf.AppearanceId); - else if (atom is DreamObjectMovable movable && movable.SpriteComponent.AppearanceId is not null) - appearance = AppearanceSystem?.MustGetAppearance(movable.SpriteComponent.AppearanceId.Value); - else if (atom is DreamObjectImage image && image.SpriteComponent.AppearanceId is not null) - appearance = AppearanceSystem?.MustGetAppearance(image.SpriteComponent.AppearanceId.Value); + appearance = turf.Appearance; + else if (atom is DreamObjectMovable movable && movable.SpriteComponent.Appearance is not null) + appearance = movable.SpriteComponent.Appearance; + else if (atom is DreamObjectImage image && image.SpriteComponent.Appearance is not null) + appearance = image.SpriteComponent.Appearance; else if (atom is DreamObjectArea area) - appearance = AppearanceSystem?.MustGetAppearance(area.AppearanceId); + appearance = area.Appearance; else appearance = null; @@ -534,11 +539,11 @@ public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasi if (atom is DreamObjectMovable movable) { targetEntity = movable.Entity; targetComponent = movable.SpriteComponent; - appearance = AppearanceSystem!.MustGetAppearance(targetComponent.AppearanceId!.Value).ToMutable(); + appearance = targetComponent.Appearance!.ToMutable(); } else if (atom is DreamObjectImage image){ targetEntity = image.Entity; targetComponent = image.SpriteComponent; - appearance = AppearanceSystem!.MustGetAppearance(targetComponent.AppearanceId!.Value).ToMutable(); + appearance = targetComponent.Appearance!.ToMutable(); } else throw new ArgumentException($"Cannot animate appearance of {atom}"); diff --git a/OpenDreamRuntime/DreamManager.cs b/OpenDreamRuntime/DreamManager.cs index 179d38118c..9bd863a53f 100644 --- a/OpenDreamRuntime/DreamManager.cs +++ b/OpenDreamRuntime/DreamManager.cs @@ -242,7 +242,7 @@ public string CreateRef(DreamValue value) { } else if (value.TryGetValueAsAppearance(out var appearance)) { refType = RefType.DreamAppearance; _appearanceSystem ??= _entitySystemManager.GetEntitySystem(); - idx = (int)_appearanceSystem.AddAppearance(appearance); + idx = _appearanceSystem.AddAppearance(appearance).GetHashCode(); } else if (value.TryGetValueAsDreamResource(out var refRsc)) { refType = RefType.DreamResource; idx = refRsc.Id; @@ -324,7 +324,7 @@ public DreamValue LocateRef(string refString) { return new DreamValue(resource); case RefType.DreamAppearance: _appearanceSystem ??= _entitySystemManager.GetEntitySystem(); - return _appearanceSystem.TryGetAppearance(refId, out ImmutableIconAppearance? appearance) + return _appearanceSystem.TryGetAppearanceByID(refId, out ImmutableIconAppearance? appearance) ? new DreamValue(appearance.ToMutable()) : DreamValue.Null; case RefType.Proc: diff --git a/OpenDreamRuntime/DreamMapManager.cs b/OpenDreamRuntime/DreamMapManager.cs index ab69ce7ee9..59a3c53ea0 100644 --- a/OpenDreamRuntime/DreamMapManager.cs +++ b/OpenDreamRuntime/DreamMapManager.cs @@ -193,53 +193,53 @@ public void SetTurf(DreamObjectTurf turf, DreamObjectDefinition type, DreamProcA private readonly Dictionary, IconAppearance> _turfAreaLookup = new(); public void SetTurfAppearance(DreamObjectTurf turf, IconAppearance appearance) { - if(turf.Cell.Area.AppearanceId != 0) - if(!appearance.Overlays.Contains(turf.Cell.Area.AppearanceId)) { - if(!_turfAreaLookup.TryGetValue((appearance, turf.Cell.Area.AppearanceId), out var newAppearance)) { + if(turf.Cell.Area.Appearance != _appearanceSystem.DefaultAppearance) + if(!appearance.Overlays.Contains(turf.Cell.Area.Appearance.GetHashCode())) { + if(!_turfAreaLookup.TryGetValue((appearance, turf.Cell.Area.Appearance.GetHashCode()), out var newAppearance)) { newAppearance = new(appearance); - newAppearance.Overlays.Add(turf.Cell.Area.AppearanceId); - _turfAreaLookup.Add((appearance, turf.Cell.Area.AppearanceId), newAppearance); + newAppearance.Overlays.Add(turf.Cell.Area.Appearance.GetHashCode()); + _turfAreaLookup.Add((appearance, turf.Cell.Area.Appearance.GetHashCode()), newAppearance); } appearance = newAppearance; } - int appearanceId = _appearanceSystem.AddAppearance(appearance); + var immutableAppearance = _appearanceSystem.AddAppearance(appearance); var level = _levels[turf.Z - 1]; - int turfId = (appearanceId + 1); // +1 because 0 is used for empty turfs + int turfId = immutableAppearance.GetHashCode() + 1; // +1 because 0 is used for empty turfs level.QueuedTileUpdates[(turf.X, turf.Y)] = new Tile(turfId); - turf.AppearanceId = appearanceId; + turf.Appearance = immutableAppearance; } public void SetAreaAppearance(DreamObjectArea area, IconAppearance appearance) { //if an area changes appearance, invalidate the lookup _turfAreaLookup.Clear(); - int oldAppearance = area.AppearanceId; - area.AppearanceId = _appearanceSystem.AddAppearance(appearance); + int oldAppearanceId = area.Appearance.GetHashCode(); + area.Appearance = _appearanceSystem.AddAppearance(appearance); //get all unique turf appearances //create the new version of each of those appearances //for each turf, update the appropriate ID - Dictionary oldToNewAppearanceID = new(); + Dictionary oldToNewAppearance = new(); foreach (var turf in area.Contents.GetTurfs()) { - if(oldToNewAppearanceID.TryGetValue(turf.AppearanceId, out int newID)) - turf.AppearanceId = newID; + if(oldToNewAppearance.TryGetValue(turf.Appearance, out var newAppearance)) + turf.Appearance = newAppearance; else { IconAppearance? turfAppearance = _atomManager.MustGetAppearance(turf)?.ToMutable(); if(turfAppearance is null) continue; - turfAppearance.Overlays.Remove(oldAppearance); - turfAppearance.Overlays.Add(area.AppearanceId); - newID = _appearanceSystem.AddAppearance(turfAppearance); - oldToNewAppearanceID.Add(turf.AppearanceId, newID); - turf.AppearanceId = newID; + turfAppearance.Overlays.Remove(oldAppearanceId); + turfAppearance.Overlays.Add(area.Appearance.GetHashCode()); + newAppearance = _appearanceSystem.AddAppearance(turfAppearance); + oldToNewAppearance.Add(turf.Appearance, newAppearance); + turf.Appearance = newAppearance; } var level = _levels[turf.Z - 1]; - int turfId = (newID + 1); // +1 because 0 is used for empty turfs + int turfId = newAppearance.GetHashCode() + 1; // +1 because 0 is used for empty turfs level.QueuedTileUpdates[(turf.X, turf.Y)] = new Tile(turfId); } } diff --git a/OpenDreamShared/Dream/ImmutableIconAppearance.cs b/OpenDreamRuntime/ImmutableIconAppearance.cs similarity index 76% rename from OpenDreamShared/Dream/ImmutableIconAppearance.cs rename to OpenDreamRuntime/ImmutableIconAppearance.cs index a5ba9a41bb..812b469305 100644 --- a/OpenDreamShared/Dream/ImmutableIconAppearance.cs +++ b/OpenDreamRuntime/ImmutableIconAppearance.cs @@ -5,24 +5,25 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Robust.Shared.GameObjects; +using OpenDreamShared.Dream; +using System.Diagnostics.Contracts; +using System.Linq; +using Pidgin; -namespace OpenDreamShared.Dream; +namespace OpenDreamRuntime.Rendering; /* * Woe, weary traveler, modifying this class is not for the faint of heart. * If you modify IconAppearance, be sure to update the following places: - * - All of the methods on IconAppearance itself + * - All of the methods on ImmutableIconAppearance itself + * - IconAppearance * - IconAppearance methods in AtomManager - * - MsgAllAppearances - * - IconDebugWindow * - There may be others */ // TODO: Wow this is huge! Probably look into splitting this by most used/least used to reduce the size of these -[Serializable, NetSerializable] -public sealed class ImmutableIconAppearance : IEquatable, IEquatable { - public static readonly ImmutableIconAppearance Default = new(); +public sealed class ImmutableIconAppearance : IEquatable { [ViewVariables] public readonly string Name = string.Empty; [ViewVariables] public readonly int? Icon; [ViewVariables] public readonly string? IconState; @@ -43,8 +44,8 @@ public sealed class ImmutableIconAppearance : IEquatable PixelOffset + PixelOffset2; private int? storedHashCode; + private ServerAppearanceSystem appearanceSystem; - public ImmutableIconAppearance() { - Overlays = []; - Underlays = []; - VisContents = []; - Filters = []; - Verbs = []; - } + public ImmutableIconAppearance(IconAppearance appearance, ServerAppearanceSystem serverAppearanceSystem) { + this.appearanceSystem = serverAppearanceSystem; - public ImmutableIconAppearance(IconAppearance appearance) { Name = appearance.Name; Icon = appearance.Icon; IconState = appearance.IconState; @@ -103,19 +99,28 @@ public ImmutableIconAppearance(IconAppearance appearance) { Invisibility = appearance.Invisibility; Opacity = appearance.Opacity; MouseOpacity = appearance.MouseOpacity; - Overlays = appearance.Overlays.ToArray(); - Underlays = appearance.Underlays.ToArray(); + + int i = 0; + Overlays = new ImmutableIconAppearance[appearance.Overlays.Count]; + foreach(int overlayId in appearance.Overlays) + Overlays[i++] = serverAppearanceSystem.MustGetAppearanceByID(overlayId); + + i = 0; + Underlays = new ImmutableIconAppearance[appearance.Underlays.Count]; + foreach(int underlayId in appearance.Underlays) + Underlays[i++] = serverAppearanceSystem.MustGetAppearanceByID(underlayId); + VisContents = appearance.VisContents.ToArray(); Filters = appearance.Filters.ToArray(); Verbs = appearance.Verbs.ToArray(); Override = appearance.Override; - for (int i = 0; i < 6; i++) { + for (i = 0; i < 6; i++) { Transform[i] = appearance.Transform[i]; } } - public override bool Equals(object? obj) => (obj is IconAppearance appearance && Equals(appearance)) || (obj is ImmutableIconAppearance immutable && Equals(immutable)); + public override bool Equals(object? obj) => obj is ImmutableIconAppearance immutable && Equals(immutable); public bool Equals(ImmutableIconAppearance? immutableIconAppearance) { if (immutableIconAppearance == null) return false; @@ -174,63 +179,6 @@ public bool Equals(ImmutableIconAppearance? immutableIconAppearance) { return true; } - public bool Equals(IconAppearance? appearance) { - if (appearance == null) return false; - - if (appearance.Name != Name) return false; - if (appearance.Icon != Icon) return false; - if (appearance.IconState != IconState) return false; - if (appearance.Direction != Direction) return false; - if (appearance.InheritsDirection != InheritsDirection) return false; - if (appearance.PixelOffset != PixelOffset) return false; - if (appearance.PixelOffset2 != PixelOffset2) return false; - if (appearance.Color != Color) return false; - if (appearance.Alpha != Alpha) return false; - if (appearance.GlideSize != GlideSize) return false; - if (!appearance.ColorMatrix.Equals(ColorMatrix)) return false; - if (appearance.Layer != Layer) return false; - if (appearance.Plane != Plane) return false; - if (appearance.RenderSource != RenderSource) return false; - if (appearance.RenderTarget != RenderTarget) return false; - if (appearance.BlendMode != BlendMode) return false; - if (appearance.AppearanceFlags != AppearanceFlags) return false; - if (appearance.Invisibility != Invisibility) return false; - if (appearance.Opacity != Opacity) return false; - if (appearance.MouseOpacity != MouseOpacity) return false; - if (appearance.Overlays.Count != Overlays.Length) return false; - if (appearance.Underlays.Count != Underlays.Length) return false; - if (appearance.VisContents.Count != VisContents.Length) return false; - if (appearance.Filters.Count != Filters.Length) return false; - if (appearance.Verbs.Count != Verbs.Length) return false; - if (appearance.Override != Override) return false; - - for (int i = 0; i < Filters.Length; i++) { - if (appearance.Filters[i] != Filters[i]) return false; - } - - for (int i = 0; i < Overlays.Length; i++) { - if (appearance.Overlays[i] != Overlays[i]) return false; - } - - for (int i = 0; i < Underlays.Length; i++) { - if (appearance.Underlays[i] != Underlays[i]) return false; - } - - for (int i = 0; i < VisContents.Length; i++) { - if (appearance.VisContents[i] != VisContents[i]) return false; - } - - for (int i = 0; i < Verbs.Length; i++) { - if (appearance.Verbs[i] != Verbs[i]) return false; - } - - for (int i = 0; i < 6; i++) { - if (!appearance.Transform[i].Equals(Transform[i])) return false; - } - - return true; - } - /// /// This is a helper used for both optimization and parity.
/// In BYOND, if a color matrix is representable as an RGBA color string,
@@ -289,11 +237,11 @@ public override int GetHashCode() { hashCode.Add(BlendMode); hashCode.Add(AppearanceFlags); - foreach (int overlay in Overlays) { + foreach (ImmutableIconAppearance overlay in Overlays) { hashCode.Add(overlay); } - foreach (int underlay in Underlays) { + foreach (ImmutableIconAppearance underlay in Underlays) { hashCode.Add(underlay); } @@ -318,6 +266,7 @@ public override int GetHashCode() { } //Creates an editable *copy* of this appearance, which must be added to the ServerAppearanceSystem to be used. + [Pure] public IconAppearance ToMutable() { IconAppearance result = new IconAppearance() { Name = this.Name, @@ -340,13 +289,17 @@ public IconAppearance ToMutable() { Invisibility = this.Invisibility, Opacity = this.Opacity, MouseOpacity = this.MouseOpacity, - Overlays = new(this.Overlays), - Underlays = new(this.Underlays), + Overlays = new(this.Overlays.Length), + Underlays = new(this.Underlays.Length), VisContents = new(this.VisContents), Filters = new(this.Filters), Verbs = new(this.Verbs), Override = this.Override, }; + foreach(ImmutableIconAppearance overlay in Overlays) + result.Overlays.Add(overlay.GetHashCode()); + foreach(ImmutableIconAppearance underlay in Underlays) + result.Underlays.Add(underlay.GetHashCode()); for (int i = 0; i < 6; i++) { result.Transform[i] = Transform[i]; @@ -354,5 +307,9 @@ public IconAppearance ToMutable() { return result; } + ~ImmutableIconAppearance() { + appearanceSystem.RemoveAppearance(this); + } + } diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index 989b395574..4dfc4c9680 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -667,9 +667,7 @@ public override List GetValues() { var values = new List(overlays.Length); foreach (var overlay in overlays) { - var overlayAppearance = _appearanceSystem.MustGetAppearance(overlay); - - values.Add(new(overlayAppearance.ToMutable())); + values.Add(new(overlay.ToMutable())); } return values; @@ -697,8 +695,7 @@ public override DreamValue GetValue(DreamValue key) { if (_appearanceSystem == null) return DreamValue.Null; - int overlayId = overlaysList[overlayIndex - 1]; - ImmutableIconAppearance overlayAppearance = _appearanceSystem.MustGetAppearance(overlayId); + ImmutableIconAppearance overlayAppearance = overlaysList[overlayIndex - 1]; return new DreamValue(overlayAppearance.ToMutable()); } @@ -714,7 +711,7 @@ public override void AddValue(DreamValue value) { IconAppearance? overlayAppearance = CreateOverlayAppearance(_atomManager, value, appearance.Icon); overlayAppearance ??= new IconAppearance(); - GetOverlaysList(appearance).Add(_appearanceSystem.AddAppearance(overlayAppearance)); + GetOverlaysList(appearance).Add(_appearanceSystem.AddAppearance(overlayAppearance).GetHashCode()); }); } @@ -724,10 +721,10 @@ public override void RemoveValue(DreamValue value) { _atomManager.UpdateAppearance(_owner, appearance => { IconAppearance? overlayAppearance = CreateOverlayAppearance(_atomManager, value, appearance.Icon); - if (overlayAppearance == null || !_appearanceSystem.TryGetAppearanceId(overlayAppearance, out var id)) + if (overlayAppearance == null) return; - GetOverlaysList(appearance).Remove(id); + GetOverlaysList(appearance).Remove(_appearanceSystem.AddAppearance(overlayAppearance).GetHashCode()); }); } @@ -739,7 +736,7 @@ public override int GetLength() { private List GetOverlaysList(IconAppearance appearance) => _isUnderlays ? appearance.Underlays : appearance.Overlays; - private int[] GetOverlaysArray(ImmutableIconAppearance appearance) => + private ImmutableIconAppearance[] GetOverlaysArray(ImmutableIconAppearance appearance) => _isUnderlays ? appearance.Underlays : appearance.Overlays; private ImmutableIconAppearance GetAppearance() { diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs b/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs index 45d57160fd..909df5507c 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs @@ -1,12 +1,15 @@ -namespace OpenDreamRuntime.Objects.Types; +using OpenDreamRuntime.Rendering; + +namespace OpenDreamRuntime.Objects.Types; public sealed class DreamObjectArea : DreamObjectAtom { public int X, Y, Z; public readonly AreaContentsList Contents; - public int AppearanceId; + public ImmutableIconAppearance Appearance; public DreamObjectArea(DreamObjectDefinition objectDefinition) : base(objectDefinition) { Contents = new(ObjectTree.List.ObjectDefinition, this); + Appearance = AppearanceSystem!.DefaultAppearance; AtomManager.SetAtomAppearance(this, AtomManager.GetAppearanceFromDefinition(ObjectDefinition)); } diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs index 4cab00e3b3..6562a7a5c1 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs @@ -1,12 +1,12 @@ -namespace OpenDreamRuntime.Objects.Types; +using OpenDreamRuntime.Rendering; + +namespace OpenDreamRuntime.Objects.Types; public sealed class DreamObjectTurf : DreamObjectAtom { public readonly int X, Y, Z; public readonly IDreamMapManager.Cell Cell; public readonly TurfContentsList Contents; - public int AppearanceId { get => _appearanceId!.Value; set => SetAppearanceId(value); } - - private int? _appearanceId; + public ImmutableIconAppearance Appearance; public DreamObjectTurf(DreamObjectDefinition objectDefinition, int x, int y, int z, IDreamMapManager.Cell cell) : base(objectDefinition) { X = x; @@ -14,6 +14,8 @@ public DreamObjectTurf(DreamObjectDefinition objectDefinition, int x, int y, int Z = z; Cell = cell; Contents = new TurfContentsList(ObjectTree.List.ObjectDefinition, Cell); + Appearance = AppearanceSystem!.DefaultAppearance; + } public void SetTurfType(DreamObjectDefinition objectDefinition) { @@ -65,8 +67,4 @@ protected override void SetVar(string varName, DreamValue value) { break; } } - - public void SetAppearanceId(int appearanceId) { - _appearanceId = appearanceId; - } } diff --git a/OpenDreamRuntime/Procs/DMProc.cs b/OpenDreamRuntime/Procs/DMProc.cs index 64c013aa8b..b99a398c44 100644 --- a/OpenDreamRuntime/Procs/DMProc.cs +++ b/OpenDreamRuntime/Procs/DMProc.cs @@ -8,8 +8,10 @@ using OpenDreamRuntime.Objects; using OpenDreamRuntime.Objects.Types; using OpenDreamRuntime.Procs.DebugAdapter; +using OpenDreamRuntime.Rendering; using OpenDreamRuntime.Resources; using OpenDreamShared.Dream; +using Robust.Server.GameObjects; using Robust.Shared.Utility; namespace OpenDreamRuntime.Procs { @@ -892,7 +894,7 @@ public DreamValue DereferenceField(DreamValue owner, string field) { if (!Proc.AtomManager.IsValidAppearanceVar(field)) ThrowInvalidAppearanceVar(field); - return Proc.AtomManager.GetAppearanceVar(new ImmutableIconAppearance(appearance), field); //TODO gross, bad + return Proc.AtomManager.GetAppearanceVar(appearance, field); } else if (owner.TryGetValueAsType(out var ownerType) && ownerType.ObjectDefinition.Variables.TryGetValue(field, out var val)) { return val; // equivalent to initial() } diff --git a/OpenDreamRuntime/Rendering/DMISpriteComponent.cs b/OpenDreamRuntime/Rendering/DMISpriteComponent.cs index 81c08be0b6..68ec90b618 100644 --- a/OpenDreamRuntime/Rendering/DMISpriteComponent.cs +++ b/OpenDreamRuntime/Rendering/DMISpriteComponent.cs @@ -10,6 +10,6 @@ public sealed partial class DMISpriteComponent : SharedDMISpriteComponent { public ScreenLocation ScreenLocation; [Access(typeof(DMISpriteSystem))] - [ViewVariables] public int? AppearanceId; + [ViewVariables] public ImmutableIconAppearance? Appearance; } diff --git a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs index 33b03eaaf2..853dd0f8e0 100644 --- a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs +++ b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs @@ -14,12 +14,12 @@ public override void Initialize() { } private void GetComponentState(EntityUid uid, DMISpriteComponent component, ref ComponentGetState args) { - args.State = new SharedDMISpriteComponent.DMISpriteComponentState(component.AppearanceId, component.ScreenLocation); + args.State = new SharedDMISpriteComponent.DMISpriteComponentState(component.Appearance.GetHashCode(), component.ScreenLocation); } public void SetSpriteAppearance(Entity ent, IconAppearance appearance, bool dirty = true) { DMISpriteComponent component = ent.Comp; - component.AppearanceId = _appearance?.AddAppearance(appearance); + component.Appearance = _appearance?.AddAppearance(appearance); if(dirty) Dirty(ent, component); } diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 2e950dc80d..f92e19738f 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -9,11 +9,11 @@ namespace OpenDreamRuntime.Rendering; public sealed class ServerAppearanceSystem : SharedAppearanceSystem { - private readonly Dictionary _appearanceToId = new(); - private readonly Dictionary _idToAppearance = new(); - private readonly Dictionary _appearanceRefCounts = new(); - private int _appearanceIdCounter; + //use the appearance hash as the id! + //appearance hash to weakref + private readonly Dictionary> _idToAppearance = new(); + public readonly ImmutableIconAppearance DefaultAppearance; /// /// This system is used by the PVS thread, we need to be thread-safe @@ -22,62 +22,64 @@ public sealed class ServerAppearanceSystem : SharedAppearanceSystem { [Dependency] private readonly IPlayerManager _playerManager = default!; + public ServerAppearanceSystem() { + DefaultAppearance = new ImmutableIconAppearance(IconAppearance.Default, this); + } + public override void Initialize() { - //register empty appearance as ID 0 - _appearanceToId.Add(ImmutableIconAppearance.Default, 0); - _idToAppearance.Add(0, ImmutableIconAppearance.Default); - _appearanceRefCounts.Add(ImmutableIconAppearance.Default, 1); - _appearanceIdCounter = 1; + _idToAppearance.Add(DefaultAppearance.GetHashCode(), new(DefaultAppearance)); _playerManager.PlayerStatusChanged += OnPlayerStatusChanged; } public override void Shutdown() { lock (_lock) { - _appearanceToId.Clear(); _idToAppearance.Clear(); - _appearanceIdCounter = 0; } } private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { if (e.NewStatus == SessionStatus.InGame) { - e.Session.Channel.SendMessage(new MsgAllAppearances(_idToAppearance)); + //todo this is probably stupid slow + Dictionary sendData = new(_idToAppearance.Count); + ImmutableIconAppearance? immutable; + foreach(int key in _idToAppearance.Keys ){ + if(_idToAppearance[key].TryGetTarget(out immutable)) + sendData.Add(key, immutable.ToMutable()); + } + e.Session.Channel.SendMessage(new MsgAllAppearances(sendData)); } } - public int AddAppearance(IconAppearance appearance) { - ImmutableIconAppearance immutableAppearance = new(appearance); + public ImmutableIconAppearance AddAppearance(IconAppearance appearance) { + ImmutableIconAppearance immutableAppearance = new(appearance, this); lock (_lock) { - if (!_appearanceToId.TryGetValue(immutableAppearance, out int appearanceId)) { - appearanceId = _appearanceIdCounter++; - _appearanceToId.Add(immutableAppearance, appearanceId); - _idToAppearance.Add(appearanceId, immutableAppearance); - RaiseNetworkEvent(new NewAppearanceEvent(appearanceId, immutableAppearance)); + if (_idToAppearance.TryAdd(immutableAppearance.GetHashCode(), new(immutableAppearance))) { + RaiseNetworkEvent(new NewAppearanceEvent(immutableAppearance.GetHashCode(), immutableAppearance.ToMutable())); } - return appearanceId; + return immutableAppearance; } } - public ImmutableIconAppearance MustGetAppearance(int appearanceId) { - lock (_lock) { - return _idToAppearance[appearanceId]; - } + //this should only be called by the ImmutableIconAppearance's finalizer + public void RemoveAppearance(ImmutableIconAppearance appearance) { + RaiseNetworkEvent(new RemoveAppearanceEvent(appearance.GetHashCode())); } - public bool TryGetAppearance(int appearanceId, [NotNullWhen(true)] out ImmutableIconAppearance? appearance) { - lock (_lock) { - return _idToAppearance.TryGetValue(appearanceId, out appearance); - } + public ImmutableIconAppearance MustGetAppearanceByID(int appearanceId) { + if(!_idToAppearance[appearanceId].TryGetTarget(out var result)) + throw new Exception($"Deleted appearance ID ${appearanceId} in MustGetAppearanceByID()"); + return result; } - public bool TryGetAppearanceId(IconAppearance appearance,out int appearanceId) { + public bool TryGetAppearanceByID(int appearanceId, [NotNullWhen(true)] out ImmutableIconAppearance? appearance) { lock (_lock) { - return _appearanceToId.TryGetValue(new(appearance), out appearanceId); + appearance = null; + return _idToAppearance.TryGetValue(appearanceId, out var appearanceRef) && appearanceRef.TryGetTarget(out appearance); } } public void Animate(NetEntity entity, IconAppearance targetAppearance, TimeSpan duration, AnimationEasing easing, int loop, AnimationFlags flags, int delay, bool chainAnim) { - int appearanceId = AddAppearance(targetAppearance); + int appearanceId = AddAppearance(targetAppearance).GetHashCode(); RaiseNetworkEvent(new AnimationEvent(entity, appearanceId, duration, easing, loop, flags, delay, chainAnim)); } diff --git a/OpenDreamShared/Dream/IconAppearance.cs b/OpenDreamShared/Dream/IconAppearance.cs index 5d4d11e17b..da42bdd0e6 100644 --- a/OpenDreamShared/Dream/IconAppearance.cs +++ b/OpenDreamShared/Dream/IconAppearance.cs @@ -20,7 +20,8 @@ namespace OpenDreamShared.Dream; */ // TODO: Wow this is huge! Probably look into splitting this by most used/least used to reduce the size of these -public sealed class IconAppearance : IEquatable, IEquatable { +[Serializable, NetSerializable] +public sealed class IconAppearance : IEquatable { public static readonly IconAppearance Default = new(); [ViewVariables] public string Name = string.Empty; @@ -113,11 +114,7 @@ public IconAppearance(IconAppearance appearance) { } } - public override bool Equals(object? obj) => (obj is IconAppearance appearance && Equals(appearance)) || (obj is ImmutableIconAppearance immutableIconAppearance && Equals(immutableIconAppearance)); - - public bool Equals(ImmutableIconAppearance? immutableIconAppearance) { - return immutableIconAppearance is null ? false : immutableIconAppearance.Equals(this); - } + public override bool Equals(object? obj) => obj is IconAppearance appearance && Equals(appearance); public bool Equals(IconAppearance? appearance) { if (appearance == null) return false; diff --git a/OpenDreamShared/Network/Messages/MsgAllAppearances.cs b/OpenDreamShared/Network/Messages/MsgAllAppearances.cs index ccb982cb56..4377d00aa6 100644 --- a/OpenDreamShared/Network/Messages/MsgAllAppearances.cs +++ b/OpenDreamShared/Network/Messages/MsgAllAppearances.cs @@ -9,7 +9,7 @@ namespace OpenDreamShared.Network.Messages; -public sealed class MsgAllAppearances(Dictionary allAppearances) : NetMessage { +public sealed class MsgAllAppearances(Dictionary allAppearances) : NetMessage { public override MsgGroups MsgGroup => MsgGroups.EntityEvent; private enum Property : byte { @@ -45,9 +45,7 @@ private enum Property : byte { End } - //write them as immutable, read them as mutable (client wants them mutable) - public Dictionary AllAppearances = allAppearances; - public Dictionary? AllAppearancesMutable = null; + public Dictionary AllAppearances = allAppearances; public MsgAllAppearances() : this(new()) { } @@ -55,7 +53,7 @@ public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer var count = buffer.ReadInt32(); var appearanceId = -1; - AllAppearancesMutable = new(count); + AllAppearances = new(count); for (int i = 0; i < count; i++) { var appearance = new IconAppearance(); @@ -209,7 +207,7 @@ public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer property = (Property)buffer.ReadByte(); } - AllAppearancesMutable.Add(appearanceId, appearance); + AllAppearances.Add(appearanceId, appearance); } } @@ -335,37 +333,37 @@ public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer buffer.Write((byte)appearance.MouseOpacity); } - if (appearance.Overlays.Length != 0) { + if (appearance.Overlays.Count != 0) { buffer.Write((byte)Property.Overlays); - buffer.WriteVariableInt32(appearance.Overlays.Length); + buffer.WriteVariableInt32(appearance.Overlays.Count); foreach (var overlay in appearance.Overlays) { buffer.WriteVariableInt32(overlay); } } - if (appearance.Underlays.Length != 0) { + if (appearance.Underlays.Count != 0) { buffer.Write((byte)Property.Underlays); - buffer.WriteVariableInt32(appearance.Underlays.Length); + buffer.WriteVariableInt32(appearance.Underlays.Count); foreach (var underlay in appearance.Underlays) { buffer.WriteVariableInt32(underlay); } } - if (appearance.VisContents.Length != 0) { + if (appearance.VisContents.Count != 0) { buffer.Write((byte)Property.VisContents); - buffer.WriteVariableInt32(appearance.VisContents.Length); + buffer.WriteVariableInt32(appearance.VisContents.Count); foreach (var item in appearance.VisContents) { buffer.Write(item); } } - if (appearance.Filters.Length != 0) { + if (appearance.Filters.Count != 0) { buffer.Write((byte)Property.Filters); - buffer.Write(appearance.Filters.Length); + buffer.Write(appearance.Filters.Count); foreach (var filter in appearance.Filters) { using var filterStream = new MemoryStream(); @@ -376,10 +374,10 @@ public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer } } - if (appearance.Verbs.Length != 0) { + if (appearance.Verbs.Count != 0) { buffer.Write((byte)Property.Verbs); - buffer.WriteVariableInt32(appearance.Verbs.Length); + buffer.WriteVariableInt32(appearance.Verbs.Count); foreach (var verb in appearance.Verbs) { buffer.WriteVariableInt32(verb); } diff --git a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs index 5eb6daf26a..d7e95b465e 100644 --- a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs +++ b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs @@ -7,9 +7,9 @@ namespace OpenDreamShared.Rendering; public abstract class SharedAppearanceSystem : EntitySystem { [Serializable, NetSerializable] - public sealed class NewAppearanceEvent(int appearanceId, ImmutableIconAppearance appearance) : EntityEventArgs { + public sealed class NewAppearanceEvent(int appearanceId, IconAppearance appearance) : EntityEventArgs { public int AppearanceId { get; } = appearanceId; - public ImmutableIconAppearance Appearance { get; } = appearance; + public IconAppearance Appearance { get; } = appearance; } [Serializable, NetSerializable] diff --git a/OpenDreamShared/Rendering/SharedDMISpriteComponent.cs b/OpenDreamShared/Rendering/SharedDMISpriteComponent.cs index 004ffe2534..d8fdff8bc4 100644 --- a/OpenDreamShared/Rendering/SharedDMISpriteComponent.cs +++ b/OpenDreamShared/Rendering/SharedDMISpriteComponent.cs @@ -4,18 +4,17 @@ using Robust.Shared.GameStates; using OpenDreamShared.Dream; -namespace OpenDreamShared.Rendering { - [NetworkedComponent] - public abstract partial class SharedDMISpriteComponent : Component { - [Serializable, NetSerializable] - public sealed class DMISpriteComponentState : ComponentState { - public readonly int? AppearanceId; - public readonly ScreenLocation ScreenLocation; +namespace OpenDreamShared.Rendering; +[NetworkedComponent] +public abstract partial class SharedDMISpriteComponent : Component { + [Serializable, NetSerializable] + public sealed class DMISpriteComponentState : ComponentState { + public readonly int? AppearanceId; + public readonly ScreenLocation ScreenLocation; - public DMISpriteComponentState(int? appearanceId, ScreenLocation screenLocation) { - AppearanceId = appearanceId; - ScreenLocation = screenLocation; - } + public DMISpriteComponentState(int? appearanceId, ScreenLocation screenLocation) { + AppearanceId = appearanceId; + ScreenLocation = screenLocation; } } } From ca1f6b745f71b7303cb11c2cf788c221b1cca551 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Fri, 11 Oct 2024 16:53:33 +0100 Subject: [PATCH 31/54] bugs --- .../Rendering/ClientAppearanceSystem.cs | 1 + .../Rendering/ServerAppearanceSystem.cs | 26 ++++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs index 8a6af8b511..264bcde83d 100644 --- a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs +++ b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs @@ -48,6 +48,7 @@ public void SetAllAppearances(Dictionary appearances) { public void LoadAppearance(int appearanceId, Action loadCallback) { if (_appearances.TryGetValue(appearanceId, out var appearance)) { loadCallback(appearance); + return; } if (!_appearanceLoadCallbacks.ContainsKey(appearanceId)) { diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index f92e19738f..9ed55d873a 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -40,13 +40,16 @@ public override void Shutdown() { private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { if (e.NewStatus == SessionStatus.InGame) { //todo this is probably stupid slow - Dictionary sendData = new(_idToAppearance.Count); - ImmutableIconAppearance? immutable; - foreach(int key in _idToAppearance.Keys ){ - if(_idToAppearance[key].TryGetTarget(out immutable)) - sendData.Add(key, immutable.ToMutable()); + lock (_lock) { + Dictionary sendData = new(_idToAppearance.Count); + ImmutableIconAppearance? immutable; + foreach(int key in _idToAppearance.Keys ){ + if(_idToAppearance[key].TryGetTarget(out immutable)) + sendData.Add(key, immutable.ToMutable()); + } + e.Session.Channel.SendMessage(new MsgAllAppearances(sendData)); } - e.Session.Channel.SendMessage(new MsgAllAppearances(sendData)); + } } @@ -62,13 +65,18 @@ public ImmutableIconAppearance AddAppearance(IconAppearance appearance) { //this should only be called by the ImmutableIconAppearance's finalizer public void RemoveAppearance(ImmutableIconAppearance appearance) { + lock (_lock) { + _idToAppearance.Remove(appearance.GetHashCode()); + } RaiseNetworkEvent(new RemoveAppearanceEvent(appearance.GetHashCode())); } public ImmutableIconAppearance MustGetAppearanceByID(int appearanceId) { - if(!_idToAppearance[appearanceId].TryGetTarget(out var result)) - throw new Exception($"Deleted appearance ID ${appearanceId} in MustGetAppearanceByID()"); - return result; + lock (_lock) { + if(!_idToAppearance[appearanceId].TryGetTarget(out var result)) + throw new Exception($"Attempted to access deleted appearance ID ${appearanceId} in MustGetAppearanceByID()"); + return result; + } } public bool TryGetAppearanceByID(int appearanceId, [NotNullWhen(true)] out ImmutableIconAppearance? appearance) { From bd3f0e169fd7031635a5985a0999b422ce52696a Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sat, 12 Oct 2024 17:40:08 +0100 Subject: [PATCH 32/54] fixed it --- OpenDreamRuntime/ImmutableIconAppearance.cs | 1 + OpenDreamRuntime/Objects/Types/DreamList.cs | 12 +++++++---- .../Objects/Types/DreamObjectTurf.cs | 3 +-- .../Rendering/ServerAppearanceSystem.cs | 21 +++++++++++++++---- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/OpenDreamRuntime/ImmutableIconAppearance.cs b/OpenDreamRuntime/ImmutableIconAppearance.cs index 812b469305..9766eca1db 100644 --- a/OpenDreamRuntime/ImmutableIconAppearance.cs +++ b/OpenDreamRuntime/ImmutableIconAppearance.cs @@ -24,6 +24,7 @@ namespace OpenDreamRuntime.Rendering; // TODO: Wow this is huge! Probably look into splitting this by most used/least used to reduce the size of these public sealed class ImmutableIconAppearance : IEquatable { + [ViewVariables] public readonly string Name = string.Empty; [ViewVariables] public readonly int? Icon; [ViewVariables] public readonly string? IconState; diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index 4dfc4c9680..c92585cf6e 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -647,6 +647,8 @@ public sealed class DreamOverlaysList : DreamList { [Dependency] private readonly AtomManager _atomManager = default!; private readonly ServerAppearanceSystem? _appearanceSystem; + private List hardRefs = new(); + private readonly DreamObject _owner; private readonly bool _isUnderlays; @@ -710,8 +712,9 @@ public override void AddValue(DreamValue value) { _atomManager.UpdateAppearance(_owner, appearance => { IconAppearance? overlayAppearance = CreateOverlayAppearance(_atomManager, value, appearance.Icon); overlayAppearance ??= new IconAppearance(); - - GetOverlaysList(appearance).Add(_appearanceSystem.AddAppearance(overlayAppearance).GetHashCode()); + ImmutableIconAppearance immutableOverlay = _appearanceSystem.AddAppearance(overlayAppearance); + hardRefs.Add(immutableOverlay); + GetOverlaysList(appearance).Add(immutableOverlay.GetHashCode()); }); } @@ -723,8 +726,9 @@ public override void RemoveValue(DreamValue value) { IconAppearance? overlayAppearance = CreateOverlayAppearance(_atomManager, value, appearance.Icon); if (overlayAppearance == null) return; - - GetOverlaysList(appearance).Remove(_appearanceSystem.AddAppearance(overlayAppearance).GetHashCode()); + ImmutableIconAppearance immutableOverlay = _appearanceSystem.AddAppearance(overlayAppearance); + hardRefs.Remove(immutableOverlay); + GetOverlaysList(appearance).Remove(immutableOverlay.GetHashCode()); }); } diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs index 6562a7a5c1..3584046da7 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs @@ -14,8 +14,7 @@ public DreamObjectTurf(DreamObjectDefinition objectDefinition, int x, int y, int Z = z; Cell = cell; Contents = new TurfContentsList(ObjectTree.List.ObjectDefinition, Cell); - Appearance = AppearanceSystem!.DefaultAppearance; - + AtomManager.SetAtomAppearance(this, AtomManager.GetAppearanceFromDefinition(ObjectDefinition)); } public void SetTurfType(DreamObjectDefinition objectDefinition) { diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 9ed55d873a..6d7d754da3 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -20,10 +20,13 @@ public sealed class ServerAppearanceSystem : SharedAppearanceSystem { /// private readonly object _lock = new(); + private ISawmill _sawmill; + [Dependency] private readonly IPlayerManager _playerManager = default!; public ServerAppearanceSystem() { DefaultAppearance = new ImmutableIconAppearance(IconAppearance.Default, this); + _sawmill = Logger.GetSawmill("Appearance"); } public override void Initialize() { @@ -47,6 +50,7 @@ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { if(_idToAppearance[key].TryGetTarget(out immutable)) sendData.Add(key, immutable.ToMutable()); } + _sawmill.Debug($"Sending {sendData.Count} appearances to client"); e.Session.Channel.SendMessage(new MsgAllAppearances(sendData)); } @@ -56,19 +60,28 @@ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { public ImmutableIconAppearance AddAppearance(IconAppearance appearance) { ImmutableIconAppearance immutableAppearance = new(appearance, this); lock (_lock) { - if (_idToAppearance.TryAdd(immutableAppearance.GetHashCode(), new(immutableAppearance))) { + if(_idToAppearance.TryGetValue(immutableAppearance.GetHashCode(), out var weakReference) && weakReference.TryGetTarget(out var originalImmutable)) { + return originalImmutable; + } else { + _idToAppearance[immutableAppearance.GetHashCode()] = new(immutableAppearance); + //immutableAppearance.MarkRegistered(); RaiseNetworkEvent(new NewAppearanceEvent(immutableAppearance.GetHashCode(), immutableAppearance.ToMutable())); + _sawmill.Debug($"Created appearance ${immutableAppearance.GetHashCode()}"); + return immutableAppearance; } - return immutableAppearance; } } //this should only be called by the ImmutableIconAppearance's finalizer public void RemoveAppearance(ImmutableIconAppearance appearance) { lock (_lock) { - _idToAppearance.Remove(appearance.GetHashCode()); + //only remove if this is the exact same reference + if(_idToAppearance.TryGetValue(appearance.GetHashCode(), out var weakReference) && weakReference.TryGetTarget(out var immutableIconAppearance) && object.ReferenceEquals(immutableIconAppearance,appearance)){ + _idToAppearance.Remove(appearance.GetHashCode()); + RaiseNetworkEvent(new RemoveAppearanceEvent(appearance.GetHashCode())); + _sawmill.Debug($"Deleted appearance ${appearance.GetHashCode()}"); + } } - RaiseNetworkEvent(new RemoveAppearanceEvent(appearance.GetHashCode())); } public ImmutableIconAppearance MustGetAppearanceByID(int appearanceId) { From 15611280868fd53c0162a151e80c387917812a19 Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sat, 12 Oct 2024 18:49:15 +0100 Subject: [PATCH 33/54] tracking down weird bugs --- OpenDreamRuntime/ImmutableIconAppearance.cs | 8 +++++++- OpenDreamRuntime/Objects/Types/DreamList.cs | 4 ++-- .../Rendering/ServerAppearanceSystem.cs | 13 +++++++++---- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/OpenDreamRuntime/ImmutableIconAppearance.cs b/OpenDreamRuntime/ImmutableIconAppearance.cs index 9766eca1db..1696974de4 100644 --- a/OpenDreamRuntime/ImmutableIconAppearance.cs +++ b/OpenDreamRuntime/ImmutableIconAppearance.cs @@ -25,6 +25,7 @@ namespace OpenDreamRuntime.Rendering; public sealed class ImmutableIconAppearance : IEquatable { + bool registered = false; [ViewVariables] public readonly string Name = string.Empty; [ViewVariables] public readonly int? Icon; [ViewVariables] public readonly string? IconState; @@ -77,6 +78,10 @@ public sealed class ImmutableIconAppearance : IEquatable Date: Sat, 12 Oct 2024 19:48:16 +0100 Subject: [PATCH 34/54] half done turf & area anims --- .../Rendering/ClientAppearanceSystem.cs | 21 +++++++++----- OpenDreamRuntime/AtomManager.cs | 29 +++++++++++++++---- .../Rendering/ServerAppearanceSystem.cs | 4 +-- .../Rendering/SharedAppearanceSystem.cs | 3 +- 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs index 264bcde83d..3814514c0b 100644 --- a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs +++ b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs @@ -78,13 +78,20 @@ private void OnNewAppearance(NewAppearanceEvent e) { } private void OnAnimation(AnimationEvent e) { - EntityUid ent = _entityManager.GetEntity(e.Entity); - if (!_entityManager.TryGetComponent(ent, out var sprite)) - return; - - LoadAppearance(e.TargetAppearanceId, targetAppearance => { - sprite.Icon.StartAppearanceAnimation(targetAppearance, e.Duration, e.Easing, e.Loop, e.Flags, e.Delay, e.ChainAnim); - }); + if(e.Entity == NetEntity.Invalid && e.turfId is not null) { //it's a turf or area + if(_turfIcons.TryGetValue(e.turfId.Value, out var turfIcon)) + LoadAppearance(e.TargetAppearanceId, targetAppearance => { + turfIcon.StartAppearanceAnimation(targetAppearance, e.Duration, e.Easing, e.Loop, e.Flags, e.Delay, e.ChainAnim); + }); + } else { //image or movable + EntityUid ent = _entityManager.GetEntity(e.Entity); + if (!_entityManager.TryGetComponent(ent, out var sprite)) + return; + + LoadAppearance(e.TargetAppearanceId, targetAppearance => { + sprite.Icon.StartAppearanceAnimation(targetAppearance, e.Duration, e.Easing, e.Loop, e.Flags, e.Delay, e.ChainAnim); + }); + } } private void OnWorldAABB(EntityUid uid, DMISpriteComponent comp, ref WorldAABBEvent e) { diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index c66d2e6f22..ebc4d26453 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -535,7 +535,10 @@ public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasi //TODO: should handle filters IconAppearance appearance; EntityUid targetEntity; - DMISpriteComponent targetComponent; + DMISpriteComponent? targetComponent = null; + NetEntity ent = NetEntity.Invalid; + int? turfId = null; + if (atom is DreamObjectMovable movable) { targetEntity = movable.Entity; targetComponent = movable.SpriteComponent; @@ -544,17 +547,33 @@ public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasi targetEntity = image.Entity; targetComponent = image.SpriteComponent; appearance = targetComponent.Appearance!.ToMutable(); + } else if (atom is DreamObjectTurf turf) { + targetEntity = EntityUid.Invalid; + turfId = turf.Appearance.GetHashCode() + 1; + appearance = turf.Appearance.ToMutable(); + //} else if (atom is DreamObjectArea area) { + //?????? + // appearance = area.Appearance.ToMutable(); + // area appearance should be an overlay on turfs, so could maybe get away with animating that? } else throw new ArgumentException($"Cannot animate appearance of {atom}"); animate(appearance); - // Don't send the updated appearance to clients, they will animate it - DMISpriteSystem.SetSpriteAppearance(new(targetEntity, targetComponent), appearance, dirty: false); - NetEntity ent = _entityManager.GetNetEntity(targetEntity); - AppearanceSystem?.Animate(ent, appearance, duration, easing, loop, flags, delay, chainAnim); + if(targetComponent is not null) { + ent = _entityManager.GetNetEntity(targetEntity); + // Don't send the updated appearance to clients, they will animate it + DMISpriteSystem.SetSpriteAppearance(new(targetEntity, targetComponent), appearance, dirty: false); + } else if (atom is DreamObjectTurf turf) { + //this is basically the only time it's okay to set turf.Appearance outside of DreamMapManager.SetTurfAppearance() + //because we don't want to notify the client of the appearance change + turf.Appearance = AppearanceSystem!.AddAppearance(appearance); + } else if (atom is DreamObjectArea area) { + //fuck knows, this will trigger a bunch of turf updates to? idek + } + AppearanceSystem?.Animate(ent, appearance, duration, easing, loop, flags, delay, chainAnim, turfId); } public bool TryCreateAppearanceFrom(DreamValue value, [NotNullWhen(true)] out IconAppearance? appearance) { diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 5279f30ffc..45886671ce 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -104,9 +104,9 @@ public bool TryGetAppearanceByID(int appearanceId, [NotNullWhen(true)] out Immut } } - public void Animate(NetEntity entity, IconAppearance targetAppearance, TimeSpan duration, AnimationEasing easing, int loop, AnimationFlags flags, int delay, bool chainAnim) { + public void Animate(NetEntity entity, IconAppearance targetAppearance, TimeSpan duration, AnimationEasing easing, int loop, AnimationFlags flags, int delay, bool chainAnim, int? turfId) { int appearanceId = AddAppearance(targetAppearance).GetHashCode(); - RaiseNetworkEvent(new AnimationEvent(entity, appearanceId, duration, easing, loop, flags, delay, chainAnim)); + RaiseNetworkEvent(new AnimationEvent(entity, appearanceId, duration, easing, loop, flags, delay, chainAnim, turfId)); } } diff --git a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs index d7e95b465e..4a289eb45f 100644 --- a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs +++ b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs @@ -18,7 +18,7 @@ public sealed class RemoveAppearanceEvent(int appearanceId) : EntityEventArgs { } [Serializable, NetSerializable] - public sealed class AnimationEvent(NetEntity entity, int targetAppearanceId, TimeSpan duration, AnimationEasing easing, int loop, AnimationFlags flags, int delay, bool chainAnim) + public sealed class AnimationEvent(NetEntity entity, int targetAppearanceId, TimeSpan duration, AnimationEasing easing, int loop, AnimationFlags flags, int delay, bool chainAnim, int? turfId) : EntityEventArgs { public NetEntity Entity = entity; public int TargetAppearanceId = targetAppearanceId; @@ -28,5 +28,6 @@ public sealed class AnimationEvent(NetEntity entity, int targetAppearanceId, Tim public AnimationFlags Flags = flags; public int Delay = delay; public bool ChainAnim = chainAnim; + public int? turfId = turfId; } } From e2cd921ae015c24162eedb80386c934ea9f43847 Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sun, 13 Oct 2024 14:10:13 +0100 Subject: [PATCH 35/54] /mutable_appearance --- OpenDreamRuntime/AtomManager.cs | 17 ++++++++------ OpenDreamRuntime/Objects/Types/DreamList.cs | 3 +-- .../Objects/Types/DreamObjectImage.cs | 23 +++++++++++-------- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index ebc4d26453..81a62f855c 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -481,20 +481,20 @@ public DreamValue GetAppearanceVar(ImmutableIconAppearance appearance, string va DreamObjectTurf turf => turf.Appearance, DreamObjectMovable movable => movable.SpriteComponent.Appearance!, DreamObjectArea area => area.Appearance, - DreamObjectImage image => image.SpriteComponent.Appearance!, + DreamObjectImage image => image.IsMutableAppearance ? AppearanceSystem!.AddAppearance(image.MutableAppearance!) : image.SpriteComponent!.Appearance!, _ => throw new Exception($"Cannot get appearance of {atom}") }; } /// - /// Optionally looks up for an appearance. Does not try to create a new one when one is not found for this atom. An invalid AppearanceId is an error, but null is fine. + /// Optionally looks up for an appearance. Does not try to create a new one when one is not found for this atom. /// public bool TryGetAppearance(DreamObject atom, [NotNullWhen(true)] out ImmutableIconAppearance? appearance) { if (atom is DreamObjectTurf turf) appearance = turf.Appearance; else if (atom is DreamObjectMovable movable && movable.SpriteComponent.Appearance is not null) appearance = movable.SpriteComponent.Appearance; - else if (atom is DreamObjectImage image && image.SpriteComponent.Appearance is not null) + else if (atom is DreamObjectImage image) appearance = image.SpriteComponent.Appearance; else if (atom is DreamObjectArea area) appearance = area.Appearance; @@ -505,7 +505,7 @@ public bool TryGetAppearance(DreamObject atom, [NotNullWhen(true)] out Immutable } public void UpdateAppearance(DreamObject atom, Action update) { - ImmutableIconAppearance immutableAppearance = MustGetAppearance(atom); + ImmutableIconAppearance? immutableAppearance = MustGetAppearance(atom); IconAppearance appearance = (immutableAppearance != null) ? immutableAppearance.ToMutable() : new(); // Clone the appearance update(appearance); SetAtomAppearance(atom, appearance); @@ -517,7 +517,10 @@ public void SetAtomAppearance(DreamObject atom, IconAppearance appearance) { } else if (atom is DreamObjectMovable movable) { DMISpriteSystem.SetSpriteAppearance(new(movable.Entity, movable.SpriteComponent), appearance); } else if (atom is DreamObjectImage image) { - DMISpriteSystem.SetSpriteAppearance(new(image.Entity, image.SpriteComponent), appearance); + if(image.IsMutableAppearance) + image.MutableAppearance = appearance; + else + DMISpriteSystem.SetSpriteAppearance(new(image.Entity, image.SpriteComponent!), appearance); } else if (atom is DreamObjectArea area) { _dreamMapManager.SetAreaAppearance(area, appearance); } @@ -543,10 +546,10 @@ public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasi targetEntity = movable.Entity; targetComponent = movable.SpriteComponent; appearance = targetComponent.Appearance!.ToMutable(); - } else if (atom is DreamObjectImage image){ + } else if (atom is DreamObjectImage image && !image.IsMutableAppearance){ targetEntity = image.Entity; targetComponent = image.SpriteComponent; - appearance = targetComponent.Appearance!.ToMutable(); + appearance = targetComponent!.Appearance!.ToMutable(); } else if (atom is DreamObjectTurf turf) { targetEntity = EntityUid.Invalid; turfId = turf.Appearance.GetHashCode() + 1; diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index 4bd9b30e09..4f2b0c75e8 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -713,7 +713,7 @@ public override void AddValue(DreamValue value) { IconAppearance? overlayAppearance = CreateOverlayAppearance(_atomManager, value, appearance.Icon); overlayAppearance ??= new IconAppearance(); ImmutableIconAppearance immutableOverlay = _appearanceSystem.AddAppearance(overlayAppearance); - //hardRefs.Add(immutableOverlay); + hardRefs.Add(immutableOverlay); GetOverlaysList(appearance).Add(immutableOverlay.GetHashCode()); }); } @@ -727,7 +727,6 @@ public override void RemoveValue(DreamValue value) { if (overlayAppearance == null) return; ImmutableIconAppearance immutableOverlay = _appearanceSystem.AddAppearance(overlayAppearance); - //hardRefs.Remove(immutableOverlay); GetOverlaysList(appearance).Remove(immutableOverlay.GetHashCode()); }); } diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index c36fe96571..7789db1035 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -7,11 +7,13 @@ namespace OpenDreamRuntime.Objects.Types; public sealed class DreamObjectImage : DreamObject { public EntityUid Entity = EntityUid.Invalid; - public readonly DMISpriteComponent SpriteComponent; + public readonly DMISpriteComponent? SpriteComponent; private DreamObject? _loc; private DreamList _overlays; private DreamList _underlays; private readonly DreamList _filters; + public readonly bool IsMutableAppearance; + public IconAppearance? MutableAppearance; /// /// All the args in /image/New() after "icon" and "loc", in their correct order @@ -30,25 +32,26 @@ public DreamObjectImage(DreamObjectDefinition objectDefinition) : base(objectDef _overlays = ObjectTree.CreateList(); _underlays = ObjectTree.CreateList(); _filters = ObjectTree.CreateList(); + IsMutableAppearance = true; } else { _overlays = new DreamOverlaysList(ObjectTree.List.ObjectDefinition, this, AppearanceSystem, false); _underlays = new DreamOverlaysList(ObjectTree.List.ObjectDefinition, this, AppearanceSystem, true); _filters = new DreamFilterList(ObjectTree.List.ObjectDefinition, this); + IsMutableAppearance = false; + Entity = EntityManager.SpawnEntity(null, new MapCoordinates(0, 0, MapId.Nullspace)); //spawning an entity in nullspace means it never actually gets sent to any clients until it's placed on the map, or it gets a PVS override + SpriteComponent = EntityManager.AddComponent(Entity); } - //TODO this means we send all mutable appearances to clients, even though we don't send the entity they're attached to - Entity = EntityManager.SpawnEntity(null, new MapCoordinates(0, 0, MapId.Nullspace)); //spawning an entity in nullspace means it never actually gets sent to any clients until it's placed on the map, or it gets a PVS override - SpriteComponent = EntityManager.AddComponent(Entity); - AtomManager.SetSpriteAppearance((Entity, SpriteComponent), AtomManager.GetAppearanceFromDefinition(ObjectDefinition)); + AtomManager.SetAtomAppearance(this, AtomManager.GetAppearanceFromDefinition(ObjectDefinition)); } public override void Initialize(DreamProcArguments args) { base.Initialize(args); DreamValue icon = args.GetArgument(0); - IconAppearance? iconAppearance; - if (icon.IsNull || !AtomManager.TryCreateAppearanceFrom(icon, out iconAppearance)) { + IconAppearance iconAppearance; + if (icon.IsNull || !AtomManager.TryCreateAppearanceFrom(icon, out iconAppearance!)) { // Use a default appearance, but log a warning about it if icon wasn't null - iconAppearance = AtomManager.MustGetAppearance(this)!.ToMutable(); //object def appearance is created in the constructor + iconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this)!.ToMutable(); //object def appearance is created in the constructor if (!icon.IsNull) Logger.GetSawmill("opendream.image") .Warning($"Attempted to create an /image from {icon}. This is invalid and a default image was created instead."); @@ -203,14 +206,14 @@ protected override void SetVar(string varName, DreamValue value) { break; } case "override": { - IconAppearance iconAppearance = AtomManager.MustGetAppearance(this)!.ToMutable(); + IconAppearance iconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this)!.ToMutable(); iconAppearance.Override = value.IsTruthy(); AtomManager.SetAtomAppearance(this, iconAppearance); break; } default: if (AtomManager.IsValidAppearanceVar(varName)) { - IconAppearance iconAppearance = AtomManager.MustGetAppearance(this)!.ToMutable(); + IconAppearance iconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this)!.ToMutable(); AtomManager.SetAppearanceVar(iconAppearance, varName, value); AtomManager.SetAtomAppearance(this, iconAppearance); break; From ed74230d045383b2760c1522f55d438aa7e9be78 Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sun, 13 Oct 2024 14:39:48 +0100 Subject: [PATCH 36/54] fix colormatrix hashes --- OpenDreamShared/Dream/ColorMatrix.cs | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/OpenDreamShared/Dream/ColorMatrix.cs b/OpenDreamShared/Dream/ColorMatrix.cs index cc9d5c255f..60b39a48b6 100644 --- a/OpenDreamShared/Dream/ColorMatrix.cs +++ b/OpenDreamShared/Dream/ColorMatrix.cs @@ -263,6 +263,36 @@ public bool Equals(in ColorMatrix other) { c54 == other.c54; } + public override int GetHashCode() { + HashCode hashCode = new HashCode(); + hashCode.Add(c11); + hashCode.Add(c12); + hashCode.Add(c13); + hashCode.Add(c14); + + hashCode.Add(c21); + hashCode.Add(c22); + hashCode.Add(c23); + hashCode.Add(c24); + + hashCode.Add(c31); + hashCode.Add(c32); + hashCode.Add(c33); + hashCode.Add(c34); + + hashCode.Add(c41); + hashCode.Add(c42); + hashCode.Add(c43); + hashCode.Add(c44); + + hashCode.Add(c51); + hashCode.Add(c52); + hashCode.Add(c53); + hashCode.Add(c54); + + return hashCode.ToHashCode(); + } + /// /// Multiplies two instances. /// From babc6fe95c74a4c039bac9dee366a957522dae0e Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sun, 13 Oct 2024 17:47:17 +0100 Subject: [PATCH 37/54] enormous amount of linting --- OpenDreamRuntime/AtomManager.cs | 15 +- OpenDreamRuntime/DreamMapManager.cs | 5 +- OpenDreamRuntime/ImmutableIconAppearance.cs | 143 +++++++----------- OpenDreamRuntime/Objects/Types/DreamList.cs | 1 + .../Objects/Types/DreamObjectAtom.cs | 6 +- .../Objects/Types/DreamObjectImage.cs | 16 +- OpenDreamRuntime/Procs/DMProc.cs | 2 - .../Procs/Native/DreamProcNativeHelpers.cs | 4 +- OpenDreamRuntime/Rendering/DMISpriteSystem.cs | 2 +- .../Rendering/ServerAppearanceSystem.cs | 23 +-- .../Rendering/SharedAppearanceSystem.cs | 2 +- .../Rendering/SharedDMISpriteComponent.cs | 1 + 12 files changed, 86 insertions(+), 134 deletions(-) diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index 81a62f855c..2fc65bd567 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -476,7 +476,7 @@ public DreamValue GetAppearanceVar(ImmutableIconAppearance appearance, string va /// Gets an atom's appearance. Will throw if the appearance system is not available. /// /// The atom to find the appearance of. - public ImmutableIconAppearance? MustGetAppearance(DreamObject atom) { + public ImmutableIconAppearance MustGetAppearance(DreamObject atom) { return atom switch { DreamObjectTurf turf => turf.Appearance, DreamObjectMovable movable => movable.SpriteComponent.Appearance!, @@ -495,7 +495,7 @@ public bool TryGetAppearance(DreamObject atom, [NotNullWhen(true)] out Immutable else if (atom is DreamObjectMovable movable && movable.SpriteComponent.Appearance is not null) appearance = movable.SpriteComponent.Appearance; else if (atom is DreamObjectImage image) - appearance = image.SpriteComponent.Appearance; + appearance = image.SpriteComponent?.Appearance; else if (atom is DreamObjectArea area) appearance = area.Appearance; else @@ -505,8 +505,8 @@ public bool TryGetAppearance(DreamObject atom, [NotNullWhen(true)] out Immutable } public void UpdateAppearance(DreamObject atom, Action update) { - ImmutableIconAppearance? immutableAppearance = MustGetAppearance(atom); - IconAppearance appearance = (immutableAppearance != null) ? immutableAppearance.ToMutable() : new(); // Clone the appearance + ImmutableIconAppearance immutableAppearance = MustGetAppearance(atom); + IconAppearance appearance = immutableAppearance.ToMutable(); // Clone the appearance update(appearance); SetAtomAppearance(atom, appearance); } @@ -563,8 +563,6 @@ public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasi animate(appearance); - - if(targetComponent is not null) { ent = _entityManager.GetNetEntity(targetEntity); // Don't send the updated appearance to clients, they will animate it @@ -576,6 +574,7 @@ public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasi } else if (atom is DreamObjectArea area) { //fuck knows, this will trigger a bunch of turf updates to? idek } + AppearanceSystem?.Animate(ent, appearance, duration, easing, loop, flags, delay, chainAnim, turfId); } @@ -586,7 +585,7 @@ public bool TryCreateAppearanceFrom(DreamValue value, [NotNullWhen(true)] out Ic } if (value.TryGetValueAsDreamObject(out var copyFromImage)) { - appearance = MustGetAppearance(copyFromImage)!.ToMutable(); + appearance = MustGetAppearance(copyFromImage).ToMutable(); return true; } @@ -596,7 +595,7 @@ public bool TryCreateAppearanceFrom(DreamValue value, [NotNullWhen(true)] out Ic } if (value.TryGetValueAsDreamObject(out var copyFromAtom)) { - appearance = MustGetAppearance(copyFromAtom)?.ToMutable(); + appearance = MustGetAppearance(copyFromAtom).ToMutable(); return true; } diff --git a/OpenDreamRuntime/DreamMapManager.cs b/OpenDreamRuntime/DreamMapManager.cs index 8d8266ae2b..3f570537b8 100644 --- a/OpenDreamRuntime/DreamMapManager.cs +++ b/OpenDreamRuntime/DreamMapManager.cs @@ -12,7 +12,6 @@ using Robust.Shared.Utility; using Level = OpenDreamRuntime.IDreamMapManager.Level; using Cell = OpenDreamRuntime.IDreamMapManager.Cell; -using System.Collections; namespace OpenDreamRuntime; @@ -226,9 +225,7 @@ public void SetAreaAppearance(DreamObjectArea area, IconAppearance appearance) { if(oldToNewAppearance.TryGetValue(turf.Appearance, out var newAppearance)) turf.Appearance = newAppearance; else { - IconAppearance? turfAppearance = _atomManager.MustGetAppearance(turf)?.ToMutable(); - - if(turfAppearance is null) continue; + IconAppearance turfAppearance = _atomManager.MustGetAppearance(turf).ToMutable(); turfAppearance.Overlays.Remove(oldAppearanceId); turfAppearance.Overlays.Add(area.Appearance.GetHashCode()); diff --git a/OpenDreamRuntime/ImmutableIconAppearance.cs b/OpenDreamRuntime/ImmutableIconAppearance.cs index 1696974de4..673b174560 100644 --- a/OpenDreamRuntime/ImmutableIconAppearance.cs +++ b/OpenDreamRuntime/ImmutableIconAppearance.cs @@ -1,14 +1,6 @@ -using Robust.Shared.Maths; -using Robust.Shared.Serialization; -using Robust.Shared.ViewVariables; -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using Robust.Shared.GameObjects; -using OpenDreamShared.Dream; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; -using System.Linq; -using Pidgin; +using OpenDreamShared.Dream; namespace OpenDreamRuntime.Rendering; @@ -24,28 +16,27 @@ namespace OpenDreamRuntime.Rendering; // TODO: Wow this is huge! Probably look into splitting this by most used/least used to reduce the size of these public sealed class ImmutableIconAppearance : IEquatable { - - bool registered = false; - [ViewVariables] public readonly string Name = string.Empty; + private bool registered = false; + [ViewVariables] public readonly string Name; [ViewVariables] public readonly int? Icon; [ViewVariables] public readonly string? IconState; - [ViewVariables] public readonly AtomDirection Direction = AtomDirection.South; - [ViewVariables] public readonly bool InheritsDirection = true; // Inherits direction when used as an overlay + [ViewVariables] public readonly AtomDirection Direction; + [ViewVariables] public readonly bool InheritsDirection; // Inherits direction when used as an overlay [ViewVariables] public readonly Vector2i PixelOffset; // pixel_x and pixel_y [ViewVariables] public readonly Vector2i PixelOffset2; // pixel_w and pixel_z - [ViewVariables] public readonly Color Color = Color.White; - [ViewVariables] public readonly byte Alpha = 255; + [ViewVariables] public readonly Color Color; + [ViewVariables] public readonly byte Alpha; [ViewVariables] public readonly float GlideSize; - [ViewVariables] public readonly float Layer = -1f; - [ViewVariables] public int Plane = -32767; - [ViewVariables] public readonly BlendMode BlendMode = BlendMode.Default; - [ViewVariables] public readonly AppearanceFlags AppearanceFlags = AppearanceFlags.None; + [ViewVariables] public readonly float Layer; + [ViewVariables] public int Plane; + [ViewVariables] public readonly BlendMode BlendMode; + [ViewVariables] public readonly AppearanceFlags AppearanceFlags; [ViewVariables] public readonly sbyte Invisibility; [ViewVariables] public readonly bool Opacity; [ViewVariables] public readonly bool Override; [ViewVariables] public readonly string? RenderSource; [ViewVariables] public readonly string? RenderTarget; - [ViewVariables] public readonly MouseOpacity MouseOpacity = MouseOpacity.PixelOpaque; + [ViewVariables] public readonly MouseOpacity MouseOpacity; [ViewVariables] public readonly ImmutableIconAppearance[] Overlays; [ViewVariables] public readonly ImmutableIconAppearance[] Underlays; [ViewVariables] public readonly NetEntity[] VisContents; @@ -75,15 +66,15 @@ public sealed class ImmutableIconAppearance : IEquatable PixelOffset + PixelOffset2; - private int? storedHashCode; - private ServerAppearanceSystem appearanceSystem; + private int? _storedHashCode; + private readonly ServerAppearanceSystem appearanceSystem; public void MarkRegistered(){ registered = true; } public ImmutableIconAppearance(IconAppearance appearance, ServerAppearanceSystem serverAppearanceSystem) { - this.appearanceSystem = serverAppearanceSystem; + appearanceSystem = serverAppearanceSystem; Name = appearance.Name; Icon = appearance.Icon; @@ -109,12 +100,12 @@ public ImmutableIconAppearance(IconAppearance appearance, ServerAppearanceSystem int i = 0; Overlays = new ImmutableIconAppearance[appearance.Overlays.Count]; foreach(int overlayId in appearance.Overlays) - Overlays[i++] = serverAppearanceSystem.MustGetAppearanceByID(overlayId); + Overlays[i++] = serverAppearanceSystem.MustGetAppearanceById(overlayId); i = 0; Underlays = new ImmutableIconAppearance[appearance.Underlays.Count]; foreach(int underlayId in appearance.Underlays) - Underlays[i++] = serverAppearanceSystem.MustGetAppearanceByID(underlayId); + Underlays[i++] = serverAppearanceSystem.MustGetAppearanceById(underlayId); VisContents = appearance.VisContents.ToArray(); Filters = appearance.Filters.ToArray(); @@ -163,11 +154,11 @@ public bool Equals(ImmutableIconAppearance? immutableIconAppearance) { } for (int i = 0; i < Overlays.Length; i++) { - if (immutableIconAppearance.Overlays[i] != Overlays[i]) return false; + if (!immutableIconAppearance.Overlays[i].Equals(Overlays[i])) return false; } for (int i = 0; i < Underlays.Length; i++) { - if (immutableIconAppearance.Underlays[i] != Underlays[i]) return false; + if (!immutableIconAppearance.Underlays[i].Equals(Underlays[i])) return false; } for (int i = 0; i < VisContents.Length; i++) { @@ -185,40 +176,9 @@ public bool Equals(ImmutableIconAppearance? immutableIconAppearance) { return true; } - /// - /// This is a helper used for both optimization and parity.
- /// In BYOND, if a color matrix is representable as an RGBA color string,
- /// then it is coerced into one internally before being saved onto some appearance.
- /// This does the linear algebra madness necessary to determine whether this is the case or not. - ///
- private static bool TryRepresentMatrixAsRgbaColor(in ColorMatrix matrix, [NotNullWhen(true)] out Color? maybeColor) { - maybeColor = null; - - // The R G B A values need to be bounded [0,1] for a color conversion to work; - // anything higher implies trying to render "superblue" or something. - float diagonalSum = 0f; - foreach (float diagonalValue in matrix.GetDiagonal()) { - if (diagonalValue < 0 || diagonalValue > 1) - return false; - diagonalSum += diagonalValue; - } - - // and then all of the other values need to be zero, including the offset vector. - float sum = 0f; - foreach (float value in matrix.GetValues()) { - if (value < 0f) // To avoid situations like negatives and positives cancelling out this checksum. - return false; - sum += value; - } - - if (sum - diagonalSum == 0) // PREEETTY sure I can trust the floating-point math here. Not 100% though - maybeColor = new Color(matrix.c11, matrix.c22, matrix.c33, matrix.c44); - return maybeColor is not null; - } - public override int GetHashCode() { - if(storedHashCode is not null) //because everything is readonly, this only needs to be done once - return (int)storedHashCode; + if(_storedHashCode is not null) //because everything is readonly, this only needs to be done once + return (int)_storedHashCode; HashCode hashCode = new HashCode(); @@ -267,49 +227,52 @@ public override int GetHashCode() { hashCode.Add(Transform[i]); } - storedHashCode = hashCode.ToHashCode(); - return (int)storedHashCode; + _storedHashCode = hashCode.ToHashCode(); + return (int)_storedHashCode; } //Creates an editable *copy* of this appearance, which must be added to the ServerAppearanceSystem to be used. [Pure] public IconAppearance ToMutable() { IconAppearance result = new IconAppearance() { - Name = this.Name, - Icon = this.Icon, - IconState = this.IconState, - Direction = this.Direction, - InheritsDirection = this.InheritsDirection, - PixelOffset = this.PixelOffset, - PixelOffset2 = this.PixelOffset2, - Color = this.Color, - Alpha = this.Alpha, - GlideSize = this.GlideSize, - ColorMatrix = this.ColorMatrix, - Layer = this.Layer, - Plane = this.Plane, - RenderSource = this.RenderSource, - RenderTarget = this.RenderTarget, - BlendMode = this.BlendMode, - AppearanceFlags = this.AppearanceFlags, - Invisibility = this.Invisibility, - Opacity = this.Opacity, - MouseOpacity = this.MouseOpacity, - Overlays = new(this.Overlays.Length), - Underlays = new(this.Underlays.Length), - VisContents = new(this.VisContents), - Filters = new(this.Filters), - Verbs = new(this.Verbs), - Override = this.Override, + Name = Name, + Icon = Icon, + IconState = IconState, + Direction = Direction, + InheritsDirection = InheritsDirection, + PixelOffset = PixelOffset, + PixelOffset2 = PixelOffset2, + Color = Color, + Alpha = Alpha, + GlideSize = GlideSize, + ColorMatrix = ColorMatrix, + Layer = Layer, + Plane = Plane, + RenderSource = RenderSource, + RenderTarget = RenderTarget, + BlendMode = BlendMode, + AppearanceFlags = AppearanceFlags, + Invisibility = Invisibility, + Opacity = Opacity, + MouseOpacity = MouseOpacity, + Overlays = new(Overlays.Length), + Underlays = new(Underlays.Length), + VisContents = new(VisContents), + Filters = new(Filters), + Verbs = new(Verbs), + Override = Override, }; + foreach(ImmutableIconAppearance overlay in Overlays) result.Overlays.Add(overlay.GetHashCode()); + foreach(ImmutableIconAppearance underlay in Underlays) result.Underlays.Add(underlay.GetHashCode()); for (int i = 0; i < 6; i++) { result.Transform[i] = Transform[i]; } + return result; } diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index 4f2b0c75e8..34aea404f1 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -891,6 +891,7 @@ public int GetIndexOfFilter(DreamFilter filter) { return i; i++; } + return -1; } diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs b/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs index 8bc708435b..a42ed996ae 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs @@ -58,7 +58,7 @@ protected override bool TryGetVar(string varName, out DreamValue value) { value = (Desc != null) ? new(Desc) : DreamValue.Null; return true; case "appearance": - var appearanceCopy = AtomManager.MustGetAppearance(this)!.ToMutable(); + var appearanceCopy = AtomManager.MustGetAppearance(this).ToMutable(); value = new(appearanceCopy); return true; @@ -84,7 +84,7 @@ protected override bool TryGetVar(string varName, out DreamValue value) { default: if (AtomManager.IsValidAppearanceVar(varName)) { - var appearance = AtomManager.MustGetAppearance(this)!; + var appearance = AtomManager.MustGetAppearance(this); value = AtomManager.GetAppearanceVar(appearance, varName); return true; @@ -114,7 +114,7 @@ protected override void SetVar(string varName, DreamValue value) { return; // Ignore attempts to set an invalid appearance // The dir does not get changed - newAppearance.Direction = AtomManager.MustGetAppearance(this)!.Direction; + newAppearance.Direction = AtomManager.MustGetAppearance(this).Direction; AtomManager.SetAtomAppearance(this, newAppearance); break; diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index 7789db1035..f32d6e2aab 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -41,6 +41,7 @@ public DreamObjectImage(DreamObjectDefinition objectDefinition) : base(objectDef Entity = EntityManager.SpawnEntity(null, new MapCoordinates(0, 0, MapId.Nullspace)); //spawning an entity in nullspace means it never actually gets sent to any clients until it's placed on the map, or it gets a PVS override SpriteComponent = EntityManager.AddComponent(Entity); } + AtomManager.SetAtomAppearance(this, AtomManager.GetAppearanceFromDefinition(ObjectDefinition)); } @@ -48,10 +49,9 @@ public override void Initialize(DreamProcArguments args) { base.Initialize(args); DreamValue icon = args.GetArgument(0); - IconAppearance iconAppearance; - if (icon.IsNull || !AtomManager.TryCreateAppearanceFrom(icon, out iconAppearance!)) { + if (icon.IsNull || !AtomManager.TryCreateAppearanceFrom(icon, out var iconAppearance)) { // Use a default appearance, but log a warning about it if icon wasn't null - iconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this)!.ToMutable(); //object def appearance is created in the constructor + iconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this).ToMutable(); //object def appearance is created in the constructor if (!icon.IsNull) Logger.GetSawmill("opendream.image") .Warning($"Attempted to create an /image from {icon}. This is invalid and a default image was created instead."); @@ -98,7 +98,7 @@ protected override bool TryGetVar(string varName, out DreamValue value) { return true; default: { if (AtomManager.IsValidAppearanceVar(varName)) { - value = AtomManager.GetAppearanceVar(AtomManager.MustGetAppearance(this)!, varName); + value = AtomManager.GetAppearanceVar(AtomManager.MustGetAppearance(this), varName); return true; } else { return base.TryGetVar(varName, out value); @@ -132,7 +132,7 @@ protected override void SetVar(string varName, DreamValue value) { if (valueList != null) { _overlays = valueList.CreateCopy(); } else { - var overlay = DreamOverlaysList.CreateOverlayAppearance(AtomManager, value, AtomManager.MustGetAppearance(this)?.Icon); + var overlay = DreamOverlaysList.CreateOverlayAppearance(AtomManager, value, AtomManager.MustGetAppearance(this).Icon); if (overlay == null) return; @@ -164,7 +164,7 @@ protected override void SetVar(string varName, DreamValue value) { if (valueList != null) { _underlays = valueList.CreateCopy(); } else { - var underlay = DreamOverlaysList.CreateOverlayAppearance(AtomManager, value, AtomManager.MustGetAppearance(this)?.Icon); + var underlay = DreamOverlaysList.CreateOverlayAppearance(AtomManager, value, AtomManager.MustGetAppearance(this).Icon); if (underlay == null) return; @@ -206,14 +206,14 @@ protected override void SetVar(string varName, DreamValue value) { break; } case "override": { - IconAppearance iconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this)!.ToMutable(); + IconAppearance iconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this).ToMutable(); iconAppearance.Override = value.IsTruthy(); AtomManager.SetAtomAppearance(this, iconAppearance); break; } default: if (AtomManager.IsValidAppearanceVar(varName)) { - IconAppearance iconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this)!.ToMutable(); + IconAppearance iconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this).ToMutable(); AtomManager.SetAppearanceVar(iconAppearance, varName, value); AtomManager.SetAtomAppearance(this, iconAppearance); break; diff --git a/OpenDreamRuntime/Procs/DMProc.cs b/OpenDreamRuntime/Procs/DMProc.cs index b99a398c44..c454766cab 100644 --- a/OpenDreamRuntime/Procs/DMProc.cs +++ b/OpenDreamRuntime/Procs/DMProc.cs @@ -8,10 +8,8 @@ using OpenDreamRuntime.Objects; using OpenDreamRuntime.Objects.Types; using OpenDreamRuntime.Procs.DebugAdapter; -using OpenDreamRuntime.Rendering; using OpenDreamRuntime.Resources; using OpenDreamShared.Dream; -using Robust.Server.GameObjects; using Robust.Shared.Utility; namespace OpenDreamRuntime.Procs { diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs b/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs index fb85fda761..2f6d830e42 100644 --- a/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs +++ b/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs @@ -177,7 +177,7 @@ public static (DreamObjectAtom?, ViewRange) ResolveViewArguments(DreamManager dr if (!mapManager.TryGetCellAt((eyePos.X + deltaX, eyePos.Y + deltaY), eyePos.Z, out var cell)) continue; - var appearance = atomManager.MustGetAppearance(cell.Turf!)!; + var appearance = atomManager.MustGetAppearance(cell.Turf!); var tile = new ViewAlgorithm.Tile() { Opaque = appearance.Opacity, Luminosity = 0, @@ -186,7 +186,7 @@ public static (DreamObjectAtom?, ViewRange) ResolveViewArguments(DreamManager dr }; foreach (var movable in cell.Movables) { - appearance = atomManager.MustGetAppearance(movable)!; + appearance = atomManager.MustGetAppearance(movable); tile.Opaque |= appearance.Opacity; } diff --git a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs index 853dd0f8e0..5c7f2333a8 100644 --- a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs +++ b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs @@ -14,7 +14,7 @@ public override void Initialize() { } private void GetComponentState(EntityUid uid, DMISpriteComponent component, ref ComponentGetState args) { - args.State = new SharedDMISpriteComponent.DMISpriteComponentState(component.Appearance.GetHashCode(), component.ScreenLocation); + args.State = new SharedDMISpriteComponent.DMISpriteComponentState(component.Appearance?.GetHashCode(), component.ScreenLocation); } public void SetSpriteAppearance(Entity ent, IconAppearance appearance, bool dirty = true) { diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 45886671ce..3c0074cc3c 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -20,14 +20,11 @@ public sealed class ServerAppearanceSystem : SharedAppearanceSystem { ///
private readonly object _lock = new(); - private ISawmill _sawmill; - [Dependency] private readonly IPlayerManager _playerManager = default!; public ServerAppearanceSystem() { DefaultAppearance = new ImmutableIconAppearance(IconAppearance.Default, this); DefaultAppearance.MarkRegistered(); - _sawmill = Logger.GetSawmill("Appearance"); } public override void Initialize() { @@ -46,12 +43,13 @@ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { //todo this is probably stupid slow lock (_lock) { Dictionary sendData = new(_idToAppearance.Count); - ImmutableIconAppearance? immutable; - foreach(int key in _idToAppearance.Keys ){ + + foreach(int key in _idToAppearance.Keys){ + ImmutableIconAppearance? immutable; if(_idToAppearance[key].TryGetTarget(out immutable)) sendData.Add(key, immutable.ToMutable()); } - _sawmill.Debug($"Sending {sendData.Count} appearances to client"); + e.Session.Channel.SendMessage(new MsgAllAppearances(sendData)); } @@ -67,7 +65,6 @@ public ImmutableIconAppearance AddAppearance(IconAppearance appearance) { immutableAppearance.MarkRegistered(); _idToAppearance[immutableAppearance.GetHashCode()] = new(immutableAppearance); RaiseNetworkEvent(new NewAppearanceEvent(immutableAppearance.GetHashCode(), immutableAppearance.ToMutable())); - //_sawmill.Debug($"Created appearance ${immutableAppearance.GetHashCode()}"); return immutableAppearance; } } @@ -78,18 +75,14 @@ public void RemoveAppearance(ImmutableIconAppearance appearance) { lock (_lock) { if(_idToAppearance.TryGetValue(appearance.GetHashCode(), out var weakRef)) { //it is possible that a new appearance was created with the same hash before the GC got around to cleaning up the old one - if(weakRef.TryGetTarget(out var target) && !object.ReferenceEquals(target,appearance)) + if(weakRef.TryGetTarget(out var target) && !ReferenceEquals(target,appearance)) return; _idToAppearance.Remove(appearance.GetHashCode()); RaiseNetworkEvent(new RemoveAppearanceEvent(appearance.GetHashCode())); - _sawmill.Debug($"Deleted appearance ${appearance.GetHashCode()}"); - } else { - _sawmill.Warning($"BAD REMOVAL ${appearance.GetHashCode()}"); - } - } +} } } - public ImmutableIconAppearance MustGetAppearanceByID(int appearanceId) { + public ImmutableIconAppearance MustGetAppearanceById(int appearanceId) { lock (_lock) { if(!_idToAppearance[appearanceId].TryGetTarget(out var result)) throw new Exception($"Attempted to access deleted appearance ID ${appearanceId} in MustGetAppearanceByID()"); @@ -97,7 +90,7 @@ public ImmutableIconAppearance MustGetAppearanceByID(int appearanceId) { } } - public bool TryGetAppearanceByID(int appearanceId, [NotNullWhen(true)] out ImmutableIconAppearance? appearance) { + public bool TryGetAppearanceById(int appearanceId, [NotNullWhen(true)] out ImmutableIconAppearance? appearance) { lock (_lock) { appearance = null; return _idToAppearance.TryGetValue(appearanceId, out var appearanceRef) && appearanceRef.TryGetTarget(out appearance); diff --git a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs index 4a289eb45f..750070dd46 100644 --- a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs +++ b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs @@ -28,6 +28,6 @@ public sealed class AnimationEvent(NetEntity entity, int targetAppearanceId, Tim public AnimationFlags Flags = flags; public int Delay = delay; public bool ChainAnim = chainAnim; - public int? turfId = turfId; + public int? TurfId = turfId; } } diff --git a/OpenDreamShared/Rendering/SharedDMISpriteComponent.cs b/OpenDreamShared/Rendering/SharedDMISpriteComponent.cs index d8fdff8bc4..47d3bb60c6 100644 --- a/OpenDreamShared/Rendering/SharedDMISpriteComponent.cs +++ b/OpenDreamShared/Rendering/SharedDMISpriteComponent.cs @@ -5,6 +5,7 @@ using OpenDreamShared.Dream; namespace OpenDreamShared.Rendering; + [NetworkedComponent] public abstract partial class SharedDMISpriteComponent : Component { [Serializable, NetSerializable] From b2d76af73364d2a86ab7405d90bf869105780c64 Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sun, 13 Oct 2024 17:47:36 +0100 Subject: [PATCH 38/54] missed one --- OpenDreamClient/Rendering/ClientAppearanceSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs index 3814514c0b..3b45fd6153 100644 --- a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs +++ b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs @@ -78,8 +78,8 @@ private void OnNewAppearance(NewAppearanceEvent e) { } private void OnAnimation(AnimationEvent e) { - if(e.Entity == NetEntity.Invalid && e.turfId is not null) { //it's a turf or area - if(_turfIcons.TryGetValue(e.turfId.Value, out var turfIcon)) + if(e.Entity == NetEntity.Invalid && e.TurfId is not null) { //it's a turf or area + if(_turfIcons.TryGetValue(e.TurfId.Value, out var turfIcon)) LoadAppearance(e.TargetAppearanceId, targetAppearance => { turfIcon.StartAppearanceAnimation(targetAppearance, e.Duration, e.Easing, e.Loop, e.Flags, e.Delay, e.ChainAnim); }); From 9db67b0faa2a2bad8c5e041f86b38e05c1bb5535 Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sun, 13 Oct 2024 17:51:50 +0100 Subject: [PATCH 39/54] missed another --- OpenDreamRuntime/DreamManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenDreamRuntime/DreamManager.cs b/OpenDreamRuntime/DreamManager.cs index 3a71049ac8..a9b14b663b 100644 --- a/OpenDreamRuntime/DreamManager.cs +++ b/OpenDreamRuntime/DreamManager.cs @@ -325,7 +325,7 @@ public DreamValue LocateRef(string refString) { return new DreamValue(resource); case RefType.DreamAppearance: _appearanceSystem ??= _entitySystemManager.GetEntitySystem(); - return _appearanceSystem.TryGetAppearanceByID(refId, out ImmutableIconAppearance? appearance) + return _appearanceSystem.TryGetAppearanceById(refId, out ImmutableIconAppearance? appearance) ? new DreamValue(appearance.ToMutable()) : DreamValue.Null; case RefType.Proc: From 2674ef7ebfd27736efc38a9a91e4da45f90dcceb Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sun, 13 Oct 2024 18:12:16 +0100 Subject: [PATCH 40/54] gross --- OpenDreamRuntime/AtomManager.cs | 2 +- OpenDreamRuntime/Objects/Types/DreamObjectImage.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index 2fc65bd567..367abbb06b 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -387,7 +387,7 @@ public void SetAppearanceVar(IconAppearance appearance, string varName, DreamVal //TODO THIS IS A SUPER NASTY HACK public DreamValue GetAppearanceVar(IconAppearance appearance, string varName) { - return GetAppearanceVar(AppearanceSystem.AddAppearance(appearance), varName); + return GetAppearanceVar(new ImmutableIconAppearance(appearance, AppearanceSystem), varName); } public DreamValue GetAppearanceVar(ImmutableIconAppearance appearance, string varName) { diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index f32d6e2aab..2f857ebedc 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -98,7 +98,7 @@ protected override bool TryGetVar(string varName, out DreamValue value) { return true; default: { if (AtomManager.IsValidAppearanceVar(varName)) { - value = AtomManager.GetAppearanceVar(AtomManager.MustGetAppearance(this), varName); + value = IsMutableAppearance ? AtomManager.GetAppearanceVar(MutableAppearance!, varName) : AtomManager.GetAppearanceVar(AtomManager.MustGetAppearance(this), varName); return true; } else { return base.TryGetVar(varName, out value); From 774880ab8dcae98e2f12c8d682524c2ba54845bc Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sun, 13 Oct 2024 18:27:40 +0100 Subject: [PATCH 41/54] move tests to integrated test --- .../DMProject/Tests/icons.dmi | Bin 0 -> 268 bytes .../DMProject/Tests/image.dm | 4 +++- .../DMProject/Tests}/nonlocal_var.dm | 2 +- Content.IntegrationTests/DMProject/code.dm | 2 ++ .../DMProject/environment.dme | 2 ++ .../DMProject/Broken Tests/Image/Image.dm | 2 -- 6 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 Content.IntegrationTests/DMProject/Tests/icons.dmi rename Content.Tests/DMProject/Broken Tests/Image/subclass.dm => Content.IntegrationTests/DMProject/Tests/image.dm (85%) rename {Content.Tests/DMProject/Tests/Statements/For => Content.IntegrationTests/DMProject/Tests}/nonlocal_var.dm (86%) delete mode 100644 Content.Tests/DMProject/Broken Tests/Image/Image.dm diff --git a/Content.IntegrationTests/DMProject/Tests/icons.dmi b/Content.IntegrationTests/DMProject/Tests/icons.dmi new file mode 100644 index 0000000000000000000000000000000000000000..401b72d4316b6e94ffa3f711fe7261a20833396a GIT binary patch literal 268 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@&H$ef*8>L*{Ac(-lVRqW^XAil zf{Z0We!&b5&u*jvIq6j)5hX6E#mPmP1tppJc?=8{bArPPib}tK2`>2f^@*0ZuGYCT z=Yuzd8eBAf@JQ#pkLF2+qMqIz7REuwmyNv?<~*8oWKxJiu%@}aXJ@yu>gE*^Cybvw z^3nD>oq^vvEUlyXjIf``ZiLDnRn*sWQbN1j}`pb^B|QY{Jen^9|-S)HuG4xmX4 Mp00i_>zopr01#zkRR910 literal 0 HcmV?d00001 diff --git a/Content.Tests/DMProject/Broken Tests/Image/subclass.dm b/Content.IntegrationTests/DMProject/Tests/image.dm similarity index 85% rename from Content.Tests/DMProject/Broken Tests/Image/subclass.dm rename to Content.IntegrationTests/DMProject/Tests/image.dm index 1a8010a04a..e436c22df3 100644 --- a/Content.Tests/DMProject/Broken Tests/Image/subclass.dm +++ b/Content.IntegrationTests/DMProject/Tests/image.dm @@ -2,7 +2,9 @@ plane = 123 icon_state = "subclass" -/proc/RunTest() +/proc/test_images() + ASSERT(image('icons.dmi', "mob") != null) + var/image/test = new /image/subclass ASSERT(test.plane == 123) ASSERT(test.icon_state == "subclass") diff --git a/Content.Tests/DMProject/Tests/Statements/For/nonlocal_var.dm b/Content.IntegrationTests/DMProject/Tests/nonlocal_var.dm similarity index 86% rename from Content.Tests/DMProject/Tests/Statements/For/nonlocal_var.dm rename to Content.IntegrationTests/DMProject/Tests/nonlocal_var.dm index 8f01871b2c..e0a8432b69 100644 --- a/Content.Tests/DMProject/Tests/Statements/For/nonlocal_var.dm +++ b/Content.IntegrationTests/DMProject/Tests/nonlocal_var.dm @@ -10,6 +10,6 @@ out += dir ASSERT(out == 14) -/proc/RunTest() +/proc/test_nonlocal_var() var/mob/m = new m.dodir() diff --git a/Content.IntegrationTests/DMProject/code.dm b/Content.IntegrationTests/DMProject/code.dm index ebb99dd087..b398e04b0e 100644 --- a/Content.IntegrationTests/DMProject/code.dm +++ b/Content.IntegrationTests/DMProject/code.dm @@ -30,4 +30,6 @@ test_color_matrix() test_range() test_verb_duplicate() + test_nonlocal_var() + test_images() world.log << "IntegrationTests successful, /world/New() exiting..." \ No newline at end of file diff --git a/Content.IntegrationTests/DMProject/environment.dme b/Content.IntegrationTests/DMProject/environment.dme index a4abe97328..824fd23de1 100644 --- a/Content.IntegrationTests/DMProject/environment.dme +++ b/Content.IntegrationTests/DMProject/environment.dme @@ -3,5 +3,7 @@ #include "Tests/color_matrix.dm" #include "Tests/range.dm" #include "Tests/verb_duplicate.dm" +#include "Tests/nonlocal_var.dm" +#include "Tests/image.dm" #include "map.dmm" #include "interface.dmf" \ No newline at end of file diff --git a/Content.Tests/DMProject/Broken Tests/Image/Image.dm b/Content.Tests/DMProject/Broken Tests/Image/Image.dm deleted file mode 100644 index b50648f3e6..0000000000 --- a/Content.Tests/DMProject/Broken Tests/Image/Image.dm +++ /dev/null @@ -1,2 +0,0 @@ -/proc/RunTest() - ASSERT(image('icons.dmi', "mob") != null) \ No newline at end of file From 04bee7d6e859a26283540459bb2c900cbba30b8b Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sun, 13 Oct 2024 19:15:14 +0100 Subject: [PATCH 42/54] don't love this --- OpenDreamRuntime/Objects/Types/DreamList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index 34aea404f1..655c4820fc 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -680,7 +680,6 @@ public override void Cut(int start = 1, int end = 0) { List overlaysList = GetOverlaysList(appearance); int count = overlaysList.Count + 1; if (end == 0 || end > count) end = count; - overlaysList.RemoveRange(start - 1, end - start); }); } @@ -727,6 +726,7 @@ public override void RemoveValue(DreamValue value) { if (overlayAppearance == null) return; ImmutableIconAppearance immutableOverlay = _appearanceSystem.AddAppearance(overlayAppearance); + hardRefs.Remove(immutableOverlay); GetOverlaysList(appearance).Remove(immutableOverlay.GetHashCode()); }); } From 0485fe21ff5f517d4bb0ac27ebb54e465d4c0ab5 Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sun, 13 Oct 2024 23:01:57 +0100 Subject: [PATCH 43/54] you know what? this PR should be larger --- Content.Tests/DummyDreamMapManager.cs | 4 +- DMCompiler/DMStandard/Types/Image.dm | 2 +- .../DebugWindows/IconDebugWindow.xaml.cs | 38 +++++++++--------- .../Rendering/ClientAppearanceSystem.cs | 10 ++--- OpenDreamClient/Rendering/DreamIcon.cs | 20 +++++----- OpenDreamClient/Rendering/DreamViewOverlay.cs | 8 ++-- OpenDreamRuntime/AtomManager.cs | 28 ++++++------- OpenDreamRuntime/DreamMapManager.cs | 14 +++---- OpenDreamRuntime/DreamValue.cs | 12 +++--- OpenDreamRuntime/ImmutableIconAppearance.cs | 12 +++--- OpenDreamRuntime/Objects/Types/DreamList.cs | 14 +++---- .../Objects/Types/DreamObjectAtom.cs | 2 +- .../Objects/Types/DreamObjectImage.cs | 24 +++++------ .../Objects/Types/DreamObjectMatrix.cs | 2 +- OpenDreamRuntime/Procs/DMOpcodeHandlers.cs | 2 +- OpenDreamRuntime/Rendering/DMISpriteSystem.cs | 2 +- .../Rendering/ServerAppearanceSystem.cs | 8 ++-- ...Appearance.cs => MutableIconAppearance.cs} | 18 ++++----- .../Network/Messages/MsgAllAppearances.cs | 40 +++++++++---------- .../Rendering/SharedAppearanceSystem.cs | 5 ++- 20 files changed, 133 insertions(+), 132 deletions(-) rename OpenDreamShared/Dream/{IconAppearance.cs => MutableIconAppearance.cs} (95%) diff --git a/Content.Tests/DummyDreamMapManager.cs b/Content.Tests/DummyDreamMapManager.cs index 12bece3217..3963b2a4ed 100644 --- a/Content.Tests/DummyDreamMapManager.cs +++ b/Content.Tests/DummyDreamMapManager.cs @@ -26,9 +26,9 @@ public void InitializeAtoms(List? maps) { } public void SetTurf(DreamObjectTurf turf, DreamObjectDefinition type, DreamProcArguments creationArguments) { } - public void SetTurfAppearance(DreamObjectTurf turf, IconAppearance appearance) { } + public void SetTurfAppearance(DreamObjectTurf turf, MutableIconAppearance appearance) { } - public void SetAreaAppearance(DreamObjectArea area, IconAppearance appearance) { } + public void SetAreaAppearance(DreamObjectArea area, MutableIconAppearance appearance) { } public void SetArea(DreamObjectTurf turf, DreamObjectArea area) { } diff --git a/DMCompiler/DMStandard/Types/Image.dm b/DMCompiler/DMStandard/Types/Image.dm index 2b4a00575e..b09b2cb916 100644 --- a/DMCompiler/DMStandard/Types/Image.dm +++ b/DMCompiler/DMStandard/Types/Image.dm @@ -1,7 +1,7 @@ /image parent_type = /datum - //note these values also need to be set in IconAppearance.cs + //note these values also need to be set in MutableIconAppearance.cs var/alpha = 255 var/appearance var/appearance_flags = 0 diff --git a/OpenDreamClient/Interface/DebugWindows/IconDebugWindow.xaml.cs b/OpenDreamClient/Interface/DebugWindows/IconDebugWindow.xaml.cs index 6d8d2d273c..e106313e35 100644 --- a/OpenDreamClient/Interface/DebugWindows/IconDebugWindow.xaml.cs +++ b/OpenDreamClient/Interface/DebugWindows/IconDebugWindow.xaml.cs @@ -44,25 +44,25 @@ private void Update() { // Would be nice if we could use ViewVariables instead, but I couldn't find a nice way to do that // Would be especially nice if we could use VV to make these editable - AddPropertyIfNotDefault("Name", appearance.Name, IconAppearance.Default.Name); - AddPropertyIfNotDefault("Icon State", appearance.IconState, IconAppearance.Default.IconState); - AddPropertyIfNotDefault("Direction", appearance.Direction, IconAppearance.Default.Direction); - AddPropertyIfNotDefault("Inherits Direction", appearance.InheritsDirection, IconAppearance.Default.InheritsDirection); - AddPropertyIfNotDefault("Pixel Offset X/Y", appearance.PixelOffset, IconAppearance.Default.PixelOffset); - AddPropertyIfNotDefault("Pixel Offset W/Z", appearance.PixelOffset2, IconAppearance.Default.PixelOffset2); - AddPropertyIfNotDefault("Color", appearance.Color, IconAppearance.Default.Color); - AddPropertyIfNotDefault("Alpha", appearance.Alpha, IconAppearance.Default.Alpha); - AddPropertyIfNotDefault("Glide Size", appearance.GlideSize, IconAppearance.Default.GlideSize); - AddPropertyIfNotDefault("Layer", appearance.Layer, IconAppearance.Default.Layer); - AddPropertyIfNotDefault("Plane", appearance.Plane, IconAppearance.Default.Plane); - AddPropertyIfNotDefault("Blend Mode", appearance.BlendMode, IconAppearance.Default.BlendMode); - AddPropertyIfNotDefault("Appearance Flags", appearance.AppearanceFlags, IconAppearance.Default.AppearanceFlags); - AddPropertyIfNotDefault("Invisibility", appearance.Invisibility, IconAppearance.Default.Invisibility); - AddPropertyIfNotDefault("Opacity", appearance.Opacity, IconAppearance.Default.Opacity); - AddPropertyIfNotDefault("Override", appearance.Override, IconAppearance.Default.Override); - AddPropertyIfNotDefault("Render Source", appearance.RenderSource, IconAppearance.Default.RenderSource); - AddPropertyIfNotDefault("Render Target", appearance.RenderTarget, IconAppearance.Default.RenderTarget); - AddPropertyIfNotDefault("Mouse Opacity", appearance.MouseOpacity, IconAppearance.Default.MouseOpacity); + AddPropertyIfNotDefault("Name", appearance.Name, MutableIconAppearance.Default.Name); + AddPropertyIfNotDefault("Icon State", appearance.IconState, MutableIconAppearance.Default.IconState); + AddPropertyIfNotDefault("Direction", appearance.Direction, MutableIconAppearance.Default.Direction); + AddPropertyIfNotDefault("Inherits Direction", appearance.InheritsDirection, MutableIconAppearance.Default.InheritsDirection); + AddPropertyIfNotDefault("Pixel Offset X/Y", appearance.PixelOffset, MutableIconAppearance.Default.PixelOffset); + AddPropertyIfNotDefault("Pixel Offset W/Z", appearance.PixelOffset2, MutableIconAppearance.Default.PixelOffset2); + AddPropertyIfNotDefault("Color", appearance.Color, MutableIconAppearance.Default.Color); + AddPropertyIfNotDefault("Alpha", appearance.Alpha, MutableIconAppearance.Default.Alpha); + AddPropertyIfNotDefault("Glide Size", appearance.GlideSize, MutableIconAppearance.Default.GlideSize); + AddPropertyIfNotDefault("Layer", appearance.Layer, MutableIconAppearance.Default.Layer); + AddPropertyIfNotDefault("Plane", appearance.Plane, MutableIconAppearance.Default.Plane); + AddPropertyIfNotDefault("Blend Mode", appearance.BlendMode, MutableIconAppearance.Default.BlendMode); + AddPropertyIfNotDefault("Appearance Flags", appearance.AppearanceFlags, MutableIconAppearance.Default.AppearanceFlags); + AddPropertyIfNotDefault("Invisibility", appearance.Invisibility, MutableIconAppearance.Default.Invisibility); + AddPropertyIfNotDefault("Opacity", appearance.Opacity, MutableIconAppearance.Default.Opacity); + AddPropertyIfNotDefault("Override", appearance.Override, MutableIconAppearance.Default.Override); + AddPropertyIfNotDefault("Render Source", appearance.RenderSource, MutableIconAppearance.Default.RenderSource); + AddPropertyIfNotDefault("Render Target", appearance.RenderTarget, MutableIconAppearance.Default.RenderTarget); + AddPropertyIfNotDefault("Mouse Opacity", appearance.MouseOpacity, MutableIconAppearance.Default.MouseOpacity); foreach (var overlay in _icon.Overlays) { AddDreamIconButton(OverlaysGrid, overlay); diff --git a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs index 3b45fd6153..be742fa599 100644 --- a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs +++ b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs @@ -10,8 +10,8 @@ namespace OpenDreamClient.Rendering; internal sealed class ClientAppearanceSystem : SharedAppearanceSystem { - private Dictionary _appearances = new(); - private readonly Dictionary>> _appearanceLoadCallbacks = new(); + private Dictionary _appearances = new(); + private readonly Dictionary>> _appearanceLoadCallbacks = new(); private readonly Dictionary _turfIcons = new(); private readonly Dictionary _filterShaders = new(); @@ -35,17 +35,17 @@ public override void Shutdown() { _turfIcons.Clear(); } - public void SetAllAppearances(Dictionary appearances) { + public void SetAllAppearances(Dictionary appearances) { _appearances = appearances; - foreach (KeyValuePair pair in _appearances) { + foreach (KeyValuePair pair in _appearances) { if (_appearanceLoadCallbacks.TryGetValue(pair.Key, out var callbacks)) { foreach (var callback in callbacks) callback(pair.Value); } } } - public void LoadAppearance(int appearanceId, Action loadCallback) { + public void LoadAppearance(int appearanceId, Action loadCallback) { if (_appearances.TryGetValue(appearanceId, out var appearance)) { loadCallback(appearance); return; diff --git a/OpenDreamClient/Rendering/DreamIcon.cs b/OpenDreamClient/Rendering/DreamIcon.cs index e79761ab09..66dd84586d 100644 --- a/OpenDreamClient/Rendering/DreamIcon.cs +++ b/OpenDreamClient/Rendering/DreamIcon.cs @@ -32,7 +32,7 @@ public int AnimationFrame { } [ViewVariables] - public IconAppearance? Appearance { + public MutableIconAppearance? Appearance { get => CalculateAnimatedAppearance(); private set { if (_appearance?.Equals(value) is true) @@ -42,7 +42,7 @@ private set { UpdateIcon(); } } - private IconAppearance? _appearance; + private MutableIconAppearance? _appearance; // TODO: We could cache these per-appearance instead of per-atom public IRenderTexture? CachedTexture { @@ -122,7 +122,7 @@ public void SetAppearance(int? appearanceId, AtomDirection? parentDir = null) { appearanceSystem.LoadAppearance(appearanceId.Value, appearance => { if (parentDir != null && appearance.InheritsDirection) { - appearance = new IconAppearance(appearance) { + appearance = new MutableIconAppearance(appearance) { Direction = parentDir.Value }; } @@ -132,7 +132,7 @@ public void SetAppearance(int? appearanceId, AtomDirection? parentDir = null) { } //three things to do here, chained animations, loops and parallel animations - public void StartAppearanceAnimation(IconAppearance endingAppearance, TimeSpan duration, AnimationEasing easing, int loops, AnimationFlags flags, int delay, bool chainAnim) { + public void StartAppearanceAnimation(MutableIconAppearance endingAppearance, TimeSpan duration, AnimationEasing easing, int loops, AnimationFlags flags, int delay, bool chainAnim) { _appearance = CalculateAnimatedAppearance(); //Animation starts from the current animated appearance DateTime start = DateTime.Now; if(!chainAnim) @@ -156,7 +156,7 @@ public void StartAppearanceAnimation(IconAppearance endingAppearance, TimeSpan d _appearanceAnimations[i] = lastAnim; break; } - + _appearanceAnimations.Add(new AppearanceAnimation(start, duration, endingAppearance, easing, flags, delay, true)); } @@ -232,12 +232,12 @@ private void UpdateAnimation() { DirtyTexture(); } - private IconAppearance? CalculateAnimatedAppearance() { + private MutableIconAppearance? CalculateAnimatedAppearance() { if (_appearanceAnimations == null || _appearance == null) return _appearance; _textureDirty = true; //if we have animations, we need to recalculate the texture - IconAppearance appearance = new IconAppearance(_appearance); + MutableIconAppearance appearance = new MutableIconAppearance(_appearance); List? toRemove = null; List? toReAdd = null; for(int i = 0; i < _appearanceAnimations.Count; i++) { @@ -295,7 +295,7 @@ private void UpdateAnimation() { break; } - IconAppearance endAppearance = animation.EndAppearance; + MutableIconAppearance endAppearance = animation.EndAppearance; //non-smooth animations /* @@ -552,10 +552,10 @@ private void DirtyTexture() { CachedTexture = null; } - private struct AppearanceAnimation(DateTime start, TimeSpan duration, IconAppearance endAppearance, AnimationEasing easing, AnimationFlags flags, int delay, bool lastInSequence) { + private struct AppearanceAnimation(DateTime start, TimeSpan duration, MutableIconAppearance endAppearance, AnimationEasing easing, AnimationFlags flags, int delay, bool lastInSequence) { public readonly DateTime Start = start; public readonly TimeSpan Duration = duration; - public readonly IconAppearance EndAppearance = endAppearance; + public readonly MutableIconAppearance EndAppearance = endAppearance; public readonly AnimationEasing Easing = easing; public readonly AnimationFlags Flags = flags; public readonly int Delay = delay; diff --git a/OpenDreamClient/Rendering/DreamViewOverlay.cs b/OpenDreamClient/Rendering/DreamViewOverlay.cs index 55eb129c44..c7cfa8654d 100644 --- a/OpenDreamClient/Rendering/DreamViewOverlay.cs +++ b/OpenDreamClient/Rendering/DreamViewOverlay.cs @@ -188,7 +188,7 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u current.MouseOpacity = icon.Appearance.MouseOpacity; //reverse rotation transforms because of 180 flip from RenderTarget->world transform - Matrix3x2 iconAppearanceTransformMatrix = new Matrix3x2( + Matrix3x2 MutableIconAppearanceTransformMatrix = new Matrix3x2( icon.Appearance.Transform[0], -icon.Appearance.Transform[2], -icon.Appearance.Transform[1], icon.Appearance.Transform[3], icon.Appearance.Transform[4], icon.Appearance.Transform[5] @@ -210,9 +210,9 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u current.AlphaToApply = parentIcon.AlphaToApply * (icon.Appearance.Alpha / 255.0f); if ((icon.Appearance.AppearanceFlags & AppearanceFlags.ResetTransform) != 0 || keepTogether) //RESET_TRANSFORM - current.TransformToApply = iconAppearanceTransformMatrix; + current.TransformToApply = MutableIconAppearanceTransformMatrix; else - current.TransformToApply = iconAppearanceTransformMatrix * parentIcon.TransformToApply; + current.TransformToApply = MutableIconAppearanceTransformMatrix * parentIcon.TransformToApply; if ((icon.Appearance.Plane < -10000)) //FLOAT_PLANE - Note: yes, this really is how it works. Yes it's dumb as shit. current.Plane = parentIcon.Plane + (icon.Appearance.Plane + 32767); @@ -228,7 +228,7 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u current.ColorToApply = icon.Appearance.Color; current.ColorMatrixToApply = icon.Appearance.ColorMatrix; current.AlphaToApply = icon.Appearance.Alpha/255.0f; - current.TransformToApply = iconAppearanceTransformMatrix; + current.TransformToApply = MutableIconAppearanceTransformMatrix; current.Plane = icon.Appearance.Plane; current.Layer = Math.Max(0, icon.Appearance.Layer); //float layers are invalid for icons with no parent } diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index 367abbb06b..6218199f31 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -30,7 +30,7 @@ public sealed class AtomManager { private int _nextEmptyTurfSlot; private readonly Dictionary _entityToAtom = new(); - private readonly Dictionary _definitionAppearanceCache = new(); + private readonly Dictionary _definitionAppearanceCache = new(); private ServerAppearanceSystem? AppearanceSystem{ get { @@ -250,7 +250,7 @@ public bool IsValidAppearanceVar(string name) { } } - public void SetAppearanceVar(IconAppearance appearance, string varName, DreamValue value) { + public void SetAppearanceVar(MutableIconAppearance appearance, string varName, DreamValue value) { switch (varName) { case "name": value.TryGetValueAsString(out var name); @@ -386,7 +386,7 @@ public void SetAppearanceVar(IconAppearance appearance, string varName, DreamVal } //TODO THIS IS A SUPER NASTY HACK - public DreamValue GetAppearanceVar(IconAppearance appearance, string varName) { + public DreamValue GetAppearanceVar(MutableIconAppearance appearance, string varName) { return GetAppearanceVar(new ImmutableIconAppearance(appearance, AppearanceSystem), varName); } @@ -463,7 +463,7 @@ public DreamValue GetAppearanceVar(ImmutableIconAppearance appearance, string va return new(matrix); case "appearance": - IconAppearance appearanceCopy = appearance.ToMutable(); // Return a copy + MutableIconAppearance appearanceCopy = appearance.ToMutable(); // Return a copy return new(appearanceCopy); // TODO: overlays, underlays, filters // Those are handled separately by whatever is calling GetAppearanceVar currently @@ -504,14 +504,14 @@ public bool TryGetAppearance(DreamObject atom, [NotNullWhen(true)] out Immutable return appearance is not null; } - public void UpdateAppearance(DreamObject atom, Action update) { + public void UpdateAppearance(DreamObject atom, Action update) { ImmutableIconAppearance immutableAppearance = MustGetAppearance(atom); - IconAppearance appearance = immutableAppearance.ToMutable(); // Clone the appearance + MutableIconAppearance appearance = immutableAppearance.ToMutable(); // Clone the appearance update(appearance); SetAtomAppearance(atom, appearance); } - public void SetAtomAppearance(DreamObject atom, IconAppearance appearance) { + public void SetAtomAppearance(DreamObject atom, MutableIconAppearance appearance) { if (atom is DreamObjectTurf turf) { _dreamMapManager.SetTurfAppearance(turf, appearance); } else if (atom is DreamObjectMovable movable) { @@ -530,13 +530,13 @@ public void SetMovableScreenLoc(DreamObjectMovable movable, ScreenLocation scree DMISpriteSystem.SetSpriteScreenLocation(new(movable.Entity, movable.SpriteComponent), screenLocation); } - public void SetSpriteAppearance(Entity ent, IconAppearance appearance) { + public void SetSpriteAppearance(Entity ent, MutableIconAppearance appearance) { DMISpriteSystem.SetSpriteAppearance(ent, appearance); } - public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasing easing, int loop, AnimationFlags flags, int delay, bool chainAnim, Action animate) { + public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasing easing, int loop, AnimationFlags flags, int delay, bool chainAnim, Action animate) { //TODO: should handle filters - IconAppearance appearance; + MutableIconAppearance appearance; EntityUid targetEntity; DMISpriteComponent? targetComponent = null; NetEntity ent = NetEntity.Invalid; @@ -578,7 +578,7 @@ public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasi AppearanceSystem?.Animate(ent, appearance, duration, easing, loop, flags, delay, chainAnim, turfId); } - public bool TryCreateAppearanceFrom(DreamValue value, [NotNullWhen(true)] out IconAppearance? appearance) { + public bool TryCreateAppearanceFrom(DreamValue value, [NotNullWhen(true)] out MutableIconAppearance? appearance) { if (value.TryGetValueAsAppearance(out var copyFromAppearance)) { appearance = new(copyFromAppearance); return true; @@ -600,7 +600,7 @@ public bool TryCreateAppearanceFrom(DreamValue value, [NotNullWhen(true)] out Ic } if (_resourceManager.TryLoadIcon(value, out var iconResource)) { - appearance = new IconAppearance() { + appearance = new MutableIconAppearance() { Icon = iconResource.Id }; @@ -611,7 +611,7 @@ public bool TryCreateAppearanceFrom(DreamValue value, [NotNullWhen(true)] out Ic return false; } - public IconAppearance GetAppearanceFromDefinition(DreamObjectDefinition def) { + public MutableIconAppearance GetAppearanceFromDefinition(DreamObjectDefinition def) { if (_definitionAppearanceCache.TryGetValue(def, out var appearance)) return appearance; @@ -634,7 +634,7 @@ public IconAppearance GetAppearanceFromDefinition(DreamObjectDefinition def) { def.TryGetVariable("blend_mode", out var blendModeVar); def.TryGetVariable("appearance_flags", out var appearanceFlagsVar); - appearance = new IconAppearance(); + appearance = new MutableIconAppearance(); SetAppearanceVar(appearance, "name", nameVar); SetAppearanceVar(appearance, "icon", iconVar); SetAppearanceVar(appearance, "icon_state", stateVar); diff --git a/OpenDreamRuntime/DreamMapManager.cs b/OpenDreamRuntime/DreamMapManager.cs index 3f570537b8..18d83a18fc 100644 --- a/OpenDreamRuntime/DreamMapManager.cs +++ b/OpenDreamRuntime/DreamMapManager.cs @@ -173,7 +173,7 @@ private DreamObject SetTurf(Vector2i pos, int z, DreamObjectDefinition type, Dre cell.Area.Contents.AddValue(new(cell.Turf)); } - IconAppearance turfAppearance = _atomManager.GetAppearanceFromDefinition(cell.Turf.ObjectDefinition); + MutableIconAppearance turfAppearance = _atomManager.GetAppearanceFromDefinition(cell.Turf.ObjectDefinition); SetTurfAppearance(cell.Turf, turfAppearance); cell.Turf.InitSpawn(creationArguments); @@ -188,9 +188,9 @@ public void SetTurf(DreamObjectTurf turf, DreamObjectDefinition type, DreamProcA /// Caches the turf/area appearance pair instead of recreating and re-registering it for every turf in the game. /// This is cleared out when an area appearance changes ///
- private readonly Dictionary, IconAppearance> _turfAreaLookup = new(); + private readonly Dictionary, MutableIconAppearance> _turfAreaLookup = new(); - public void SetTurfAppearance(DreamObjectTurf turf, IconAppearance appearance) { + public void SetTurfAppearance(DreamObjectTurf turf, MutableIconAppearance appearance) { if(turf.Cell.Area.Appearance != _appearanceSystem.DefaultAppearance) if(!appearance.Overlays.Contains(turf.Cell.Area.Appearance.GetHashCode())) { if(!_turfAreaLookup.TryGetValue((appearance, turf.Cell.Area.Appearance.GetHashCode()), out var newAppearance)) { @@ -210,7 +210,7 @@ public void SetTurfAppearance(DreamObjectTurf turf, IconAppearance appearance) { turf.Appearance = immutableAppearance; } - public void SetAreaAppearance(DreamObjectArea area, IconAppearance appearance) { + public void SetAreaAppearance(DreamObjectArea area, MutableIconAppearance appearance) { //if an area changes appearance, invalidate the lookup _turfAreaLookup.Clear(); int oldAppearanceId = area.Appearance.GetHashCode(); @@ -225,7 +225,7 @@ public void SetAreaAppearance(DreamObjectArea area, IconAppearance appearance) { if(oldToNewAppearance.TryGetValue(turf.Appearance, out var newAppearance)) turf.Appearance = newAppearance; else { - IconAppearance turfAppearance = _atomManager.MustGetAppearance(turf).ToMutable(); + MutableIconAppearance turfAppearance = _atomManager.MustGetAppearance(turf).ToMutable(); turfAppearance.Overlays.Remove(oldAppearanceId); turfAppearance.Overlays.Add(area.Appearance.GetHashCode()); @@ -495,8 +495,8 @@ public Cell(DreamObjectArea area) { public void UpdateTiles(); public void SetTurf(DreamObjectTurf turf, DreamObjectDefinition type, DreamProcArguments creationArguments); - public void SetTurfAppearance(DreamObjectTurf turf, IconAppearance appearance); - public void SetAreaAppearance(DreamObjectArea area, IconAppearance appearance); + public void SetTurfAppearance(DreamObjectTurf turf, MutableIconAppearance appearance); + public void SetAreaAppearance(DreamObjectArea area, MutableIconAppearance appearance); public bool TryGetCellAt(Vector2i pos, int z, [NotNullWhen(true)] out Cell? cell); public bool TryGetTurfAt(Vector2i pos, int z, [NotNullWhen(true)] out DreamObjectTurf? turf); public void SetZLevels(int levels); diff --git a/OpenDreamRuntime/DreamValue.cs b/OpenDreamRuntime/DreamValue.cs index bc195ad83e..825ee4bd5e 100644 --- a/OpenDreamRuntime/DreamValue.cs +++ b/OpenDreamRuntime/DreamValue.cs @@ -102,7 +102,7 @@ public DreamValue(DreamProc value) { _refValue = value; } - public DreamValue(IconAppearance appearance) { + public DreamValue(MutableIconAppearance appearance) { Type = DreamValueType.Appearance; _refValue = appearance; } @@ -315,9 +315,9 @@ public DreamProc MustGetValueAsProc() { throw new InvalidCastException("Value " + this + " was not the expected type of DreamProc"); } - public bool TryGetValueAsAppearance([NotNullWhen(true)] out IconAppearance? args) { + public bool TryGetValueAsAppearance([NotNullWhen(true)] out MutableIconAppearance? args) { if (Type == DreamValueType.Appearance) { - args = Unsafe.As(_refValue)!; + args = Unsafe.As(_refValue)!; return true; } @@ -326,9 +326,9 @@ public bool TryGetValueAsAppearance([NotNullWhen(true)] out IconAppearance? args return false; } - public IconAppearance MustGetValueAsAppearance() { + public MutableIconAppearance MustGetValueAsAppearance() { if (Type == DreamValueType.Appearance) { - return Unsafe.As(_refValue)!; + return Unsafe.As(_refValue)!; } throw new InvalidCastException("Value " + this + " was not the expected type of Appearance"); } @@ -757,4 +757,4 @@ public ColorMatrix CreateCopy(ISerializationManager serializationManager, ColorM return new(source); } } -#endregion Serialization \ No newline at end of file +#endregion Serialization diff --git a/OpenDreamRuntime/ImmutableIconAppearance.cs b/OpenDreamRuntime/ImmutableIconAppearance.cs index 673b174560..91d8980e4a 100644 --- a/OpenDreamRuntime/ImmutableIconAppearance.cs +++ b/OpenDreamRuntime/ImmutableIconAppearance.cs @@ -6,10 +6,10 @@ namespace OpenDreamRuntime.Rendering; /* * Woe, weary traveler, modifying this class is not for the faint of heart. - * If you modify IconAppearance, be sure to update the following places: + * If you modify MutableIconAppearance, be sure to update the following places: * - All of the methods on ImmutableIconAppearance itself - * - IconAppearance - * - IconAppearance methods in AtomManager + * - MutableIconAppearance + * - MutableIconAppearance methods in AtomManager * - There may be others */ @@ -73,7 +73,7 @@ public void MarkRegistered(){ registered = true; } - public ImmutableIconAppearance(IconAppearance appearance, ServerAppearanceSystem serverAppearanceSystem) { + public ImmutableIconAppearance(MutableIconAppearance appearance, ServerAppearanceSystem serverAppearanceSystem) { appearanceSystem = serverAppearanceSystem; Name = appearance.Name; @@ -233,8 +233,8 @@ public override int GetHashCode() { //Creates an editable *copy* of this appearance, which must be added to the ServerAppearanceSystem to be used. [Pure] - public IconAppearance ToMutable() { - IconAppearance result = new IconAppearance() { + public MutableIconAppearance ToMutable() { + MutableIconAppearance result = new MutableIconAppearance() { Name = Name, Icon = Icon, IconState = IconState, diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index 655c4820fc..c7d4d8019b 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -709,8 +709,8 @@ public override void AddValue(DreamValue value) { return; _atomManager.UpdateAppearance(_owner, appearance => { - IconAppearance? overlayAppearance = CreateOverlayAppearance(_atomManager, value, appearance.Icon); - overlayAppearance ??= new IconAppearance(); + MutableIconAppearance? overlayAppearance = CreateOverlayAppearance(_atomManager, value, appearance.Icon); + overlayAppearance ??= new MutableIconAppearance(); ImmutableIconAppearance immutableOverlay = _appearanceSystem.AddAppearance(overlayAppearance); hardRefs.Add(immutableOverlay); GetOverlaysList(appearance).Add(immutableOverlay.GetHashCode()); @@ -722,7 +722,7 @@ public override void RemoveValue(DreamValue value) { return; _atomManager.UpdateAppearance(_owner, appearance => { - IconAppearance? overlayAppearance = CreateOverlayAppearance(_atomManager, value, appearance.Icon); + MutableIconAppearance? overlayAppearance = CreateOverlayAppearance(_atomManager, value, appearance.Icon); if (overlayAppearance == null) return; ImmutableIconAppearance immutableOverlay = _appearanceSystem.AddAppearance(overlayAppearance); @@ -736,7 +736,7 @@ public override int GetLength() { } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private List GetOverlaysList(IconAppearance appearance) => + private List GetOverlaysList(MutableIconAppearance appearance) => _isUnderlays ? appearance.Underlays : appearance.Overlays; private ImmutableIconAppearance[] GetOverlaysArray(ImmutableIconAppearance appearance) => @@ -750,11 +750,11 @@ private ImmutableIconAppearance GetAppearance() { return appearance; } - public static IconAppearance? CreateOverlayAppearance(AtomManager atomManager, DreamValue value, int? defaultIcon) { - IconAppearance overlay; + public static MutableIconAppearance? CreateOverlayAppearance(AtomManager atomManager, DreamValue value, int? defaultIcon) { + MutableIconAppearance overlay; if (value.TryGetValueAsString(out var iconState)) { - overlay = new IconAppearance() { + overlay = new MutableIconAppearance() { IconState = iconState }; overlay.Icon ??= defaultIcon; diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs b/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs index a42ed996ae..ca916a04a6 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs @@ -182,7 +182,7 @@ protected override void SetVar(string varName, DreamValue value) { // Clone the appearance // TODO: We can probably avoid cloning while the DMISpriteComponent is dirty - IconAppearance appearance = (immutableAppearance != null) ? immutableAppearance.ToMutable() : new(); + MutableIconAppearance appearance = (immutableAppearance != null) ? immutableAppearance.ToMutable() : new(); AtomManager.SetAppearanceVar(appearance, varName, value); AtomManager.SetAtomAppearance(this, appearance); diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index 2f857ebedc..e81fabb6fb 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -13,7 +13,7 @@ public sealed class DreamObjectImage : DreamObject { private DreamList _underlays; private readonly DreamList _filters; public readonly bool IsMutableAppearance; - public IconAppearance? MutableAppearance; + public MutableIconAppearance? MutableAppearance; /// /// All the args in /image/New() after "icon" and "loc", in their correct order @@ -49,9 +49,9 @@ public override void Initialize(DreamProcArguments args) { base.Initialize(args); DreamValue icon = args.GetArgument(0); - if (icon.IsNull || !AtomManager.TryCreateAppearanceFrom(icon, out var iconAppearance)) { + if (icon.IsNull || !AtomManager.TryCreateAppearanceFrom(icon, out var MutableIconAppearance)) { // Use a default appearance, but log a warning about it if icon wasn't null - iconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this).ToMutable(); //object def appearance is created in the constructor + MutableIconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this).ToMutable(); //object def appearance is created in the constructor if (!icon.IsNull) Logger.GetSawmill("opendream.image") .Warning($"Attempted to create an /image from {icon}. This is invalid and a default image was created instead."); @@ -68,16 +68,16 @@ public override void Initialize(DreamProcArguments args) { if (arg.IsNull) continue; - AtomManager.SetAppearanceVar(iconAppearance, argName, arg); + AtomManager.SetAppearanceVar(MutableIconAppearance, argName, arg); if (argName == "dir" && arg.TryGetValueAsInteger(out var argDir) && argDir > 0) { // If a dir is explicitly given in the constructor then overlays using this won't use their owner's dir // Setting dir after construction does not affect this // This is undocumented and I hate it - iconAppearance.InheritsDirection = false; + MutableIconAppearance.InheritsDirection = false; } } - AtomManager.SetAtomAppearance(this, iconAppearance); + AtomManager.SetAtomAppearance(this, MutableIconAppearance); } protected override bool TryGetVar(string varName, out DreamValue value) { @@ -206,16 +206,16 @@ protected override void SetVar(string varName, DreamValue value) { break; } case "override": { - IconAppearance iconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this).ToMutable(); - iconAppearance.Override = value.IsTruthy(); - AtomManager.SetAtomAppearance(this, iconAppearance); + MutableIconAppearance MutableIconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this).ToMutable(); + MutableIconAppearance.Override = value.IsTruthy(); + AtomManager.SetAtomAppearance(this, MutableIconAppearance); break; } default: if (AtomManager.IsValidAppearanceVar(varName)) { - IconAppearance iconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this).ToMutable(); - AtomManager.SetAppearanceVar(iconAppearance, varName, value); - AtomManager.SetAtomAppearance(this, iconAppearance); + MutableIconAppearance MutableIconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this).ToMutable(); + AtomManager.SetAppearanceVar(MutableIconAppearance, varName, value); + AtomManager.SetAtomAppearance(this, MutableIconAppearance); break; } diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectMatrix.cs b/OpenDreamRuntime/Objects/Types/DreamObjectMatrix.cs index ed1a311ddb..6645598ded 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectMatrix.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectMatrix.cs @@ -259,7 +259,7 @@ public override DreamValue OperatorRemove(DreamValue b) { #endregion Operators #region Helpers - /// Used to create a float array understandable by to be a transform. + /// Used to create a float array understandable by to be a transform. /// The matrix's values in an array, in [a,d,b,e,c,f] order. /// This will not verify that this is a /matrix public static float[] MatrixToTransformFloatArray(DreamObjectMatrix matrix) { diff --git a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs index 1ee37031ae..89945d655b 100644 --- a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs +++ b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs @@ -2616,7 +2616,7 @@ private static bool IsEqual(DreamValue first, DreamValue second) { if (!second.TryGetValueAsAppearance(out var secondValue)) return false; - IconAppearance firstValue = first.MustGetValueAsAppearance(); + MutableIconAppearance firstValue = first.MustGetValueAsAppearance(); return firstValue.Equals(secondValue); } } diff --git a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs index 5c7f2333a8..5da77d0156 100644 --- a/OpenDreamRuntime/Rendering/DMISpriteSystem.cs +++ b/OpenDreamRuntime/Rendering/DMISpriteSystem.cs @@ -17,7 +17,7 @@ private void GetComponentState(EntityUid uid, DMISpriteComponent component, ref args.State = new SharedDMISpriteComponent.DMISpriteComponentState(component.Appearance?.GetHashCode(), component.ScreenLocation); } - public void SetSpriteAppearance(Entity ent, IconAppearance appearance, bool dirty = true) { + public void SetSpriteAppearance(Entity ent, MutableIconAppearance appearance, bool dirty = true) { DMISpriteComponent component = ent.Comp; component.Appearance = _appearance?.AddAppearance(appearance); if(dirty) diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 3c0074cc3c..4df3728488 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -23,7 +23,7 @@ public sealed class ServerAppearanceSystem : SharedAppearanceSystem { [Dependency] private readonly IPlayerManager _playerManager = default!; public ServerAppearanceSystem() { - DefaultAppearance = new ImmutableIconAppearance(IconAppearance.Default, this); + DefaultAppearance = new ImmutableIconAppearance(MutableIconAppearance.Default, this); DefaultAppearance.MarkRegistered(); } @@ -42,7 +42,7 @@ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { if (e.NewStatus == SessionStatus.InGame) { //todo this is probably stupid slow lock (_lock) { - Dictionary sendData = new(_idToAppearance.Count); + Dictionary sendData = new(_idToAppearance.Count); foreach(int key in _idToAppearance.Keys){ ImmutableIconAppearance? immutable; @@ -56,7 +56,7 @@ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { } } - public ImmutableIconAppearance AddAppearance(IconAppearance appearance) { + public ImmutableIconAppearance AddAppearance(MutableIconAppearance appearance) { ImmutableIconAppearance immutableAppearance = new(appearance, this); lock (_lock) { if(_idToAppearance.TryGetValue(immutableAppearance.GetHashCode(), out var weakReference) && weakReference.TryGetTarget(out var originalImmutable)) { @@ -97,7 +97,7 @@ public bool TryGetAppearanceById(int appearanceId, [NotNullWhen(true)] out Immut } } - public void Animate(NetEntity entity, IconAppearance targetAppearance, TimeSpan duration, AnimationEasing easing, int loop, AnimationFlags flags, int delay, bool chainAnim, int? turfId) { + public void Animate(NetEntity entity, MutableIconAppearance targetAppearance, TimeSpan duration, AnimationEasing easing, int loop, AnimationFlags flags, int delay, bool chainAnim, int? turfId) { int appearanceId = AddAppearance(targetAppearance).GetHashCode(); RaiseNetworkEvent(new AnimationEvent(entity, appearanceId, duration, easing, loop, flags, delay, chainAnim, turfId)); diff --git a/OpenDreamShared/Dream/IconAppearance.cs b/OpenDreamShared/Dream/MutableIconAppearance.cs similarity index 95% rename from OpenDreamShared/Dream/IconAppearance.cs rename to OpenDreamShared/Dream/MutableIconAppearance.cs index da42bdd0e6..9a2366bf3e 100644 --- a/OpenDreamShared/Dream/IconAppearance.cs +++ b/OpenDreamShared/Dream/MutableIconAppearance.cs @@ -10,10 +10,10 @@ namespace OpenDreamShared.Dream; /* * Woe, weary traveler, modifying this class is not for the faint of heart. - * If you modify IconAppearance, be sure to update the following places: - * - All of the methods on IconAppearance itself + * If you modify MutableIconAppearance, be sure to update the following places: + * - All of the methods on MutableIconAppearance itself * - ImmutableIconAppearance - * - IconAppearance methods in AtomManager + * - MutableIconAppearance methods in AtomManager * - MsgAllAppearances * - IconDebugWindow * - There may be others @@ -21,8 +21,8 @@ namespace OpenDreamShared.Dream; // TODO: Wow this is huge! Probably look into splitting this by most used/least used to reduce the size of these [Serializable, NetSerializable] -public sealed class IconAppearance : IEquatable { - public static readonly IconAppearance Default = new(); +public sealed class MutableIconAppearance : IEquatable { + public static readonly MutableIconAppearance Default = new(); [ViewVariables] public string Name = string.Empty; [ViewVariables] public int? Icon; @@ -73,7 +73,7 @@ public sealed class IconAppearance : IEquatable { // PixelOffset2 behaves the same as PixelOffset in top-down mode, so this is used public Vector2i TotalPixelOffset => PixelOffset + PixelOffset2; - public IconAppearance() { + public MutableIconAppearance() { Overlays = new(); Underlays = new(); VisContents = new(); @@ -81,7 +81,7 @@ public IconAppearance() { Verbs = new(); } - public IconAppearance(IconAppearance appearance) { + public MutableIconAppearance(MutableIconAppearance appearance) { Name = appearance.Name; Icon = appearance.Icon; IconState = appearance.IconState; @@ -114,9 +114,9 @@ public IconAppearance(IconAppearance appearance) { } } - public override bool Equals(object? obj) => obj is IconAppearance appearance && Equals(appearance); + public override bool Equals(object? obj) => obj is MutableIconAppearance appearance && Equals(appearance); - public bool Equals(IconAppearance? appearance) { + public bool Equals(MutableIconAppearance? appearance) { if (appearance == null) return false; if (appearance.Name != Name) return false; diff --git a/OpenDreamShared/Network/Messages/MsgAllAppearances.cs b/OpenDreamShared/Network/Messages/MsgAllAppearances.cs index 4377d00aa6..f5e73bc0a1 100644 --- a/OpenDreamShared/Network/Messages/MsgAllAppearances.cs +++ b/OpenDreamShared/Network/Messages/MsgAllAppearances.cs @@ -9,7 +9,7 @@ namespace OpenDreamShared.Network.Messages; -public sealed class MsgAllAppearances(Dictionary allAppearances) : NetMessage { +public sealed class MsgAllAppearances(Dictionary allAppearances) : NetMessage { public override MsgGroups MsgGroup => MsgGroups.EntityEvent; private enum Property : byte { @@ -45,7 +45,7 @@ private enum Property : byte { End } - public Dictionary AllAppearances = allAppearances; + public Dictionary AllAppearances = allAppearances; public MsgAllAppearances() : this(new()) { } @@ -56,7 +56,7 @@ public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer AllAppearances = new(count); for (int i = 0; i < count; i++) { - var appearance = new IconAppearance(); + var appearance = new MutableIconAppearance(); var property = (Property)buffer.ReadByte(); appearanceId++; @@ -225,7 +225,7 @@ public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer lastId = pair.Key; - if (appearance.Name != IconAppearance.Default.Name) { + if (appearance.Name != MutableIconAppearance.Default.Name) { buffer.Write((byte)Property.Name); buffer.Write(appearance.Name); } @@ -240,7 +240,7 @@ public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer buffer.Write(appearance.IconState); } - if (appearance.Direction != IconAppearance.Default.Direction) { + if (appearance.Direction != MutableIconAppearance.Default.Direction) { buffer.Write((byte)Property.Direction); buffer.Write((byte)appearance.Direction); } @@ -249,71 +249,71 @@ public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer buffer.Write((byte)Property.DoesntInheritDirection); } - if (appearance.PixelOffset != IconAppearance.Default.PixelOffset) { + if (appearance.PixelOffset != MutableIconAppearance.Default.PixelOffset) { buffer.Write((byte)Property.PixelOffset); buffer.WriteVariableInt32(appearance.PixelOffset.X); buffer.WriteVariableInt32(appearance.PixelOffset.Y); } - if (appearance.PixelOffset2 != IconAppearance.Default.PixelOffset2) { + if (appearance.PixelOffset2 != MutableIconAppearance.Default.PixelOffset2) { buffer.Write((byte)Property.PixelOffset2); buffer.WriteVariableInt32(appearance.PixelOffset2.X); buffer.WriteVariableInt32(appearance.PixelOffset2.Y); } - if (appearance.Color != IconAppearance.Default.Color) { + if (appearance.Color != MutableIconAppearance.Default.Color) { buffer.Write((byte)Property.Color); buffer.Write(appearance.Color); } - if (appearance.Alpha != IconAppearance.Default.Alpha) { + if (appearance.Alpha != MutableIconAppearance.Default.Alpha) { buffer.Write((byte)Property.Alpha); buffer.Write(appearance.Alpha); } - if (!appearance.GlideSize.Equals(IconAppearance.Default.GlideSize)) { + if (!appearance.GlideSize.Equals(MutableIconAppearance.Default.GlideSize)) { buffer.Write((byte)Property.GlideSize); buffer.Write(appearance.GlideSize); } - if (!appearance.ColorMatrix.Equals(IconAppearance.Default.ColorMatrix)) { + if (!appearance.ColorMatrix.Equals(MutableIconAppearance.Default.ColorMatrix)) { buffer.Write((byte)Property.ColorMatrix); foreach (var value in appearance.ColorMatrix.GetValues()) buffer.Write(value); } - if (!appearance.Layer.Equals(IconAppearance.Default.Layer)) { + if (!appearance.Layer.Equals(MutableIconAppearance.Default.Layer)) { buffer.Write((byte)Property.Layer); buffer.Write(appearance.Layer); } - if (appearance.Plane != IconAppearance.Default.Plane) { + if (appearance.Plane != MutableIconAppearance.Default.Plane) { buffer.Write((byte)Property.Plane); buffer.WriteVariableInt32(appearance.Plane); } - if (appearance.BlendMode != IconAppearance.Default.BlendMode) { + if (appearance.BlendMode != MutableIconAppearance.Default.BlendMode) { buffer.Write((byte)Property.BlendMode); buffer.Write((byte)appearance.BlendMode); } - if (appearance.AppearanceFlags != IconAppearance.Default.AppearanceFlags) { + if (appearance.AppearanceFlags != MutableIconAppearance.Default.AppearanceFlags) { buffer.Write((byte)Property.AppearanceFlags); buffer.Write((int)appearance.AppearanceFlags); } - if (appearance.Invisibility != IconAppearance.Default.Invisibility) { + if (appearance.Invisibility != MutableIconAppearance.Default.Invisibility) { buffer.Write((byte)Property.Invisibility); buffer.Write(appearance.Invisibility); } - if (appearance.Opacity != IconAppearance.Default.Opacity) { + if (appearance.Opacity != MutableIconAppearance.Default.Opacity) { buffer.Write((byte)Property.Opacity); buffer.Write(appearance.Opacity); } - if (appearance.Override != IconAppearance.Default.Override) { + if (appearance.Override != MutableIconAppearance.Default.Override) { buffer.Write((byte)Property.Override); buffer.Write(appearance.Override); } @@ -328,7 +328,7 @@ public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer buffer.Write(appearance.RenderTarget); } - if (appearance.MouseOpacity != IconAppearance.Default.MouseOpacity) { + if (appearance.MouseOpacity != MutableIconAppearance.Default.MouseOpacity) { buffer.Write((byte)Property.MouseOpacity); buffer.Write((byte)appearance.MouseOpacity); } @@ -383,7 +383,7 @@ public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer } } - if (!appearance.Transform.SequenceEqual(IconAppearance.Default.Transform)) { + if (!appearance.Transform.SequenceEqual(MutableIconAppearance.Default.Transform)) { buffer.Write((byte)Property.Transform); for (int i = 0; i < 6; i++) { diff --git a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs index 750070dd46..3511341463 100644 --- a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs +++ b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs @@ -7,9 +7,10 @@ namespace OpenDreamShared.Rendering; public abstract class SharedAppearanceSystem : EntitySystem { [Serializable, NetSerializable] - public sealed class NewAppearanceEvent(int appearanceId, IconAppearance appearance) : EntityEventArgs { + public sealed class NewAppearanceEvent(int appearanceId, MutableIconAppearance appearance) : EntityEventArgs { public int AppearanceId { get; } = appearanceId; - public IconAppearance Appearance { get; } = appearance; + public MutableIconAppearance Appearance { get; } = appearance; + } [Serializable, NetSerializable] From 897b62d9dc36a2f986d7580fd0becdb151eae0b2 Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Mon, 14 Oct 2024 22:25:26 +0100 Subject: [PATCH 44/54] fun adventures in abusing networking --- OpenDreamClient/EntryPoint.cs | 10 + .../Rendering/ClientAppearanceSystem.cs | 16 +- OpenDreamRuntime/DreamManager.Connections.cs | 1 + OpenDreamRuntime/ImmutableIconAppearance.cs | 184 ++++++++- .../Rendering/ServerAppearanceSystem.cs | 9 +- .../Dream/IBufferableAppearance.cs | 9 + .../Dream/MutableIconAppearance.cs | 202 +++++++++- .../Network/Messages/MsgAllAppearances.cs | 371 +----------------- .../Network/Messages/MsgNewAppearance.cs | 28 ++ 9 files changed, 449 insertions(+), 381 deletions(-) create mode 100644 OpenDreamShared/Dream/IBufferableAppearance.cs create mode 100644 OpenDreamShared/Network/Messages/MsgNewAppearance.cs diff --git a/OpenDreamClient/EntryPoint.cs b/OpenDreamClient/EntryPoint.cs index b9f70af63c..dc82f7669e 100644 --- a/OpenDreamClient/EntryPoint.cs +++ b/OpenDreamClient/EntryPoint.cs @@ -86,6 +86,7 @@ public override void PostInit() { IoCManager.Resolve().Initialize(); _netManager.RegisterNetMessage(RxAllAppearances); + _netManager.RegisterNetMessage(RxNewAppearance); if (_configurationManager.GetCVar(CVars.DisplayCompat)) _dreamInterface.OpenAlert( @@ -113,6 +114,15 @@ private void RxAllAppearances(MsgAllAppearances message) { clientAppearanceSystem.SetAllAppearances(message.AllAppearances); } + private void RxNewAppearance(MsgNewAppearance message) { + if (!_entitySystemManager.TryGetEntitySystem(out var clientAppearanceSystem)) { + Logger.GetSawmill("opendream").Error("Received MsgNewAppearance before initializing entity systems"); + return; + } + + clientAppearanceSystem.OnNewAppearance(message); + } + // As of RobustToolbox v0.90.0.0 there's a TileEdgeOverlay that breaks our rendering // because we don't have an ITileDefinition for each tile. // This removes that overlay immediately after MapSystem adds it. diff --git a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs index be742fa599..cf73a9d89a 100644 --- a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs +++ b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs @@ -6,6 +6,7 @@ using OpenDreamClient.Resources; using OpenDreamClient.Resources.ResourceTypes; using Robust.Shared.Timing; +using OpenDreamShared.Network.Messages; namespace OpenDreamClient.Rendering; @@ -23,7 +24,6 @@ internal sealed class ClientAppearanceSystem : SharedAppearanceSystem { [Dependency] private readonly DMISpriteSystem _spriteSystem = default!; public override void Initialize() { - SubscribeNetworkEvent(OnNewAppearance); SubscribeNetworkEvent(e => _appearances.Remove(e.AppearanceId)); SubscribeNetworkEvent(OnAnimation); SubscribeLocalEvent(OnWorldAABB); @@ -35,12 +35,14 @@ public override void Shutdown() { _turfIcons.Clear(); } - public void SetAllAppearances(Dictionary appearances) { - _appearances = appearances; + public void SetAllAppearances(Dictionary appearances) { + _appearances = new(appearances.Count); - foreach (KeyValuePair pair in _appearances) { + foreach (KeyValuePair pair in appearances) { + MutableIconAppearance cast = (MutableIconAppearance) pair.Value; + _appearances.Add(pair.Key, cast); if (_appearanceLoadCallbacks.TryGetValue(pair.Key, out var callbacks)) { - foreach (var callback in callbacks) callback(pair.Value); + foreach (var callback in callbacks) callback(cast); } } } @@ -69,8 +71,8 @@ public DreamIcon GetTurfIcon(int turfId) { return icon; } - private void OnNewAppearance(NewAppearanceEvent e) { - _appearances[e.AppearanceId] = e.Appearance; + public void OnNewAppearance(MsgNewAppearance e) { + _appearances[e.AppearanceId] = (MutableIconAppearance)e.Appearance; if (_appearanceLoadCallbacks.TryGetValue(e.AppearanceId, out var callbacks)) { foreach (var callback in callbacks) callback(_appearances[e.AppearanceId]); diff --git a/OpenDreamRuntime/DreamManager.Connections.cs b/OpenDreamRuntime/DreamManager.Connections.cs index c3c6929016..b8bc9cee8d 100644 --- a/OpenDreamRuntime/DreamManager.Connections.cs +++ b/OpenDreamRuntime/DreamManager.Connections.cs @@ -71,6 +71,7 @@ private void InitializeConnectionManager() { _netManager.RegisterNetMessage(); _netManager.RegisterNetMessage(); _netManager.RegisterNetMessage(); + _netManager.RegisterNetMessage(); var topicPort = _config.GetCVar(OpenDreamCVars.TopicPort); var worldTopicAddress = new IPEndPoint(IPAddress.Loopback, topicPort); diff --git a/OpenDreamRuntime/ImmutableIconAppearance.cs b/OpenDreamRuntime/ImmutableIconAppearance.cs index 91d8980e4a..d25c026550 100644 --- a/OpenDreamRuntime/ImmutableIconAppearance.cs +++ b/OpenDreamRuntime/ImmutableIconAppearance.cs @@ -1,6 +1,11 @@ using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; +using System.IO; +using Lidgren.Network; +using Robust.Shared.Network; using OpenDreamShared.Dream; +using Robust.Shared.Serialization; +using System.Linq; namespace OpenDreamRuntime.Rendering; @@ -15,7 +20,7 @@ namespace OpenDreamRuntime.Rendering; // TODO: Wow this is huge! Probably look into splitting this by most used/least used to reduce the size of these -public sealed class ImmutableIconAppearance : IEquatable { +public sealed class ImmutableIconAppearance : IEquatable, IBufferableAppearance { private bool registered = false; [ViewVariables] public readonly string Name; [ViewVariables] public readonly int? Icon; @@ -276,6 +281,183 @@ public MutableIconAppearance ToMutable() { return result; } + public void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) { + buffer.Write((byte)IconAppearanceProperty.Id); + buffer.WriteVariableInt32(GetHashCode()); + + if (Name != MutableIconAppearance.Default.Name) { + buffer.Write((byte)IconAppearanceProperty.Name); + buffer.Write(Name); + } + + if (Icon != null) { + buffer.Write((byte)IconAppearanceProperty.Icon); + buffer.WriteVariableInt32(Icon.Value); + } + + if (IconState != null) { + buffer.Write((byte)IconAppearanceProperty.IconState); + buffer.Write(IconState); + } + + if (Direction != MutableIconAppearance.Default.Direction) { + buffer.Write((byte)IconAppearanceProperty.Direction); + buffer.Write((byte)Direction); + } + + if (InheritsDirection != true) { + buffer.Write((byte)IconAppearanceProperty.DoesntInheritDirection); + } + + if (PixelOffset != MutableIconAppearance.Default.PixelOffset) { + buffer.Write((byte)IconAppearanceProperty.PixelOffset); + buffer.WriteVariableInt32(PixelOffset.X); + buffer.WriteVariableInt32(PixelOffset.Y); + } + + if (PixelOffset2 != MutableIconAppearance.Default.PixelOffset2) { + buffer.Write((byte)IconAppearanceProperty.PixelOffset2); + buffer.WriteVariableInt32(PixelOffset2.X); + buffer.WriteVariableInt32(PixelOffset2.Y); + } + + if (Color != MutableIconAppearance.Default.Color) { + buffer.Write((byte)IconAppearanceProperty.Color); + buffer.Write(Color); + } + + if (Alpha != MutableIconAppearance.Default.Alpha) { + buffer.Write((byte)IconAppearanceProperty.Alpha); + buffer.Write(Alpha); + } + + if (!GlideSize.Equals(MutableIconAppearance.Default.GlideSize)) { + buffer.Write((byte)IconAppearanceProperty.GlideSize); + buffer.Write(GlideSize); + } + + if (!ColorMatrix.Equals(MutableIconAppearance.Default.ColorMatrix)) { + buffer.Write((byte)IconAppearanceProperty.ColorMatrix); + + foreach (var value in ColorMatrix.GetValues()) + buffer.Write(value); + } + + if (!Layer.Equals(MutableIconAppearance.Default.Layer)) { + buffer.Write((byte)IconAppearanceProperty.Layer); + buffer.Write(Layer); + } + + if (Plane != MutableIconAppearance.Default.Plane) { + buffer.Write((byte)IconAppearanceProperty.Plane); + buffer.WriteVariableInt32(Plane); + } + + if (BlendMode != MutableIconAppearance.Default.BlendMode) { + buffer.Write((byte)IconAppearanceProperty.BlendMode); + buffer.Write((byte)BlendMode); + } + + if (AppearanceFlags != MutableIconAppearance.Default.AppearanceFlags) { + buffer.Write((byte)IconAppearanceProperty.AppearanceFlags); + buffer.Write((int)AppearanceFlags); + } + + if (Invisibility != MutableIconAppearance.Default.Invisibility) { + buffer.Write((byte)IconAppearanceProperty.Invisibility); + buffer.Write(Invisibility); + } + + if (Opacity != MutableIconAppearance.Default.Opacity) { + buffer.Write((byte)IconAppearanceProperty.Opacity); + buffer.Write(Opacity); + } + + if (Override != MutableIconAppearance.Default.Override) { + buffer.Write((byte)IconAppearanceProperty.Override); + buffer.Write(Override); + } + + if (!string.IsNullOrWhiteSpace(RenderSource)) { + buffer.Write((byte)IconAppearanceProperty.RenderSource); + buffer.Write(RenderSource); + } + + if (!string.IsNullOrWhiteSpace(RenderTarget)) { + buffer.Write((byte)IconAppearanceProperty.RenderTarget); + buffer.Write(RenderTarget); + } + + if (MouseOpacity != MutableIconAppearance.Default.MouseOpacity) { + buffer.Write((byte)IconAppearanceProperty.MouseOpacity); + buffer.Write((byte)MouseOpacity); + } + + if (Overlays.Length != 0) { + buffer.Write((byte)IconAppearanceProperty.Overlays); + + buffer.WriteVariableInt32(Overlays.Length); + foreach (var overlay in Overlays) { + buffer.WriteVariableInt32(overlay.GetHashCode()); + } + } + + if (Underlays.Length != 0) { + buffer.Write((byte)IconAppearanceProperty.Underlays); + + buffer.WriteVariableInt32(Underlays.Length); + foreach (var underlay in Underlays) { + buffer.WriteVariableInt32(underlay.GetHashCode()); + } + } + + if (VisContents.Length != 0) { + buffer.Write((byte)IconAppearanceProperty.VisContents); + + buffer.WriteVariableInt32(VisContents.Length); + foreach (var item in VisContents) { + buffer.Write(item); + } + } + + if (Filters.Length != 0) { + buffer.Write((byte)IconAppearanceProperty.Filters); + + buffer.Write(Filters.Length); + foreach (var filter in Filters) { + using var filterStream = new MemoryStream(); + + serializer.Serialize(filterStream, filter); + buffer.WriteVariableInt32((int)filterStream.Length); + filterStream.TryGetBuffer(out var filterBuffer); + buffer.Write(filterBuffer); + } + } + + if (Verbs.Length != 0) { + buffer.Write((byte)IconAppearanceProperty.Verbs); + + buffer.WriteVariableInt32(Verbs.Length); + foreach (var verb in Verbs) { + buffer.WriteVariableInt32(verb); + } + } + + if (!Transform.SequenceEqual(MutableIconAppearance.Default.Transform)) { + buffer.Write((byte)IconAppearanceProperty.Transform); + + for (int i = 0; i < 6; i++) { + buffer.Write(Transform[i]); + } + } + + buffer.Write((byte)IconAppearanceProperty.End); + } + + public int ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) { + throw new NotImplementedException(); + } + ~ImmutableIconAppearance() { if(registered) appearanceSystem.RemoveAppearance(this); diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 4df3728488..b0a12e97a6 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -5,6 +5,8 @@ using System.Diagnostics.CodeAnalysis; using OpenDreamShared.Network.Messages; using Robust.Shared.Player; +using Robust.Server.GameObjects; +using Robust.Shared.Network; namespace OpenDreamRuntime.Rendering; @@ -14,6 +16,7 @@ public sealed class ServerAppearanceSystem : SharedAppearanceSystem { private readonly Dictionary> _idToAppearance = new(); public readonly ImmutableIconAppearance DefaultAppearance; + [Dependency] private readonly IServerNetManager _networkManager = default!; /// /// This system is used by the PVS thread, we need to be thread-safe @@ -42,12 +45,12 @@ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { if (e.NewStatus == SessionStatus.InGame) { //todo this is probably stupid slow lock (_lock) { - Dictionary sendData = new(_idToAppearance.Count); + Dictionary sendData = new(_idToAppearance.Count); foreach(int key in _idToAppearance.Keys){ ImmutableIconAppearance? immutable; if(_idToAppearance[key].TryGetTarget(out immutable)) - sendData.Add(key, immutable.ToMutable()); + sendData.Add(key, immutable); } e.Session.Channel.SendMessage(new MsgAllAppearances(sendData)); @@ -64,7 +67,7 @@ public ImmutableIconAppearance AddAppearance(MutableIconAppearance appearance) { } else { immutableAppearance.MarkRegistered(); _idToAppearance[immutableAppearance.GetHashCode()] = new(immutableAppearance); - RaiseNetworkEvent(new NewAppearanceEvent(immutableAppearance.GetHashCode(), immutableAppearance.ToMutable())); + _networkManager.ServerSendToAll(new MsgNewAppearance(immutableAppearance)); return immutableAppearance; } } diff --git a/OpenDreamShared/Dream/IBufferableAppearance.cs b/OpenDreamShared/Dream/IBufferableAppearance.cs new file mode 100644 index 0000000000..cf42993e35 --- /dev/null +++ b/OpenDreamShared/Dream/IBufferableAppearance.cs @@ -0,0 +1,9 @@ +using Lidgren.Network; +using Robust.Shared.Serialization; + +namespace OpenDreamShared.Dream; + +public interface IBufferableAppearance { + public int ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer); + public void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer); +} diff --git a/OpenDreamShared/Dream/MutableIconAppearance.cs b/OpenDreamShared/Dream/MutableIconAppearance.cs index 9a2366bf3e..ef0eaf6ddb 100644 --- a/OpenDreamShared/Dream/MutableIconAppearance.cs +++ b/OpenDreamShared/Dream/MutableIconAppearance.cs @@ -5,6 +5,9 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Robust.Shared.GameObjects; +using Lidgren.Network; +using Robust.Shared.Network; +using System.IO; namespace OpenDreamShared.Dream; @@ -13,15 +16,15 @@ namespace OpenDreamShared.Dream; * If you modify MutableIconAppearance, be sure to update the following places: * - All of the methods on MutableIconAppearance itself * - ImmutableIconAppearance - * - MutableIconAppearance methods in AtomManager + * - IconAppearance methods in AtomManager * - MsgAllAppearances * - IconDebugWindow + * - IconAppearanceProperty enum * - There may be others */ // TODO: Wow this is huge! Probably look into splitting this by most used/least used to reduce the size of these -[Serializable, NetSerializable] -public sealed class MutableIconAppearance : IEquatable { +public sealed class MutableIconAppearance : IEquatable, IBufferableAppearance { public static readonly MutableIconAppearance Default = new(); [ViewVariables] public string Name = string.Empty; @@ -281,6 +284,165 @@ public void SetColor(in ColorMatrix matrix) { Color = Color.White; ColorMatrix = matrix; } + + + public int ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) { + int? appearanceId = null; + var property = (IconAppearanceProperty)buffer.ReadByte(); + while (property != IconAppearanceProperty.End) { + switch (property) { + case IconAppearanceProperty.Name: + Name = buffer.ReadString(); + break; + case IconAppearanceProperty.Id: + appearanceId = buffer.ReadVariableInt32(); + break; + case IconAppearanceProperty.Icon: + Icon = buffer.ReadVariableInt32(); + break; + case IconAppearanceProperty.IconState: + IconState = buffer.ReadString(); + break; + case IconAppearanceProperty.Direction: + Direction = (AtomDirection)buffer.ReadByte(); + break; + case IconAppearanceProperty.DoesntInheritDirection: + InheritsDirection = false; + break; + case IconAppearanceProperty.PixelOffset: + PixelOffset = (buffer.ReadVariableInt32(), buffer.ReadVariableInt32()); + break; + case IconAppearanceProperty.PixelOffset2: + PixelOffset2 = (buffer.ReadVariableInt32(), buffer.ReadVariableInt32()); + break; + case IconAppearanceProperty.Color: + Color = buffer.ReadColor(); + break; + case IconAppearanceProperty.Alpha: + Alpha = buffer.ReadByte(); + break; + case IconAppearanceProperty.GlideSize: + GlideSize = buffer.ReadFloat(); + break; + case IconAppearanceProperty.Layer: + Layer = buffer.ReadFloat(); + break; + case IconAppearanceProperty.Plane: + Plane = buffer.ReadVariableInt32(); + break; + case IconAppearanceProperty.BlendMode: + BlendMode = (BlendMode)buffer.ReadByte(); + break; + case IconAppearanceProperty.AppearanceFlags: + AppearanceFlags = (AppearanceFlags)buffer.ReadInt32(); + break; + case IconAppearanceProperty.Invisibility: + Invisibility = buffer.ReadSByte(); + break; + case IconAppearanceProperty.Opacity: + Opacity = buffer.ReadBoolean(); + break; + case IconAppearanceProperty.Override: + Override = buffer.ReadBoolean(); + break; + case IconAppearanceProperty.RenderSource: + RenderSource = buffer.ReadString(); + break; + case IconAppearanceProperty.RenderTarget: + RenderTarget = buffer.ReadString(); + break; + case IconAppearanceProperty.MouseOpacity: + MouseOpacity = (MouseOpacity)buffer.ReadByte(); + break; + case IconAppearanceProperty.ColorMatrix: + ColorMatrix = new( + buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), + buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), + buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), + buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), + buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle() + ); + + break; + case IconAppearanceProperty.Overlays: { + var overlaysCount = buffer.ReadVariableInt32(); + + Overlays.EnsureCapacity(overlaysCount); + for (int overlaysI = 0; overlaysI < overlaysCount; overlaysI++) { + Overlays.Add(buffer.ReadVariableInt32()); + } + + break; + } + case IconAppearanceProperty.Underlays: { + var underlaysCount = buffer.ReadVariableInt32(); + + Underlays.EnsureCapacity(underlaysCount); + for (int underlaysI = 0; underlaysI < underlaysCount; underlaysI++) { + Underlays.Add(buffer.ReadVariableInt32()); + } + + break; + } + case IconAppearanceProperty.VisContents: { + var visContentsCount = buffer.ReadVariableInt32(); + + VisContents.EnsureCapacity(visContentsCount); + for (int visContentsI = 0; visContentsI < visContentsCount; visContentsI++) { + VisContents.Add(buffer.ReadNetEntity()); + } + + break; + } + case IconAppearanceProperty.Filters: { + var filtersCount = buffer.ReadInt32(); + + Filters.EnsureCapacity(filtersCount); + for (int filtersI = 0; filtersI < filtersCount; filtersI++) { + var filterLength = buffer.ReadVariableInt32(); + var filterData = buffer.ReadBytes(filterLength); + using var filterStream = new MemoryStream(filterData); + var filter = serializer.Deserialize(filterStream); + + Filters.Add(filter); + } + + break; + } + case IconAppearanceProperty.Verbs: { + var verbsCount = buffer.ReadVariableInt32(); + + Verbs.EnsureCapacity(verbsCount); + for (int verbsI = 0; verbsI < verbsCount; verbsI++) { + Verbs.Add(buffer.ReadVariableInt32()); + } + + break; + } + case IconAppearanceProperty.Transform: { + Transform = [ + buffer.ReadSingle(), buffer.ReadSingle(), + buffer.ReadSingle(), buffer.ReadSingle(), + buffer.ReadSingle(), buffer.ReadSingle() + ]; + + break; + } + default: + throw new Exception($"Invalid property {property}"); + } + + property = (IconAppearanceProperty)buffer.ReadByte(); + } + if(appearanceId is not null) + return appearanceId.Value; + else + throw new Exception("No appearance ID found in buffer"); + } + + public void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) { + throw new NotImplementedException(); + } } public enum BlendMode { @@ -334,3 +496,37 @@ public enum AnimationFlags { AnimationRelative = 256, AnimationContinue = 512 } + +//used for encoding for netmessages +public enum IconAppearanceProperty : byte { + Name, + Icon, + IconState, + Direction, + DoesntInheritDirection, + PixelOffset, + PixelOffset2, + Color, + Alpha, + GlideSize, + ColorMatrix, + Layer, + Plane, + BlendMode, + AppearanceFlags, + Invisibility, + Opacity, + Override, + RenderSource, + RenderTarget, + MouseOpacity, + Overlays, + Underlays, + VisContents, + Filters, + Verbs, + Transform, + + Id, + End + } diff --git a/OpenDreamShared/Network/Messages/MsgAllAppearances.cs b/OpenDreamShared/Network/Messages/MsgAllAppearances.cs index f5e73bc0a1..05b22d7877 100644 --- a/OpenDreamShared/Network/Messages/MsgAllAppearances.cs +++ b/OpenDreamShared/Network/Messages/MsgAllAppearances.cs @@ -9,389 +9,26 @@ namespace OpenDreamShared.Network.Messages; -public sealed class MsgAllAppearances(Dictionary allAppearances) : NetMessage { +public sealed class MsgAllAppearances(Dictionary allAppearances) : NetMessage { public override MsgGroups MsgGroup => MsgGroups.EntityEvent; - - private enum Property : byte { - Name, - Icon, - IconState, - Direction, - DoesntInheritDirection, - PixelOffset, - PixelOffset2, - Color, - Alpha, - GlideSize, - ColorMatrix, - Layer, - Plane, - BlendMode, - AppearanceFlags, - Invisibility, - Opacity, - Override, - RenderSource, - RenderTarget, - MouseOpacity, - Overlays, - Underlays, - VisContents, - Filters, - Verbs, - Transform, - - Id, - End - } - - public Dictionary AllAppearances = allAppearances; - + public Dictionary AllAppearances = allAppearances; public MsgAllAppearances() : this(new()) { } public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) { var count = buffer.ReadInt32(); - var appearanceId = -1; - AllAppearances = new(count); for (int i = 0; i < count; i++) { var appearance = new MutableIconAppearance(); - var property = (Property)buffer.ReadByte(); - - appearanceId++; - - while (property != Property.End) { - switch (property) { - case Property.Name: - appearance.Name = buffer.ReadString(); - break; - case Property.Id: - appearanceId = buffer.ReadVariableInt32(); - break; - case Property.Icon: - appearance.Icon = buffer.ReadVariableInt32(); - break; - case Property.IconState: - appearance.IconState = buffer.ReadString(); - break; - case Property.Direction: - appearance.Direction = (AtomDirection)buffer.ReadByte(); - break; - case Property.DoesntInheritDirection: - appearance.InheritsDirection = false; - break; - case Property.PixelOffset: - appearance.PixelOffset = (buffer.ReadVariableInt32(), buffer.ReadVariableInt32()); - break; - case Property.PixelOffset2: - appearance.PixelOffset2 = (buffer.ReadVariableInt32(), buffer.ReadVariableInt32()); - break; - case Property.Color: - appearance.Color = buffer.ReadColor(); - break; - case Property.Alpha: - appearance.Alpha = buffer.ReadByte(); - break; - case Property.GlideSize: - appearance.GlideSize = buffer.ReadFloat(); - break; - case Property.Layer: - appearance.Layer = buffer.ReadFloat(); - break; - case Property.Plane: - appearance.Plane = buffer.ReadVariableInt32(); - break; - case Property.BlendMode: - appearance.BlendMode = (BlendMode)buffer.ReadByte(); - break; - case Property.AppearanceFlags: - appearance.AppearanceFlags = (AppearanceFlags)buffer.ReadInt32(); - break; - case Property.Invisibility: - appearance.Invisibility = buffer.ReadSByte(); - break; - case Property.Opacity: - appearance.Opacity = buffer.ReadBoolean(); - break; - case Property.Override: - appearance.Override = buffer.ReadBoolean(); - break; - case Property.RenderSource: - appearance.RenderSource = buffer.ReadString(); - break; - case Property.RenderTarget: - appearance.RenderTarget = buffer.ReadString(); - break; - case Property.MouseOpacity: - appearance.MouseOpacity = (MouseOpacity)buffer.ReadByte(); - break; - case Property.ColorMatrix: - appearance.ColorMatrix = new( - buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), - buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), - buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), - buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), - buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle() - ); - - break; - case Property.Overlays: { - var overlaysCount = buffer.ReadVariableInt32(); - - appearance.Overlays.EnsureCapacity(overlaysCount); - for (int overlaysI = 0; overlaysI < overlaysCount; overlaysI++) { - appearance.Overlays.Add(buffer.ReadVariableInt32()); - } - - break; - } - case Property.Underlays: { - var underlaysCount = buffer.ReadVariableInt32(); - - appearance.Underlays.EnsureCapacity(underlaysCount); - for (int underlaysI = 0; underlaysI < underlaysCount; underlaysI++) { - appearance.Underlays.Add(buffer.ReadVariableInt32()); - } - - break; - } - case Property.VisContents: { - var visContentsCount = buffer.ReadVariableInt32(); - - appearance.VisContents.EnsureCapacity(visContentsCount); - for (int visContentsI = 0; visContentsI < visContentsCount; visContentsI++) { - appearance.VisContents.Add(buffer.ReadNetEntity()); - } - - break; - } - case Property.Filters: { - var filtersCount = buffer.ReadInt32(); - - appearance.Filters.EnsureCapacity(filtersCount); - for (int filtersI = 0; filtersI < filtersCount; filtersI++) { - var filterLength = buffer.ReadVariableInt32(); - var filterData = buffer.ReadBytes(filterLength); - using var filterStream = new MemoryStream(filterData); - var filter = serializer.Deserialize(filterStream); - - appearance.Filters.Add(filter); - } - - break; - } - case Property.Verbs: { - var verbsCount = buffer.ReadVariableInt32(); - - appearance.Verbs.EnsureCapacity(verbsCount); - for (int verbsI = 0; verbsI < verbsCount; verbsI++) { - appearance.Verbs.Add(buffer.ReadVariableInt32()); - } - - break; - } - case Property.Transform: { - appearance.Transform = [ - buffer.ReadSingle(), buffer.ReadSingle(), - buffer.ReadSingle(), buffer.ReadSingle(), - buffer.ReadSingle(), buffer.ReadSingle() - ]; - - break; - } - default: - throw new Exception($"Invalid property {property}"); - } - - property = (Property)buffer.ReadByte(); - } - + var appearanceId = appearance.ReadFromBuffer(buffer, serializer); AllAppearances.Add(appearanceId, appearance); } } public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) { - int lastId = -1; - buffer.Write(AllAppearances.Count); foreach (var pair in AllAppearances) { - var appearance = pair.Value; - - if (pair.Key != lastId + 1) { - buffer.Write((byte)Property.Id); - buffer.WriteVariableInt32(pair.Key); - } - - lastId = pair.Key; - - if (appearance.Name != MutableIconAppearance.Default.Name) { - buffer.Write((byte)Property.Name); - buffer.Write(appearance.Name); - } - - if (appearance.Icon != null) { - buffer.Write((byte)Property.Icon); - buffer.WriteVariableInt32(appearance.Icon.Value); - } - - if (appearance.IconState != null) { - buffer.Write((byte)Property.IconState); - buffer.Write(appearance.IconState); - } - - if (appearance.Direction != MutableIconAppearance.Default.Direction) { - buffer.Write((byte)Property.Direction); - buffer.Write((byte)appearance.Direction); - } - - if (appearance.InheritsDirection != true) { - buffer.Write((byte)Property.DoesntInheritDirection); - } - - if (appearance.PixelOffset != MutableIconAppearance.Default.PixelOffset) { - buffer.Write((byte)Property.PixelOffset); - buffer.WriteVariableInt32(appearance.PixelOffset.X); - buffer.WriteVariableInt32(appearance.PixelOffset.Y); - } - - if (appearance.PixelOffset2 != MutableIconAppearance.Default.PixelOffset2) { - buffer.Write((byte)Property.PixelOffset2); - buffer.WriteVariableInt32(appearance.PixelOffset2.X); - buffer.WriteVariableInt32(appearance.PixelOffset2.Y); - } - - if (appearance.Color != MutableIconAppearance.Default.Color) { - buffer.Write((byte)Property.Color); - buffer.Write(appearance.Color); - } - - if (appearance.Alpha != MutableIconAppearance.Default.Alpha) { - buffer.Write((byte)Property.Alpha); - buffer.Write(appearance.Alpha); - } - - if (!appearance.GlideSize.Equals(MutableIconAppearance.Default.GlideSize)) { - buffer.Write((byte)Property.GlideSize); - buffer.Write(appearance.GlideSize); - } - - if (!appearance.ColorMatrix.Equals(MutableIconAppearance.Default.ColorMatrix)) { - buffer.Write((byte)Property.ColorMatrix); - - foreach (var value in appearance.ColorMatrix.GetValues()) - buffer.Write(value); - } - - if (!appearance.Layer.Equals(MutableIconAppearance.Default.Layer)) { - buffer.Write((byte)Property.Layer); - buffer.Write(appearance.Layer); - } - - if (appearance.Plane != MutableIconAppearance.Default.Plane) { - buffer.Write((byte)Property.Plane); - buffer.WriteVariableInt32(appearance.Plane); - } - - if (appearance.BlendMode != MutableIconAppearance.Default.BlendMode) { - buffer.Write((byte)Property.BlendMode); - buffer.Write((byte)appearance.BlendMode); - } - - if (appearance.AppearanceFlags != MutableIconAppearance.Default.AppearanceFlags) { - buffer.Write((byte)Property.AppearanceFlags); - buffer.Write((int)appearance.AppearanceFlags); - } - - if (appearance.Invisibility != MutableIconAppearance.Default.Invisibility) { - buffer.Write((byte)Property.Invisibility); - buffer.Write(appearance.Invisibility); - } - - if (appearance.Opacity != MutableIconAppearance.Default.Opacity) { - buffer.Write((byte)Property.Opacity); - buffer.Write(appearance.Opacity); - } - - if (appearance.Override != MutableIconAppearance.Default.Override) { - buffer.Write((byte)Property.Override); - buffer.Write(appearance.Override); - } - - if (!string.IsNullOrWhiteSpace(appearance.RenderSource)) { - buffer.Write((byte)Property.RenderSource); - buffer.Write(appearance.RenderSource); - } - - if (!string.IsNullOrWhiteSpace(appearance.RenderTarget)) { - buffer.Write((byte)Property.RenderTarget); - buffer.Write(appearance.RenderTarget); - } - - if (appearance.MouseOpacity != MutableIconAppearance.Default.MouseOpacity) { - buffer.Write((byte)Property.MouseOpacity); - buffer.Write((byte)appearance.MouseOpacity); - } - - if (appearance.Overlays.Count != 0) { - buffer.Write((byte)Property.Overlays); - - buffer.WriteVariableInt32(appearance.Overlays.Count); - foreach (var overlay in appearance.Overlays) { - buffer.WriteVariableInt32(overlay); - } - } - - if (appearance.Underlays.Count != 0) { - buffer.Write((byte)Property.Underlays); - - buffer.WriteVariableInt32(appearance.Underlays.Count); - foreach (var underlay in appearance.Underlays) { - buffer.WriteVariableInt32(underlay); - } - } - - if (appearance.VisContents.Count != 0) { - buffer.Write((byte)Property.VisContents); - - buffer.WriteVariableInt32(appearance.VisContents.Count); - foreach (var item in appearance.VisContents) { - buffer.Write(item); - } - } - - if (appearance.Filters.Count != 0) { - buffer.Write((byte)Property.Filters); - - buffer.Write(appearance.Filters.Count); - foreach (var filter in appearance.Filters) { - using var filterStream = new MemoryStream(); - - serializer.Serialize(filterStream, filter); - buffer.WriteVariableInt32((int)filterStream.Length); - filterStream.TryGetBuffer(out var filterBuffer); - buffer.Write(filterBuffer); - } - } - - if (appearance.Verbs.Count != 0) { - buffer.Write((byte)Property.Verbs); - - buffer.WriteVariableInt32(appearance.Verbs.Count); - foreach (var verb in appearance.Verbs) { - buffer.WriteVariableInt32(verb); - } - } - - if (!appearance.Transform.SequenceEqual(MutableIconAppearance.Default.Transform)) { - buffer.Write((byte)Property.Transform); - - for (int i = 0; i < 6; i++) { - buffer.Write(appearance.Transform[i]); - } - } - - buffer.Write((byte)Property.End); + pair.Value.WriteToBuffer(buffer,serializer); } } } diff --git a/OpenDreamShared/Network/Messages/MsgNewAppearance.cs b/OpenDreamShared/Network/Messages/MsgNewAppearance.cs new file mode 100644 index 0000000000..2cf81f17ac --- /dev/null +++ b/OpenDreamShared/Network/Messages/MsgNewAppearance.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Lidgren.Network; +using OpenDreamShared.Dream; +using Robust.Shared.Network; +using Robust.Shared.Serialization; + +namespace OpenDreamShared.Network.Messages; + +public sealed class MsgNewAppearance: NetMessage { + public override MsgGroups MsgGroup => MsgGroups.EntityEvent; + public MsgNewAppearance() : this(MutableIconAppearance.Default) { } + public MsgNewAppearance(IBufferableAppearance appearance) => Appearance = appearance; + public IBufferableAppearance Appearance; + public int AppearanceId; + + public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) { + Appearance = new MutableIconAppearance(); + AppearanceId = Appearance.ReadFromBuffer(buffer, serializer); + } + + public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) { + AppearanceId = Appearance.GetHashCode(); + Appearance.WriteToBuffer(buffer,serializer); + } +} From b1f59e129610dde4659d37340fea69dcfed69230 Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Mon, 14 Oct 2024 22:52:36 +0100 Subject: [PATCH 45/54] idk why this is necessary --- OpenDreamShared/Dream/MutableIconAppearance.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenDreamShared/Dream/MutableIconAppearance.cs b/OpenDreamShared/Dream/MutableIconAppearance.cs index ef0eaf6ddb..124c2c1257 100644 --- a/OpenDreamShared/Dream/MutableIconAppearance.cs +++ b/OpenDreamShared/Dream/MutableIconAppearance.cs @@ -24,6 +24,7 @@ namespace OpenDreamShared.Dream; */ // TODO: Wow this is huge! Probably look into splitting this by most used/least used to reduce the size of these +[Serializable] public sealed class MutableIconAppearance : IEquatable, IBufferableAppearance { public static readonly MutableIconAppearance Default = new(); From 16ca9de926f4d9326cd83efa1361e2d133c02fb1 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Wed, 16 Oct 2024 14:06:28 +0100 Subject: [PATCH 46/54] overlays are confusing --- OpenDreamRuntime/AtomManager.cs | 2 +- OpenDreamRuntime/ImmutableIconAppearance.cs | 3 ++ OpenDreamRuntime/Objects/Types/DreamList.cs | 37 +++++++------------ .../Rendering/ServerAppearanceSystem.cs | 8 +++- .../Dream/MutableIconAppearance.cs | 1 + 5 files changed, 25 insertions(+), 26 deletions(-) diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index 6218199f31..eb4645dd16 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -481,7 +481,7 @@ public ImmutableIconAppearance MustGetAppearance(DreamObject atom) { DreamObjectTurf turf => turf.Appearance, DreamObjectMovable movable => movable.SpriteComponent.Appearance!, DreamObjectArea area => area.Appearance, - DreamObjectImage image => image.IsMutableAppearance ? AppearanceSystem!.AddAppearance(image.MutableAppearance!) : image.SpriteComponent!.Appearance!, + DreamObjectImage image => image.IsMutableAppearance ? AppearanceSystem!.AddAppearance(image.MutableAppearance!, RegisterApearance: false) : image.SpriteComponent!.Appearance!, _ => throw new Exception($"Cannot get appearance of {atom}") }; } diff --git a/OpenDreamRuntime/ImmutableIconAppearance.cs b/OpenDreamRuntime/ImmutableIconAppearance.cs index d25c026550..6d26a0c8db 100644 --- a/OpenDreamRuntime/ImmutableIconAppearance.cs +++ b/OpenDreamRuntime/ImmutableIconAppearance.cs @@ -6,6 +6,7 @@ using OpenDreamShared.Dream; using Robust.Shared.Serialization; using System.Linq; +using System.Diagnostics; namespace OpenDreamRuntime.Rendering; @@ -278,6 +279,8 @@ public MutableIconAppearance ToMutable() { result.Transform[i] = Transform[i]; } + //THIS MUST MATCH, IT NOT MATCHING MEANS APPEARANCES ARE TOTALLY BROKEN + Debug.Assert(result.GetHashCode() == this.GetHashCode()); return result; } diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index c7d4d8019b..b1b952227f 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -646,9 +646,6 @@ private int[] GetVerbs() { public sealed class DreamOverlaysList : DreamList { [Dependency] private readonly AtomManager _atomManager = default!; private readonly ServerAppearanceSystem? _appearanceSystem; - - private List hardRefs = new(); - private readonly DreamObject _owner; private readonly bool _isUnderlays; @@ -688,7 +685,7 @@ public override DreamValue GetValue(DreamValue key) { if (!key.TryGetValueAsInteger(out var overlayIndex) || overlayIndex < 1) throw new Exception($"Invalid index into {(_isUnderlays ? "underlays" : "overlays")} list: {key}"); - ImmutableIconAppearance appearance = GetAppearance(); + ImmutableIconAppearance appearance = _atomManager.MustGetAppearance(_owner); var overlaysList = GetOverlaysArray(appearance); if (overlayIndex > overlaysList.Length) throw new Exception($"Atom only has {overlaysList.Length} {(_isUnderlays ? "underlay" : "overlay")}(s), cannot index {overlayIndex}"); @@ -708,11 +705,13 @@ public override void AddValue(DreamValue value) { if (_appearanceSystem == null) return; + MutableIconAppearance? overlayAppearance = CreateOverlayAppearance(_atomManager, value, _atomManager.MustGetAppearance(_owner).Icon); + overlayAppearance ??= new MutableIconAppearance(); + ImmutableIconAppearance immutableOverlay = _appearanceSystem.AddAppearance(overlayAppearance); + + //after UpdateApparance is done, the atom is set with a new immutable appearance containing a hard ref to the overlay + //only /mutable_appearance handles it differently, and that's done in DreamObjectImage _atomManager.UpdateAppearance(_owner, appearance => { - MutableIconAppearance? overlayAppearance = CreateOverlayAppearance(_atomManager, value, appearance.Icon); - overlayAppearance ??= new MutableIconAppearance(); - ImmutableIconAppearance immutableOverlay = _appearanceSystem.AddAppearance(overlayAppearance); - hardRefs.Add(immutableOverlay); GetOverlaysList(appearance).Add(immutableOverlay.GetHashCode()); }); } @@ -721,35 +720,27 @@ public override void RemoveValue(DreamValue value) { if (_appearanceSystem == null) return; + MutableIconAppearance? overlayAppearance = CreateOverlayAppearance(_atomManager, value, _atomManager.MustGetAppearance(_owner).Icon); + if (overlayAppearance == null) + return; + _atomManager.UpdateAppearance(_owner, appearance => { - MutableIconAppearance? overlayAppearance = CreateOverlayAppearance(_atomManager, value, appearance.Icon); - if (overlayAppearance == null) - return; - ImmutableIconAppearance immutableOverlay = _appearanceSystem.AddAppearance(overlayAppearance); - hardRefs.Remove(immutableOverlay); - GetOverlaysList(appearance).Remove(immutableOverlay.GetHashCode()); + GetOverlaysList(appearance).Remove(overlayAppearance.GetHashCode()); //because the hashcode for mutable == immutable, we can use it directly }); } public override int GetLength() { - return GetOverlaysArray(GetAppearance()).Length; + return GetOverlaysArray(_atomManager.MustGetAppearance(_owner)).Length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private List GetOverlaysList(MutableIconAppearance appearance) => _isUnderlays ? appearance.Underlays : appearance.Overlays; + [MethodImpl(MethodImplOptions.AggressiveInlining)] private ImmutableIconAppearance[] GetOverlaysArray(ImmutableIconAppearance appearance) => _isUnderlays ? appearance.Underlays : appearance.Overlays; - private ImmutableIconAppearance GetAppearance() { - var appearance = _atomManager.MustGetAppearance(_owner); - if (appearance == null) - throw new Exception("Atom has no appearance"); - - return appearance; - } - public static MutableIconAppearance? CreateOverlayAppearance(AtomManager atomManager, DreamValue value, int? defaultIcon) { MutableIconAppearance overlay; diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index b0a12e97a6..7aa2ab9244 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -7,6 +7,7 @@ using Robust.Shared.Player; using Robust.Server.GameObjects; using Robust.Shared.Network; +using System.Diagnostics; namespace OpenDreamRuntime.Rendering; @@ -28,6 +29,7 @@ public sealed class ServerAppearanceSystem : SharedAppearanceSystem { public ServerAppearanceSystem() { DefaultAppearance = new ImmutableIconAppearance(MutableIconAppearance.Default, this); DefaultAppearance.MarkRegistered(); + Debug.Assert(DefaultAppearance.GetHashCode() == MutableIconAppearance.Default.GetHashCode()); } public override void Initialize() { @@ -59,16 +61,18 @@ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { } } - public ImmutableIconAppearance AddAppearance(MutableIconAppearance appearance) { + public ImmutableIconAppearance AddAppearance(MutableIconAppearance appearance, bool RegisterApearance = true) { ImmutableIconAppearance immutableAppearance = new(appearance, this); lock (_lock) { if(_idToAppearance.TryGetValue(immutableAppearance.GetHashCode(), out var weakReference) && weakReference.TryGetTarget(out var originalImmutable)) { return originalImmutable; - } else { + } else if (RegisterApearance) { immutableAppearance.MarkRegistered(); _idToAppearance[immutableAppearance.GetHashCode()] = new(immutableAppearance); _networkManager.ServerSendToAll(new MsgNewAppearance(immutableAppearance)); return immutableAppearance; + } else { + return immutableAppearance; } } } diff --git a/OpenDreamShared/Dream/MutableIconAppearance.cs b/OpenDreamShared/Dream/MutableIconAppearance.cs index 124c2c1257..9af5205dfe 100644 --- a/OpenDreamShared/Dream/MutableIconAppearance.cs +++ b/OpenDreamShared/Dream/MutableIconAppearance.cs @@ -208,6 +208,7 @@ private static bool TryRepresentMatrixAsRgbaColor(in ColorMatrix matrix, [NotNul return maybeColor is not null; } + //it is *ESSENTIAL* that this matches the hashcode of the equivelant ImmutableIconAppearance. There's a debug assert and everything. public override int GetHashCode() { HashCode hashCode = new HashCode(); From 43913164ceff7fcb42ef4a58545576401bbb3c1e Mon Sep 17 00:00:00 2001 From: amylizzle Date: Wed, 16 Oct 2024 16:31:08 +0100 Subject: [PATCH 47/54] Overlays are immutable --- .../Rendering/ClientAppearanceSystem.cs | 22 ++++++- OpenDreamClient/Rendering/DreamIcon.cs | 8 +-- OpenDreamRuntime/DreamMapManager.cs | 10 ++-- OpenDreamRuntime/Objects/Types/DreamList.cs | 8 +-- .../Objects/Types/DreamObjectArea.cs | 1 + .../Objects/Types/DreamObjectTurf.cs | 1 + .../Rendering/DMISpriteComponent.cs | 2 +- .../Rendering/ServerAppearanceSystem.cs | 6 +- .../Dream}/ImmutableIconAppearance.cs | 42 ++++++-------- .../Dream/MutableIconAppearance.cs | 58 +++++++++++++++---- .../Rendering/SharedAppearanceSystem.cs | 4 ++ 11 files changed, 109 insertions(+), 53 deletions(-) rename {OpenDreamRuntime => OpenDreamShared/Dream}/ImmutableIconAppearance.cs (93%) diff --git a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs index cf73a9d89a..62010ee816 100644 --- a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs +++ b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs @@ -39,10 +39,15 @@ public void SetAllAppearances(Dictionary appearances _appearances = new(appearances.Count); foreach (KeyValuePair pair in appearances) { - MutableIconAppearance cast = (MutableIconAppearance) pair.Value; - _appearances.Add(pair.Key, cast); + _appearances.Add(pair.Key, (MutableIconAppearance) pair.Value); + } + //need to do this because all overlays can't be resolved until the whole appearance table is populated + foreach(KeyValuePair pair in _appearances) { + pair.Value.ResolveOverlays(this); + if(pair.Value.GetHashCode() != pair.Key) + Logger.GetSawmill("opendream.appearance").Error($"SetAllAppearances: Appearance ID and Hash DO NOT MATCH. THIS IS REALLY BAD. {pair.Value.GetHashCode()} != {pair.Key}"); if (_appearanceLoadCallbacks.TryGetValue(pair.Key, out var callbacks)) { - foreach (var callback in callbacks) callback(cast); + foreach (var callback in callbacks) callback(pair.Value); } } } @@ -73,6 +78,9 @@ public DreamIcon GetTurfIcon(int turfId) { public void OnNewAppearance(MsgNewAppearance e) { _appearances[e.AppearanceId] = (MutableIconAppearance)e.Appearance; + _appearances[e.AppearanceId].ResolveOverlays(this); + if(_appearances[e.AppearanceId].GetHashCode() != e.AppearanceId) + throw new Exception($"Appearance ID and Hash DO NOT MATCH. THIS IS REALLY BAD. {_appearances[e.AppearanceId].GetHashCode()} != {e.AppearanceId}"); if (_appearanceLoadCallbacks.TryGetValue(e.AppearanceId, out var callbacks)) { foreach (var callback in callbacks) callback(_appearances[e.AppearanceId]); @@ -208,4 +216,12 @@ public ShaderInstance GetFilterShader(DreamFilter filter, Dictionary GetValues() { public override void Cut(int start = 1, int end = 0) { _atomManager.UpdateAppearance(_owner, appearance => { - List overlaysList = GetOverlaysList(appearance); + var overlaysList = GetOverlaysList(appearance); int count = overlaysList.Count + 1; if (end == 0 || end > count) end = count; overlaysList.RemoveRange(start - 1, end - start); @@ -712,7 +712,7 @@ public override void AddValue(DreamValue value) { //after UpdateApparance is done, the atom is set with a new immutable appearance containing a hard ref to the overlay //only /mutable_appearance handles it differently, and that's done in DreamObjectImage _atomManager.UpdateAppearance(_owner, appearance => { - GetOverlaysList(appearance).Add(immutableOverlay.GetHashCode()); + GetOverlaysList(appearance).Add(immutableOverlay); }); } @@ -725,7 +725,7 @@ public override void RemoveValue(DreamValue value) { return; _atomManager.UpdateAppearance(_owner, appearance => { - GetOverlaysList(appearance).Remove(overlayAppearance.GetHashCode()); //because the hashcode for mutable == immutable, we can use it directly + GetOverlaysList(appearance).Remove(_appearanceSystem.MustGetAppearanceById(overlayAppearance.GetHashCode())); //because the hashcode for mutable == immutable, we can use it directly }); } @@ -734,7 +734,7 @@ public override int GetLength() { } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private List GetOverlaysList(MutableIconAppearance appearance) => + private List GetOverlaysList(MutableIconAppearance appearance) => _isUnderlays ? appearance.Underlays : appearance.Overlays; [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs b/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs index 24f1633075..6898aedb48 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs @@ -1,4 +1,5 @@ using OpenDreamRuntime.Rendering; +using OpenDreamShared.Dream; namespace OpenDreamRuntime.Objects.Types; diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs index 3584046da7..9350b42c3c 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs @@ -1,4 +1,5 @@ using OpenDreamRuntime.Rendering; +using OpenDreamShared.Dream; namespace OpenDreamRuntime.Objects.Types; diff --git a/OpenDreamRuntime/Rendering/DMISpriteComponent.cs b/OpenDreamRuntime/Rendering/DMISpriteComponent.cs index 68ec90b618..477dbb325b 100644 --- a/OpenDreamRuntime/Rendering/DMISpriteComponent.cs +++ b/OpenDreamRuntime/Rendering/DMISpriteComponent.cs @@ -9,7 +9,7 @@ public sealed partial class DMISpriteComponent : SharedDMISpriteComponent { [Access(typeof(DMISpriteSystem))] public ScreenLocation ScreenLocation; - [Access(typeof(DMISpriteSystem))] + //[Access(typeof(DMISpriteSystem))] [ViewVariables] public ImmutableIconAppearance? Appearance; } diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 7aa2ab9244..c8bea1af7d 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -8,6 +8,7 @@ using Robust.Server.GameObjects; using Robust.Shared.Network; using System.Diagnostics; +using Robust.Shared.Utility; namespace OpenDreamRuntime.Rendering; @@ -63,6 +64,7 @@ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { public ImmutableIconAppearance AddAppearance(MutableIconAppearance appearance, bool RegisterApearance = true) { ImmutableIconAppearance immutableAppearance = new(appearance, this); + DebugTools.Assert(appearance.GetHashCode() == immutableAppearance.GetHashCode()); lock (_lock) { if(_idToAppearance.TryGetValue(immutableAppearance.GetHashCode(), out var weakReference) && weakReference.TryGetTarget(out var originalImmutable)) { return originalImmutable; @@ -78,7 +80,7 @@ public ImmutableIconAppearance AddAppearance(MutableIconAppearance appearance, b } //this should only be called by the ImmutableIconAppearance's finalizer - public void RemoveAppearance(ImmutableIconAppearance appearance) { + public override void RemoveAppearance(ImmutableIconAppearance appearance) { lock (_lock) { if(_idToAppearance.TryGetValue(appearance.GetHashCode(), out var weakRef)) { //it is possible that a new appearance was created with the same hash before the GC got around to cleaning up the old one @@ -89,7 +91,7 @@ public void RemoveAppearance(ImmutableIconAppearance appearance) { } } } - public ImmutableIconAppearance MustGetAppearanceById(int appearanceId) { + public override ImmutableIconAppearance MustGetAppearanceById(int appearanceId) { lock (_lock) { if(!_idToAppearance[appearanceId].TryGetTarget(out var result)) throw new Exception($"Attempted to access deleted appearance ID ${appearanceId} in MustGetAppearanceByID()"); diff --git a/OpenDreamRuntime/ImmutableIconAppearance.cs b/OpenDreamShared/Dream/ImmutableIconAppearance.cs similarity index 93% rename from OpenDreamRuntime/ImmutableIconAppearance.cs rename to OpenDreamShared/Dream/ImmutableIconAppearance.cs index 6d26a0c8db..0dd896e03a 100644 --- a/OpenDreamRuntime/ImmutableIconAppearance.cs +++ b/OpenDreamShared/Dream/ImmutableIconAppearance.cs @@ -1,14 +1,15 @@ -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; +using System.Diagnostics.Contracts; using System.IO; using Lidgren.Network; using Robust.Shared.Network; -using OpenDreamShared.Dream; using Robust.Shared.Serialization; using System.Linq; -using System.Diagnostics; +using Robust.Shared.ViewVariables; +using Robust.Shared.Maths; +using System; +using OpenDreamShared.Rendering; -namespace OpenDreamRuntime.Rendering; +namespace OpenDreamShared.Dream; /* * Woe, weary traveler, modifying this class is not for the faint of heart. @@ -20,7 +21,7 @@ namespace OpenDreamRuntime.Rendering; */ // TODO: Wow this is huge! Probably look into splitting this by most used/least used to reduce the size of these - +[Serializable] public sealed class ImmutableIconAppearance : IEquatable, IBufferableAppearance { private bool registered = false; [ViewVariables] public readonly string Name; @@ -45,7 +46,7 @@ public sealed class ImmutableIconAppearance : IEquatable PixelOffset + PixelOffset2; private int? _storedHashCode; - private readonly ServerAppearanceSystem appearanceSystem; + private readonly SharedAppearanceSystem appearanceSystem; public void MarkRegistered(){ registered = true; } - public ImmutableIconAppearance(MutableIconAppearance appearance, ServerAppearanceSystem serverAppearanceSystem) { + public ImmutableIconAppearance(MutableIconAppearance appearance, SharedAppearanceSystem serverAppearanceSystem) { appearanceSystem = serverAppearanceSystem; Name = appearance.Name; @@ -103,22 +104,15 @@ public ImmutableIconAppearance(MutableIconAppearance appearance, ServerAppearanc Opacity = appearance.Opacity; MouseOpacity = appearance.MouseOpacity; - int i = 0; - Overlays = new ImmutableIconAppearance[appearance.Overlays.Count]; - foreach(int overlayId in appearance.Overlays) - Overlays[i++] = serverAppearanceSystem.MustGetAppearanceById(overlayId); - - i = 0; - Underlays = new ImmutableIconAppearance[appearance.Underlays.Count]; - foreach(int underlayId in appearance.Underlays) - Underlays[i++] = serverAppearanceSystem.MustGetAppearanceById(underlayId); + Overlays = appearance.Overlays.ToArray(); + Underlays = appearance.Underlays.ToArray(); VisContents = appearance.VisContents.ToArray(); Filters = appearance.Filters.ToArray(); Verbs = appearance.Verbs.ToArray(); Override = appearance.Override; - for (i = 0; i < 6; i++) { + for (int i = 0; i < 6; i++) { Transform[i] = appearance.Transform[i]; } } @@ -210,11 +204,11 @@ public override int GetHashCode() { hashCode.Add(AppearanceFlags); foreach (ImmutableIconAppearance overlay in Overlays) { - hashCode.Add(overlay); + hashCode.Add(overlay.GetHashCode()); } foreach (ImmutableIconAppearance underlay in Underlays) { - hashCode.Add(underlay); + hashCode.Add(underlay.GetHashCode()); } foreach (int visContent in VisContents) { @@ -270,17 +264,17 @@ public MutableIconAppearance ToMutable() { }; foreach(ImmutableIconAppearance overlay in Overlays) - result.Overlays.Add(overlay.GetHashCode()); + result.Overlays.Add(overlay); foreach(ImmutableIconAppearance underlay in Underlays) - result.Underlays.Add(underlay.GetHashCode()); + result.Underlays.Add(underlay); for (int i = 0; i < 6; i++) { result.Transform[i] = Transform[i]; } //THIS MUST MATCH, IT NOT MATCHING MEANS APPEARANCES ARE TOTALLY BROKEN - Debug.Assert(result.GetHashCode() == this.GetHashCode()); + Robust.Shared.Utility.DebugTools.Assert(result.GetHashCode() == this.GetHashCode()); return result; } diff --git a/OpenDreamShared/Dream/MutableIconAppearance.cs b/OpenDreamShared/Dream/MutableIconAppearance.cs index 9af5205dfe..c2fbaa9bac 100644 --- a/OpenDreamShared/Dream/MutableIconAppearance.cs +++ b/OpenDreamShared/Dream/MutableIconAppearance.cs @@ -4,10 +4,13 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Robust.Shared.GameObjects; using Lidgren.Network; using Robust.Shared.Network; using System.IO; +using Robust.Shared.IoC; +using OpenDreamShared.Rendering; +using Robust.Shared.Enums; +using Robust.Shared.Utility; namespace OpenDreamShared.Dream; @@ -48,9 +51,15 @@ public sealed class MutableIconAppearance : IEquatable, I [ViewVariables] public string? RenderSource; [ViewVariables] public string? RenderTarget; [ViewVariables] public MouseOpacity MouseOpacity = MouseOpacity.PixelOpaque; - [ViewVariables] public List Overlays; - [ViewVariables] public List Underlays; - [ViewVariables] public List VisContents; + [ViewVariables] public List Overlays; + [ViewVariables] public List Underlays; + + [NonSerialized] + private List? _overlayIDs; + [NonSerialized] + private List? _underlayIDs; + + [ViewVariables] public List VisContents; [ViewVariables] public List Filters; [ViewVariables] public List Verbs; @@ -118,6 +127,18 @@ public MutableIconAppearance(MutableIconAppearance appearance) { } } + //this should only be called client-side, after network transfer + public void ResolveOverlays(SharedAppearanceSystem appearanceSystem) { + if(_overlayIDs is not null) + foreach(int overlayID in _overlayIDs) + Overlays.Add(appearanceSystem.MustGetAppearanceById(overlayID)); + if(_underlayIDs is not null) + foreach(int underlayID in _underlayIDs) + Underlays.Add(appearanceSystem.MustGetAppearanceById(underlayID)); + _overlayIDs = null; + _underlayIDs = null; + } + public override bool Equals(object? obj) => obj is MutableIconAppearance appearance && Equals(appearance); public bool Equals(MutableIconAppearance? appearance) { @@ -233,12 +254,23 @@ public override int GetHashCode() { hashCode.Add(BlendMode); hashCode.Add(AppearanceFlags); - foreach (int overlay in Overlays) { - hashCode.Add(overlay); + + if(_overlayIDs is not null) { + foreach(var overlayid in _overlayIDs) + hashCode.Add(overlayid); + } else { + foreach (var overlay in Overlays) { + hashCode.Add(overlay.GetHashCode()); + } } - foreach (int underlay in Underlays) { - hashCode.Add(underlay); + if(_underlayIDs is not null) { + foreach(var underlayid in _underlayIDs) + hashCode.Add(underlayid); + } else { + foreach (var underlay in Underlays) { + hashCode.Add(underlay.GetHashCode()); + } } foreach (int visContent in VisContents) { @@ -290,6 +322,7 @@ public void SetColor(in ColorMatrix matrix) { public int ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) { int? appearanceId = null; + var property = (IconAppearanceProperty)buffer.ReadByte(); while (property != IconAppearanceProperty.End) { switch (property) { @@ -370,8 +403,9 @@ public int ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serialize var overlaysCount = buffer.ReadVariableInt32(); Overlays.EnsureCapacity(overlaysCount); + _overlayIDs = new(overlaysCount); for (int overlaysI = 0; overlaysI < overlaysCount; overlaysI++) { - Overlays.Add(buffer.ReadVariableInt32()); + _overlayIDs.Add(buffer.ReadVariableInt32()); } break; @@ -380,8 +414,9 @@ public int ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serialize var underlaysCount = buffer.ReadVariableInt32(); Underlays.EnsureCapacity(underlaysCount); + _underlayIDs = new(underlaysCount); for (int underlaysI = 0; underlaysI < underlaysCount; underlaysI++) { - Underlays.Add(buffer.ReadVariableInt32()); + _underlayIDs.Add(buffer.ReadVariableInt32()); } break; @@ -436,6 +471,9 @@ public int ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serialize property = (IconAppearanceProperty)buffer.ReadByte(); } + + //if this fails it means there's something wrong with reading/writing appearances across the network and it will break everything + DebugTools.Assert(appearanceId == this.GetHashCode()); if(appearanceId is not null) return appearanceId.Value; else diff --git a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs index 3511341463..fbe900bafd 100644 --- a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs +++ b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs @@ -6,6 +6,10 @@ namespace OpenDreamShared.Rendering; public abstract class SharedAppearanceSystem : EntitySystem { + + public abstract ImmutableIconAppearance MustGetAppearanceById(int appearanceId); + public abstract void RemoveAppearance(ImmutableIconAppearance appearance); + [Serializable, NetSerializable] public sealed class NewAppearanceEvent(int appearanceId, MutableIconAppearance appearance) : EntityEventArgs { public int AppearanceId { get; } = appearanceId; From f5e4c14a3f76318e5b519a091e85eaad91d3c23a Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Thu, 17 Oct 2024 00:09:39 +0100 Subject: [PATCH 48/54] shoulda just done it this way the first time --- .../Rendering/ClientAppearanceSystem.cs | 33 ++- OpenDreamClient/Rendering/DreamIcon.cs | 7 +- .../Rendering/ServerAppearanceSystem.cs | 2 +- .../Dream/IBufferableAppearance.cs | 9 - .../Dream/ImmutableIconAppearance.cs | 232 ++++++++++++++++-- .../Dream/MutableIconAppearance.cs | 206 +--------------- .../Network/Messages/MsgAllAppearances.cs | 9 +- .../Network/Messages/MsgNewAppearance.cs | 12 +- 8 files changed, 239 insertions(+), 271 deletions(-) delete mode 100644 OpenDreamShared/Dream/IBufferableAppearance.cs diff --git a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs index 62010ee816..6aa895036e 100644 --- a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs +++ b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs @@ -11,8 +11,8 @@ namespace OpenDreamClient.Rendering; internal sealed class ClientAppearanceSystem : SharedAppearanceSystem { - private Dictionary _appearances = new(); - private readonly Dictionary>> _appearanceLoadCallbacks = new(); + private Dictionary _appearances = new(); + private readonly Dictionary>> _appearanceLoadCallbacks = new(); private readonly Dictionary _turfIcons = new(); private readonly Dictionary _filterShaders = new(); @@ -35,14 +35,10 @@ public override void Shutdown() { _turfIcons.Clear(); } - public void SetAllAppearances(Dictionary appearances) { - _appearances = new(appearances.Count); - - foreach (KeyValuePair pair in appearances) { - _appearances.Add(pair.Key, (MutableIconAppearance) pair.Value); - } + public void SetAllAppearances(Dictionary appearances) { + _appearances = appearances; //need to do this because all overlays can't be resolved until the whole appearance table is populated - foreach(KeyValuePair pair in _appearances) { + foreach(KeyValuePair pair in _appearances) { pair.Value.ResolveOverlays(this); if(pair.Value.GetHashCode() != pair.Key) Logger.GetSawmill("opendream.appearance").Error($"SetAllAppearances: Appearance ID and Hash DO NOT MATCH. THIS IS REALLY BAD. {pair.Value.GetHashCode()} != {pair.Key}"); @@ -52,7 +48,7 @@ public void SetAllAppearances(Dictionary appearances } } - public void LoadAppearance(int appearanceId, Action loadCallback) { + public void LoadAppearance(int appearanceId, Action loadCallback) { if (_appearances.TryGetValue(appearanceId, out var appearance)) { loadCallback(appearance); return; @@ -77,13 +73,12 @@ public DreamIcon GetTurfIcon(int turfId) { } public void OnNewAppearance(MsgNewAppearance e) { - _appearances[e.AppearanceId] = (MutableIconAppearance)e.Appearance; - _appearances[e.AppearanceId].ResolveOverlays(this); - if(_appearances[e.AppearanceId].GetHashCode() != e.AppearanceId) - throw new Exception($"Appearance ID and Hash DO NOT MATCH. THIS IS REALLY BAD. {_appearances[e.AppearanceId].GetHashCode()} != {e.AppearanceId}"); + int appearanceId = e.Appearance.GetHashCode(); + _appearances[appearanceId] = e.Appearance; + _appearances[appearanceId].ResolveOverlays(this); - if (_appearanceLoadCallbacks.TryGetValue(e.AppearanceId, out var callbacks)) { - foreach (var callback in callbacks) callback(_appearances[e.AppearanceId]); + if (_appearanceLoadCallbacks.TryGetValue(appearanceId, out var callbacks)) { + foreach (var callback in callbacks) callback(_appearances[appearanceId]); } } @@ -91,7 +86,7 @@ private void OnAnimation(AnimationEvent e) { if(e.Entity == NetEntity.Invalid && e.TurfId is not null) { //it's a turf or area if(_turfIcons.TryGetValue(e.TurfId.Value, out var turfIcon)) LoadAppearance(e.TargetAppearanceId, targetAppearance => { - turfIcon.StartAppearanceAnimation(targetAppearance, e.Duration, e.Easing, e.Loop, e.Flags, e.Delay, e.ChainAnim); + turfIcon.StartAppearanceAnimation(targetAppearance.ToMutable(), e.Duration, e.Easing, e.Loop, e.Flags, e.Delay, e.ChainAnim); }); } else { //image or movable EntityUid ent = _entityManager.GetEntity(e.Entity); @@ -99,7 +94,7 @@ private void OnAnimation(AnimationEvent e) { return; LoadAppearance(e.TargetAppearanceId, targetAppearance => { - sprite.Icon.StartAppearanceAnimation(targetAppearance, e.Duration, e.Easing, e.Loop, e.Flags, e.Delay, e.ChainAnim); + sprite.Icon.StartAppearanceAnimation(targetAppearance.ToMutable(), e.Duration, e.Easing, e.Loop, e.Flags, e.Delay, e.ChainAnim); }); } } @@ -218,7 +213,7 @@ public ShaderInstance GetFilterShader(DreamFilter filter, Dictionary { + var mutableAppearance = appearance.ToMutable(); if (parentDir != null && appearance.InheritsDirection) { - appearance = new MutableIconAppearance(appearance) { - Direction = parentDir.Value - }; + mutableAppearance.Direction = parentDir.Value; } - Appearance = appearance; + Appearance = mutableAppearance; }); } diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index c8bea1af7d..369893b3ca 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -48,7 +48,7 @@ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { if (e.NewStatus == SessionStatus.InGame) { //todo this is probably stupid slow lock (_lock) { - Dictionary sendData = new(_idToAppearance.Count); + Dictionary sendData = new(_idToAppearance.Count); foreach(int key in _idToAppearance.Keys){ ImmutableIconAppearance? immutable; diff --git a/OpenDreamShared/Dream/IBufferableAppearance.cs b/OpenDreamShared/Dream/IBufferableAppearance.cs deleted file mode 100644 index cf42993e35..0000000000 --- a/OpenDreamShared/Dream/IBufferableAppearance.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Lidgren.Network; -using Robust.Shared.Serialization; - -namespace OpenDreamShared.Dream; - -public interface IBufferableAppearance { - public int ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer); - public void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer); -} diff --git a/OpenDreamShared/Dream/ImmutableIconAppearance.cs b/OpenDreamShared/Dream/ImmutableIconAppearance.cs index 0dd896e03a..f7f7d0d3e7 100644 --- a/OpenDreamShared/Dream/ImmutableIconAppearance.cs +++ b/OpenDreamShared/Dream/ImmutableIconAppearance.cs @@ -8,6 +8,8 @@ using Robust.Shared.Maths; using System; using OpenDreamShared.Rendering; +using System.Collections.Generic; +using Robust.Shared.Player; namespace OpenDreamShared.Dream; @@ -22,30 +24,34 @@ namespace OpenDreamShared.Dream; // TODO: Wow this is huge! Probably look into splitting this by most used/least used to reduce the size of these [Serializable] -public sealed class ImmutableIconAppearance : IEquatable, IBufferableAppearance { +public sealed class ImmutableIconAppearance : IEquatable{ private bool registered = false; - [ViewVariables] public readonly string Name; - [ViewVariables] public readonly int? Icon; - [ViewVariables] public readonly string? IconState; - [ViewVariables] public readonly AtomDirection Direction; - [ViewVariables] public readonly bool InheritsDirection; // Inherits direction when used as an overlay - [ViewVariables] public readonly Vector2i PixelOffset; // pixel_x and pixel_y - [ViewVariables] public readonly Vector2i PixelOffset2; // pixel_w and pixel_z - [ViewVariables] public readonly Color Color; - [ViewVariables] public readonly byte Alpha; - [ViewVariables] public readonly float GlideSize; - [ViewVariables] public readonly float Layer; - [ViewVariables] public int Plane; - [ViewVariables] public readonly BlendMode BlendMode; - [ViewVariables] public readonly AppearanceFlags AppearanceFlags; - [ViewVariables] public readonly sbyte Invisibility; - [ViewVariables] public readonly bool Opacity; - [ViewVariables] public readonly bool Override; - [ViewVariables] public readonly string? RenderSource; - [ViewVariables] public readonly string? RenderTarget; - [ViewVariables] public readonly MouseOpacity MouseOpacity; + [ViewVariables] public readonly string Name = MutableIconAppearance.Default.Name; + [ViewVariables] public readonly int? Icon = MutableIconAppearance.Default.Icon; + [ViewVariables] public readonly string? IconState = MutableIconAppearance.Default.IconState; + [ViewVariables] public readonly AtomDirection Direction = MutableIconAppearance.Default.Direction; + [ViewVariables] public readonly bool InheritsDirection = MutableIconAppearance.Default.InheritsDirection; // Inherits direction when used as an overlay + [ViewVariables] public readonly Vector2i PixelOffset = MutableIconAppearance.Default.PixelOffset; // pixel_x and pixel_y + [ViewVariables] public readonly Vector2i PixelOffset2 = MutableIconAppearance.Default.PixelOffset2; // pixel_w and pixel_z + [ViewVariables] public readonly Color Color = MutableIconAppearance.Default.Color; + [ViewVariables] public readonly byte Alpha = MutableIconAppearance.Default.Alpha; + [ViewVariables] public readonly float GlideSize = MutableIconAppearance.Default.GlideSize; + [ViewVariables] public readonly float Layer = MutableIconAppearance.Default.Layer; + [ViewVariables] public int Plane = MutableIconAppearance.Default.Plane; + [ViewVariables] public readonly BlendMode BlendMode = MutableIconAppearance.Default.BlendMode; + [ViewVariables] public readonly AppearanceFlags AppearanceFlags = MutableIconAppearance.Default.AppearanceFlags; + [ViewVariables] public readonly sbyte Invisibility = MutableIconAppearance.Default.Invisibility; + [ViewVariables] public readonly bool Opacity = MutableIconAppearance.Default.Opacity; + [ViewVariables] public readonly bool Override = MutableIconAppearance.Default.Override; + [ViewVariables] public readonly string? RenderSource = MutableIconAppearance.Default.RenderSource; + [ViewVariables] public readonly string? RenderTarget = MutableIconAppearance.Default.RenderTarget; + [ViewVariables] public readonly MouseOpacity MouseOpacity = MutableIconAppearance.Default.MouseOpacity; [ViewVariables] public readonly ImmutableIconAppearance[] Overlays; [ViewVariables] public readonly ImmutableIconAppearance[] Underlays; + [NonSerialized] + private List? _overlayIDs; + [NonSerialized] + private List? _underlayIDs; [ViewVariables] public readonly Robust.Shared.GameObjects.NetEntity[] VisContents; [ViewVariables] public readonly DreamFilter[] Filters; [ViewVariables] public readonly int[] Verbs; @@ -74,13 +80,28 @@ public sealed class ImmutableIconAppearance : IEquatable PixelOffset + PixelOffset2; private int? _storedHashCode; - private readonly SharedAppearanceSystem appearanceSystem; + private readonly SharedAppearanceSystem? appearanceSystem; public void MarkRegistered(){ registered = true; } - public ImmutableIconAppearance(MutableIconAppearance appearance, SharedAppearanceSystem serverAppearanceSystem) { + //this should only be called client-side, after network transfer + public void ResolveOverlays(SharedAppearanceSystem appearanceSystem) { + if(_overlayIDs is not null) + for (int i = 0; i < _overlayIDs.Count; i++) + Overlays[i] = appearanceSystem.MustGetAppearanceById(_overlayIDs[i]); + + if(_underlayIDs is not null) + for (int i = 0; i < _underlayIDs.Count; i++) + Underlays[i] = appearanceSystem.MustGetAppearanceById(_underlayIDs[i]); + + _overlayIDs = null; + _underlayIDs = null; + } + + + public ImmutableIconAppearance(MutableIconAppearance appearance, SharedAppearanceSystem? serverAppearanceSystem) { appearanceSystem = serverAppearanceSystem; Name = appearance.Name; @@ -231,6 +252,167 @@ public override int GetHashCode() { return (int)_storedHashCode; } + public ImmutableIconAppearance(NetIncomingMessage buffer, IRobustSerializer serializer) { + Overlays = []; + Underlays = []; + VisContents = []; + Filters = []; + Verbs =[]; + + + var property = (IconAppearanceProperty)buffer.ReadByte(); + while (property != IconAppearanceProperty.End) { + switch (property) { + case IconAppearanceProperty.Name: + Name = buffer.ReadString(); + break; + case IconAppearanceProperty.Id: + _storedHashCode = buffer.ReadVariableInt32(); + break; + case IconAppearanceProperty.Icon: + Icon = buffer.ReadVariableInt32(); + break; + case IconAppearanceProperty.IconState: + IconState = buffer.ReadString(); + break; + case IconAppearanceProperty.Direction: + Direction = (AtomDirection)buffer.ReadByte(); + break; + case IconAppearanceProperty.DoesntInheritDirection: + InheritsDirection = false; + break; + case IconAppearanceProperty.PixelOffset: + PixelOffset = (buffer.ReadVariableInt32(), buffer.ReadVariableInt32()); + break; + case IconAppearanceProperty.PixelOffset2: + PixelOffset2 = (buffer.ReadVariableInt32(), buffer.ReadVariableInt32()); + break; + case IconAppearanceProperty.Color: + Color = buffer.ReadColor(); + break; + case IconAppearanceProperty.Alpha: + Alpha = buffer.ReadByte(); + break; + case IconAppearanceProperty.GlideSize: + GlideSize = buffer.ReadFloat(); + break; + case IconAppearanceProperty.Layer: + Layer = buffer.ReadFloat(); + break; + case IconAppearanceProperty.Plane: + Plane = buffer.ReadVariableInt32(); + break; + case IconAppearanceProperty.BlendMode: + BlendMode = (BlendMode)buffer.ReadByte(); + break; + case IconAppearanceProperty.AppearanceFlags: + AppearanceFlags = (AppearanceFlags)buffer.ReadInt32(); + break; + case IconAppearanceProperty.Invisibility: + Invisibility = buffer.ReadSByte(); + break; + case IconAppearanceProperty.Opacity: + Opacity = buffer.ReadBoolean(); + break; + case IconAppearanceProperty.Override: + Override = buffer.ReadBoolean(); + break; + case IconAppearanceProperty.RenderSource: + RenderSource = buffer.ReadString(); + break; + case IconAppearanceProperty.RenderTarget: + RenderTarget = buffer.ReadString(); + break; + case IconAppearanceProperty.MouseOpacity: + MouseOpacity = (MouseOpacity)buffer.ReadByte(); + break; + case IconAppearanceProperty.ColorMatrix: + ColorMatrix = new( + buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), + buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), + buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), + buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), + buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle() + ); + + break; + case IconAppearanceProperty.Overlays: { + var overlaysCount = buffer.ReadVariableInt32(); + + Overlays = new ImmutableIconAppearance[overlaysCount]; + _overlayIDs = new(overlaysCount); + for (int overlaysI = 0; overlaysI < overlaysCount; overlaysI++) { + _overlayIDs.Add(buffer.ReadVariableInt32()); + } + + break; + } + case IconAppearanceProperty.Underlays: { + var underlaysCount = buffer.ReadVariableInt32(); + + Underlays = new ImmutableIconAppearance[underlaysCount]; + _underlayIDs = new(underlaysCount); + for (int underlaysI = 0; underlaysI < underlaysCount; underlaysI++) { + _underlayIDs.Add(buffer.ReadVariableInt32()); + } + + break; + } + case IconAppearanceProperty.VisContents: { + var visContentsCount = buffer.ReadVariableInt32(); + + VisContents = new Robust.Shared.GameObjects.NetEntity[visContentsCount]; + for (int visContentsI = 0; visContentsI < visContentsCount; visContentsI++) { + VisContents[visContentsI] = buffer.ReadNetEntity(); + } + + break; + } + case IconAppearanceProperty.Filters: { + var filtersCount = buffer.ReadInt32(); + + Filters = new DreamFilter[filtersCount]; + for (int filtersI = 0; filtersI < filtersCount; filtersI++) { + var filterLength = buffer.ReadVariableInt32(); + var filterData = buffer.ReadBytes(filterLength); + using var filterStream = new MemoryStream(filterData); + var filter = serializer.Deserialize(filterStream); + + Filters[filtersI] = filter; + } + + break; + } + case IconAppearanceProperty.Verbs: { + var verbsCount = buffer.ReadVariableInt32(); + + Verbs = new int[verbsCount]; + for (int verbsI = 0; verbsI < verbsCount; verbsI++) { + Verbs[verbsI] = buffer.ReadVariableInt32(); + } + + break; + } + case IconAppearanceProperty.Transform: { + Transform = [ + buffer.ReadSingle(), buffer.ReadSingle(), + buffer.ReadSingle(), buffer.ReadSingle(), + buffer.ReadSingle(), buffer.ReadSingle() + ]; + + break; + } + default: + throw new Exception($"Invalid property {property}"); + } + + property = (IconAppearanceProperty)buffer.ReadByte(); + } + + if(_storedHashCode is null) + throw new Exception("No appearance ID found in buffer"); + } + //Creates an editable *copy* of this appearance, which must be added to the ServerAppearanceSystem to be used. [Pure] public MutableIconAppearance ToMutable() { @@ -273,8 +455,6 @@ public MutableIconAppearance ToMutable() { result.Transform[i] = Transform[i]; } - //THIS MUST MATCH, IT NOT MATCHING MEANS APPEARANCES ARE TOTALLY BROKEN - Robust.Shared.Utility.DebugTools.Assert(result.GetHashCode() == this.GetHashCode()); return result; } @@ -457,7 +637,7 @@ public int ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serialize ~ImmutableIconAppearance() { if(registered) - appearanceSystem.RemoveAppearance(this); + appearanceSystem!.RemoveAppearance(this); } } diff --git a/OpenDreamShared/Dream/MutableIconAppearance.cs b/OpenDreamShared/Dream/MutableIconAppearance.cs index c2fbaa9bac..bf7a1cfa20 100644 --- a/OpenDreamShared/Dream/MutableIconAppearance.cs +++ b/OpenDreamShared/Dream/MutableIconAppearance.cs @@ -28,7 +28,7 @@ namespace OpenDreamShared.Dream; // TODO: Wow this is huge! Probably look into splitting this by most used/least used to reduce the size of these [Serializable] -public sealed class MutableIconAppearance : IEquatable, IBufferableAppearance { +public sealed class MutableIconAppearance : IEquatable{ public static readonly MutableIconAppearance Default = new(); [ViewVariables] public string Name = string.Empty; @@ -53,12 +53,6 @@ public sealed class MutableIconAppearance : IEquatable, I [ViewVariables] public MouseOpacity MouseOpacity = MouseOpacity.PixelOpaque; [ViewVariables] public List Overlays; [ViewVariables] public List Underlays; - - [NonSerialized] - private List? _overlayIDs; - [NonSerialized] - private List? _underlayIDs; - [ViewVariables] public List VisContents; [ViewVariables] public List Filters; [ViewVariables] public List Verbs; @@ -126,19 +120,6 @@ public MutableIconAppearance(MutableIconAppearance appearance) { Transform[i] = appearance.Transform[i]; } } - - //this should only be called client-side, after network transfer - public void ResolveOverlays(SharedAppearanceSystem appearanceSystem) { - if(_overlayIDs is not null) - foreach(int overlayID in _overlayIDs) - Overlays.Add(appearanceSystem.MustGetAppearanceById(overlayID)); - if(_underlayIDs is not null) - foreach(int underlayID in _underlayIDs) - Underlays.Add(appearanceSystem.MustGetAppearanceById(underlayID)); - _overlayIDs = null; - _underlayIDs = null; - } - public override bool Equals(object? obj) => obj is MutableIconAppearance appearance && Equals(appearance); public bool Equals(MutableIconAppearance? appearance) { @@ -254,23 +235,13 @@ public override int GetHashCode() { hashCode.Add(BlendMode); hashCode.Add(AppearanceFlags); - - if(_overlayIDs is not null) { - foreach(var overlayid in _overlayIDs) - hashCode.Add(overlayid); - } else { - foreach (var overlay in Overlays) { - hashCode.Add(overlay.GetHashCode()); - } + foreach (var overlay in Overlays) { + hashCode.Add(overlay.GetHashCode()); } - if(_underlayIDs is not null) { - foreach(var underlayid in _underlayIDs) - hashCode.Add(underlayid); - } else { - foreach (var underlay in Underlays) { - hashCode.Add(underlay.GetHashCode()); - } + + foreach (var underlay in Underlays) { + hashCode.Add(underlay.GetHashCode()); } foreach (int visContent in VisContents) { @@ -318,171 +289,6 @@ public void SetColor(in ColorMatrix matrix) { Color = Color.White; ColorMatrix = matrix; } - - - public int ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) { - int? appearanceId = null; - - var property = (IconAppearanceProperty)buffer.ReadByte(); - while (property != IconAppearanceProperty.End) { - switch (property) { - case IconAppearanceProperty.Name: - Name = buffer.ReadString(); - break; - case IconAppearanceProperty.Id: - appearanceId = buffer.ReadVariableInt32(); - break; - case IconAppearanceProperty.Icon: - Icon = buffer.ReadVariableInt32(); - break; - case IconAppearanceProperty.IconState: - IconState = buffer.ReadString(); - break; - case IconAppearanceProperty.Direction: - Direction = (AtomDirection)buffer.ReadByte(); - break; - case IconAppearanceProperty.DoesntInheritDirection: - InheritsDirection = false; - break; - case IconAppearanceProperty.PixelOffset: - PixelOffset = (buffer.ReadVariableInt32(), buffer.ReadVariableInt32()); - break; - case IconAppearanceProperty.PixelOffset2: - PixelOffset2 = (buffer.ReadVariableInt32(), buffer.ReadVariableInt32()); - break; - case IconAppearanceProperty.Color: - Color = buffer.ReadColor(); - break; - case IconAppearanceProperty.Alpha: - Alpha = buffer.ReadByte(); - break; - case IconAppearanceProperty.GlideSize: - GlideSize = buffer.ReadFloat(); - break; - case IconAppearanceProperty.Layer: - Layer = buffer.ReadFloat(); - break; - case IconAppearanceProperty.Plane: - Plane = buffer.ReadVariableInt32(); - break; - case IconAppearanceProperty.BlendMode: - BlendMode = (BlendMode)buffer.ReadByte(); - break; - case IconAppearanceProperty.AppearanceFlags: - AppearanceFlags = (AppearanceFlags)buffer.ReadInt32(); - break; - case IconAppearanceProperty.Invisibility: - Invisibility = buffer.ReadSByte(); - break; - case IconAppearanceProperty.Opacity: - Opacity = buffer.ReadBoolean(); - break; - case IconAppearanceProperty.Override: - Override = buffer.ReadBoolean(); - break; - case IconAppearanceProperty.RenderSource: - RenderSource = buffer.ReadString(); - break; - case IconAppearanceProperty.RenderTarget: - RenderTarget = buffer.ReadString(); - break; - case IconAppearanceProperty.MouseOpacity: - MouseOpacity = (MouseOpacity)buffer.ReadByte(); - break; - case IconAppearanceProperty.ColorMatrix: - ColorMatrix = new( - buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), - buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), - buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), - buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), - buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle() - ); - - break; - case IconAppearanceProperty.Overlays: { - var overlaysCount = buffer.ReadVariableInt32(); - - Overlays.EnsureCapacity(overlaysCount); - _overlayIDs = new(overlaysCount); - for (int overlaysI = 0; overlaysI < overlaysCount; overlaysI++) { - _overlayIDs.Add(buffer.ReadVariableInt32()); - } - - break; - } - case IconAppearanceProperty.Underlays: { - var underlaysCount = buffer.ReadVariableInt32(); - - Underlays.EnsureCapacity(underlaysCount); - _underlayIDs = new(underlaysCount); - for (int underlaysI = 0; underlaysI < underlaysCount; underlaysI++) { - _underlayIDs.Add(buffer.ReadVariableInt32()); - } - - break; - } - case IconAppearanceProperty.VisContents: { - var visContentsCount = buffer.ReadVariableInt32(); - - VisContents.EnsureCapacity(visContentsCount); - for (int visContentsI = 0; visContentsI < visContentsCount; visContentsI++) { - VisContents.Add(buffer.ReadNetEntity()); - } - - break; - } - case IconAppearanceProperty.Filters: { - var filtersCount = buffer.ReadInt32(); - - Filters.EnsureCapacity(filtersCount); - for (int filtersI = 0; filtersI < filtersCount; filtersI++) { - var filterLength = buffer.ReadVariableInt32(); - var filterData = buffer.ReadBytes(filterLength); - using var filterStream = new MemoryStream(filterData); - var filter = serializer.Deserialize(filterStream); - - Filters.Add(filter); - } - - break; - } - case IconAppearanceProperty.Verbs: { - var verbsCount = buffer.ReadVariableInt32(); - - Verbs.EnsureCapacity(verbsCount); - for (int verbsI = 0; verbsI < verbsCount; verbsI++) { - Verbs.Add(buffer.ReadVariableInt32()); - } - - break; - } - case IconAppearanceProperty.Transform: { - Transform = [ - buffer.ReadSingle(), buffer.ReadSingle(), - buffer.ReadSingle(), buffer.ReadSingle(), - buffer.ReadSingle(), buffer.ReadSingle() - ]; - - break; - } - default: - throw new Exception($"Invalid property {property}"); - } - - property = (IconAppearanceProperty)buffer.ReadByte(); - } - - //if this fails it means there's something wrong with reading/writing appearances across the network and it will break everything - DebugTools.Assert(appearanceId == this.GetHashCode()); - if(appearanceId is not null) - return appearanceId.Value; - else - throw new Exception("No appearance ID found in buffer"); - } - - public void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) { - throw new NotImplementedException(); - } } public enum BlendMode { diff --git a/OpenDreamShared/Network/Messages/MsgAllAppearances.cs b/OpenDreamShared/Network/Messages/MsgAllAppearances.cs index 05b22d7877..f20cdd228a 100644 --- a/OpenDreamShared/Network/Messages/MsgAllAppearances.cs +++ b/OpenDreamShared/Network/Messages/MsgAllAppearances.cs @@ -9,9 +9,9 @@ namespace OpenDreamShared.Network.Messages; -public sealed class MsgAllAppearances(Dictionary allAppearances) : NetMessage { +public sealed class MsgAllAppearances(Dictionary allAppearances) : NetMessage { public override MsgGroups MsgGroup => MsgGroups.EntityEvent; - public Dictionary AllAppearances = allAppearances; + public Dictionary AllAppearances = allAppearances; public MsgAllAppearances() : this(new()) { } public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) { @@ -19,9 +19,8 @@ public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer AllAppearances = new(count); for (int i = 0; i < count; i++) { - var appearance = new MutableIconAppearance(); - var appearanceId = appearance.ReadFromBuffer(buffer, serializer); - AllAppearances.Add(appearanceId, appearance); + var appearance = new ImmutableIconAppearance(buffer, serializer); + AllAppearances.Add(appearance.GetHashCode(), appearance); } } diff --git a/OpenDreamShared/Network/Messages/MsgNewAppearance.cs b/OpenDreamShared/Network/Messages/MsgNewAppearance.cs index 2cf81f17ac..901c987b19 100644 --- a/OpenDreamShared/Network/Messages/MsgNewAppearance.cs +++ b/OpenDreamShared/Network/Messages/MsgNewAppearance.cs @@ -11,18 +11,16 @@ namespace OpenDreamShared.Network.Messages; public sealed class MsgNewAppearance: NetMessage { public override MsgGroups MsgGroup => MsgGroups.EntityEvent; - public MsgNewAppearance() : this(MutableIconAppearance.Default) { } - public MsgNewAppearance(IBufferableAppearance appearance) => Appearance = appearance; - public IBufferableAppearance Appearance; - public int AppearanceId; + + public MsgNewAppearance() : this(new ImmutableIconAppearance(MutableIconAppearance.Default, null)) {} + public MsgNewAppearance(ImmutableIconAppearance appearance) => Appearance = appearance; + public ImmutableIconAppearance Appearance; public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) { - Appearance = new MutableIconAppearance(); - AppearanceId = Appearance.ReadFromBuffer(buffer, serializer); + Appearance = new ImmutableIconAppearance(buffer, serializer); } public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) { - AppearanceId = Appearance.GetHashCode(); Appearance.WriteToBuffer(buffer,serializer); } } From db99b729ab0475b721d242f83062231ae1052ed3 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Fri, 18 Oct 2024 12:54:18 +0100 Subject: [PATCH 49/54] housekeeping --- .../Rendering/ClientAppearanceSystem.cs | 2 -- OpenDreamClient/Rendering/DreamIcon.cs | 1 + OpenDreamClient/Rendering/DreamViewOverlay.cs | 8 ++--- OpenDreamRuntime/AtomManager.cs | 8 ++--- OpenDreamRuntime/DreamValue.cs | 1 + .../Objects/Types/DreamObjectArea.cs | 3 +- .../Objects/Types/DreamObjectAtom.cs | 9 ++--- .../Objects/Types/DreamObjectImage.cs | 24 ++++++------- .../Objects/Types/DreamObjectMatrix.cs | 1 + .../Objects/Types/DreamObjectTurf.cs | 3 +- .../Rendering/DMISpriteComponent.cs | 2 +- .../Rendering/ServerAppearanceSystem.cs | 22 +++++++----- .../Dream/ImmutableIconAppearance.cs | 36 ++++++------------- .../Dream/MutableIconAppearance.cs | 11 +----- .../Network/Messages/MsgNewAppearance.cs | 6 +--- .../Rendering/SharedAppearanceSystem.cs | 2 -- 16 files changed, 54 insertions(+), 85 deletions(-) diff --git a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs index 6aa895036e..051b6d2a7b 100644 --- a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs +++ b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs @@ -40,8 +40,6 @@ public void SetAllAppearances(Dictionary appearanc //need to do this because all overlays can't be resolved until the whole appearance table is populated foreach(KeyValuePair pair in _appearances) { pair.Value.ResolveOverlays(this); - if(pair.Value.GetHashCode() != pair.Key) - Logger.GetSawmill("opendream.appearance").Error($"SetAllAppearances: Appearance ID and Hash DO NOT MATCH. THIS IS REALLY BAD. {pair.Value.GetHashCode()} != {pair.Key}"); if (_appearanceLoadCallbacks.TryGetValue(pair.Key, out var callbacks)) { foreach (var callback in callbacks) callback(pair.Value); } diff --git a/OpenDreamClient/Rendering/DreamIcon.cs b/OpenDreamClient/Rendering/DreamIcon.cs index d1b57e8317..2fdbe5167d 100644 --- a/OpenDreamClient/Rendering/DreamIcon.cs +++ b/OpenDreamClient/Rendering/DreamIcon.cs @@ -42,6 +42,7 @@ private set { UpdateIcon(); } } + private MutableIconAppearance? _appearance; // TODO: We could cache these per-appearance instead of per-atom diff --git a/OpenDreamClient/Rendering/DreamViewOverlay.cs b/OpenDreamClient/Rendering/DreamViewOverlay.cs index c7cfa8654d..55eb129c44 100644 --- a/OpenDreamClient/Rendering/DreamViewOverlay.cs +++ b/OpenDreamClient/Rendering/DreamViewOverlay.cs @@ -188,7 +188,7 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u current.MouseOpacity = icon.Appearance.MouseOpacity; //reverse rotation transforms because of 180 flip from RenderTarget->world transform - Matrix3x2 MutableIconAppearanceTransformMatrix = new Matrix3x2( + Matrix3x2 iconAppearanceTransformMatrix = new Matrix3x2( icon.Appearance.Transform[0], -icon.Appearance.Transform[2], -icon.Appearance.Transform[1], icon.Appearance.Transform[3], icon.Appearance.Transform[4], icon.Appearance.Transform[5] @@ -210,9 +210,9 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u current.AlphaToApply = parentIcon.AlphaToApply * (icon.Appearance.Alpha / 255.0f); if ((icon.Appearance.AppearanceFlags & AppearanceFlags.ResetTransform) != 0 || keepTogether) //RESET_TRANSFORM - current.TransformToApply = MutableIconAppearanceTransformMatrix; + current.TransformToApply = iconAppearanceTransformMatrix; else - current.TransformToApply = MutableIconAppearanceTransformMatrix * parentIcon.TransformToApply; + current.TransformToApply = iconAppearanceTransformMatrix * parentIcon.TransformToApply; if ((icon.Appearance.Plane < -10000)) //FLOAT_PLANE - Note: yes, this really is how it works. Yes it's dumb as shit. current.Plane = parentIcon.Plane + (icon.Appearance.Plane + 32767); @@ -228,7 +228,7 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u current.ColorToApply = icon.Appearance.Color; current.ColorMatrixToApply = icon.Appearance.ColorMatrix; current.AlphaToApply = icon.Appearance.Alpha/255.0f; - current.TransformToApply = MutableIconAppearanceTransformMatrix; + current.TransformToApply = iconAppearanceTransformMatrix; current.Plane = icon.Appearance.Plane; current.Layer = Math.Max(0, icon.Appearance.Layer); //float layers are invalid for icons with no parent } diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index eb4645dd16..acdff4896c 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -481,7 +481,7 @@ public ImmutableIconAppearance MustGetAppearance(DreamObject atom) { DreamObjectTurf turf => turf.Appearance, DreamObjectMovable movable => movable.SpriteComponent.Appearance!, DreamObjectArea area => area.Appearance, - DreamObjectImage image => image.IsMutableAppearance ? AppearanceSystem!.AddAppearance(image.MutableAppearance!, RegisterApearance: false) : image.SpriteComponent!.Appearance!, + DreamObjectImage image => image.IsMutableAppearance ? AppearanceSystem!.AddAppearance(image.MutableAppearance!, registerApearance: false) : image.SpriteComponent!.Appearance!, _ => throw new Exception($"Cannot get appearance of {atom}") }; } @@ -495,7 +495,7 @@ public bool TryGetAppearance(DreamObject atom, [NotNullWhen(true)] out Immutable else if (atom is DreamObjectMovable movable && movable.SpriteComponent.Appearance is not null) appearance = movable.SpriteComponent.Appearance; else if (atom is DreamObjectImage image) - appearance = image.SpriteComponent?.Appearance; + appearance = image.IsMutableAppearance ? AppearanceSystem!.AddAppearance(image.MutableAppearance!, registerApearance: false) : image.SpriteComponent?.Appearance; else if (atom is DreamObjectArea area) appearance = area.Appearance; else @@ -545,11 +545,11 @@ public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasi if (atom is DreamObjectMovable movable) { targetEntity = movable.Entity; targetComponent = movable.SpriteComponent; - appearance = targetComponent.Appearance!.ToMutable(); + appearance = MustGetAppearance(atom).ToMutable(); } else if (atom is DreamObjectImage image && !image.IsMutableAppearance){ targetEntity = image.Entity; targetComponent = image.SpriteComponent; - appearance = targetComponent!.Appearance!.ToMutable(); + appearance = MustGetAppearance(atom).ToMutable(); } else if (atom is DreamObjectTurf turf) { targetEntity = EntityUid.Invalid; turfId = turf.Appearance.GetHashCode() + 1; diff --git a/OpenDreamRuntime/DreamValue.cs b/OpenDreamRuntime/DreamValue.cs index 825ee4bd5e..3dfc48ae8d 100644 --- a/OpenDreamRuntime/DreamValue.cs +++ b/OpenDreamRuntime/DreamValue.cs @@ -757,4 +757,5 @@ public ColorMatrix CreateCopy(ISerializationManager serializationManager, ColorM return new(source); } } + #endregion Serialization diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs b/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs index 6898aedb48..bdaa3dfc39 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs @@ -1,5 +1,4 @@ -using OpenDreamRuntime.Rendering; -using OpenDreamShared.Dream; +using OpenDreamShared.Dream; namespace OpenDreamRuntime.Objects.Types; diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs b/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs index ca916a04a6..1c88badfa3 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs @@ -178,16 +178,11 @@ protected override void SetVar(string varName, DreamValue value) { default: if (AtomManager.IsValidAppearanceVar(varName)) { // Basically AtomManager.UpdateAppearance() but without the performance impact of using actions - var immutableAppearance = AtomManager.MustGetAppearance(this); - - // Clone the appearance - // TODO: We can probably avoid cloning while the DMISpriteComponent is dirty - MutableIconAppearance appearance = (immutableAppearance != null) ? immutableAppearance.ToMutable() : new(); - + MutableIconAppearance appearance = AtomManager.MustGetAppearance(this).ToMutable(); AtomManager.SetAppearanceVar(appearance, varName, value); AtomManager.SetAtomAppearance(this, appearance); break; - } + } base.SetVar(varName, value); break; diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs index e81fabb6fb..56c3e8c693 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectImage.cs @@ -49,9 +49,9 @@ public override void Initialize(DreamProcArguments args) { base.Initialize(args); DreamValue icon = args.GetArgument(0); - if (icon.IsNull || !AtomManager.TryCreateAppearanceFrom(icon, out var MutableIconAppearance)) { + if (icon.IsNull || !AtomManager.TryCreateAppearanceFrom(icon, out var mutableIconAppearance)) { // Use a default appearance, but log a warning about it if icon wasn't null - MutableIconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this).ToMutable(); //object def appearance is created in the constructor + mutableIconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this).ToMutable(); //object def appearance is created in the constructor if (!icon.IsNull) Logger.GetSawmill("opendream.image") .Warning($"Attempted to create an /image from {icon}. This is invalid and a default image was created instead."); @@ -68,16 +68,16 @@ public override void Initialize(DreamProcArguments args) { if (arg.IsNull) continue; - AtomManager.SetAppearanceVar(MutableIconAppearance, argName, arg); + AtomManager.SetAppearanceVar(mutableIconAppearance, argName, arg); if (argName == "dir" && arg.TryGetValueAsInteger(out var argDir) && argDir > 0) { // If a dir is explicitly given in the constructor then overlays using this won't use their owner's dir // Setting dir after construction does not affect this // This is undocumented and I hate it - MutableIconAppearance.InheritsDirection = false; + mutableIconAppearance.InheritsDirection = false; } } - AtomManager.SetAtomAppearance(this, MutableIconAppearance); + AtomManager.SetAtomAppearance(this, mutableIconAppearance); } protected override bool TryGetVar(string varName, out DreamValue value) { @@ -115,7 +115,7 @@ protected override void SetVar(string varName, DreamValue value) { // The dir does not get changed var originalAppearance = AtomManager.MustGetAppearance(this); - newAppearance.Direction = originalAppearance!.Direction; + newAppearance.Direction = originalAppearance.Direction; AtomManager.SetAtomAppearance(this, newAppearance); break; case "loc": @@ -206,16 +206,16 @@ protected override void SetVar(string varName, DreamValue value) { break; } case "override": { - MutableIconAppearance MutableIconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this).ToMutable(); - MutableIconAppearance.Override = value.IsTruthy(); - AtomManager.SetAtomAppearance(this, MutableIconAppearance); + MutableIconAppearance mutableIconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this).ToMutable(); + mutableIconAppearance.Override = value.IsTruthy(); + AtomManager.SetAtomAppearance(this, mutableIconAppearance); break; } default: if (AtomManager.IsValidAppearanceVar(varName)) { - MutableIconAppearance MutableIconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this).ToMutable(); - AtomManager.SetAppearanceVar(MutableIconAppearance, varName, value); - AtomManager.SetAtomAppearance(this, MutableIconAppearance); + MutableIconAppearance mutableIconAppearance = IsMutableAppearance ? MutableAppearance! : AtomManager.MustGetAppearance(this).ToMutable(); + AtomManager.SetAppearanceVar(mutableIconAppearance, varName, value); + AtomManager.SetAtomAppearance(this, mutableIconAppearance); break; } diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectMatrix.cs b/OpenDreamRuntime/Objects/Types/DreamObjectMatrix.cs index 6645598ded..d65ad2b315 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectMatrix.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectMatrix.cs @@ -259,6 +259,7 @@ public override DreamValue OperatorRemove(DreamValue b) { #endregion Operators #region Helpers + /// Used to create a float array understandable by to be a transform. /// The matrix's values in an array, in [a,d,b,e,c,f] order. /// This will not verify that this is a /matrix diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs index 9350b42c3c..729d3fa884 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs @@ -1,5 +1,4 @@ -using OpenDreamRuntime.Rendering; -using OpenDreamShared.Dream; +using OpenDreamShared.Dream; namespace OpenDreamRuntime.Objects.Types; diff --git a/OpenDreamRuntime/Rendering/DMISpriteComponent.cs b/OpenDreamRuntime/Rendering/DMISpriteComponent.cs index 477dbb325b..68ec90b618 100644 --- a/OpenDreamRuntime/Rendering/DMISpriteComponent.cs +++ b/OpenDreamRuntime/Rendering/DMISpriteComponent.cs @@ -9,7 +9,7 @@ public sealed partial class DMISpriteComponent : SharedDMISpriteComponent { [Access(typeof(DMISpriteSystem))] public ScreenLocation ScreenLocation; - //[Access(typeof(DMISpriteSystem))] + [Access(typeof(DMISpriteSystem))] [ViewVariables] public ImmutableIconAppearance? Appearance; } diff --git a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs index 369893b3ca..a8c4686585 100644 --- a/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs +++ b/OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs @@ -5,7 +5,6 @@ using System.Diagnostics.CodeAnalysis; using OpenDreamShared.Network.Messages; using Robust.Shared.Player; -using Robust.Server.GameObjects; using Robust.Shared.Network; using System.Diagnostics; using Robust.Shared.Utility; @@ -13,8 +12,11 @@ namespace OpenDreamRuntime.Rendering; public sealed class ServerAppearanceSystem : SharedAppearanceSystem { - //use the appearance hash as the id! - //appearance hash to weakref + /// + /// Each appearance's HashCode is used as its ID. Here we store these as weakrefs, so each object which holds an appearance MUST + /// hold that ImmutableIconAppearance until it is no longer needed. Overlays & underlays are stored as hard refs on the ImmutableIconAppearance + /// so you only need to hold the main appearance. + /// private readonly Dictionary> _idToAppearance = new(); public readonly ImmutableIconAppearance DefaultAppearance; @@ -51,8 +53,7 @@ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { Dictionary sendData = new(_idToAppearance.Count); foreach(int key in _idToAppearance.Keys){ - ImmutableIconAppearance? immutable; - if(_idToAppearance[key].TryGetTarget(out immutable)) + if(_idToAppearance[key].TryGetTarget(out var immutable)) sendData.Add(key, immutable); } @@ -62,14 +63,16 @@ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { } } - public ImmutableIconAppearance AddAppearance(MutableIconAppearance appearance, bool RegisterApearance = true) { + public ImmutableIconAppearance AddAppearance(MutableIconAppearance appearance, bool registerApearance = true) { ImmutableIconAppearance immutableAppearance = new(appearance, this); + //if this debug assert fails, you've probably changed an icon appearance var and not updated its counterpart + //this debug MUST pass. A number of things rely on these hashcodes being equivalent *on the server*. DebugTools.Assert(appearance.GetHashCode() == immutableAppearance.GetHashCode()); lock (_lock) { if(_idToAppearance.TryGetValue(immutableAppearance.GetHashCode(), out var weakReference) && weakReference.TryGetTarget(out var originalImmutable)) { return originalImmutable; - } else if (RegisterApearance) { - immutableAppearance.MarkRegistered(); + } else if (registerApearance) { + immutableAppearance.MarkRegistered(); //lets this appearance know it needs to do GC finaliser _idToAppearance[immutableAppearance.GetHashCode()] = new(immutableAppearance); _networkManager.ServerSendToAll(new MsgNewAppearance(immutableAppearance)); return immutableAppearance; @@ -88,7 +91,8 @@ public override void RemoveAppearance(ImmutableIconAppearance appearance) { return; _idToAppearance.Remove(appearance.GetHashCode()); RaiseNetworkEvent(new RemoveAppearanceEvent(appearance.GetHashCode())); -} } + } + } } public override ImmutableIconAppearance MustGetAppearanceById(int appearanceId) { diff --git a/OpenDreamShared/Dream/ImmutableIconAppearance.cs b/OpenDreamShared/Dream/ImmutableIconAppearance.cs index f7f7d0d3e7..a685f3fd1a 100644 --- a/OpenDreamShared/Dream/ImmutableIconAppearance.cs +++ b/OpenDreamShared/Dream/ImmutableIconAppearance.cs @@ -9,7 +9,6 @@ using System; using OpenDreamShared.Rendering; using System.Collections.Generic; -using Robust.Shared.Player; namespace OpenDreamShared.Dream; @@ -25,7 +24,9 @@ namespace OpenDreamShared.Dream; // TODO: Wow this is huge! Probably look into splitting this by most used/least used to reduce the size of these [Serializable] public sealed class ImmutableIconAppearance : IEquatable{ - private bool registered = false; + private bool _registered; + private int? _storedHashCode; + private readonly SharedAppearanceSystem? _appearanceSystem; [ViewVariables] public readonly string Name = MutableIconAppearance.Default.Name; [ViewVariables] public readonly int? Icon = MutableIconAppearance.Default.Icon; [ViewVariables] public readonly string? IconState = MutableIconAppearance.Default.IconState; @@ -37,7 +38,7 @@ public sealed class ImmutableIconAppearance : IEquatable? _overlayIDs; + [NonSerialized] private List? _underlayIDs; + [ViewVariables] public readonly Robust.Shared.GameObjects.NetEntity[] VisContents; [ViewVariables] public readonly DreamFilter[] Filters; [ViewVariables] public readonly int[] Verbs; - - /// - /// An appearance can gain a color matrix filter by two possible forces:
- /// 1. the /atom.color var is modified.
- /// 2. the /atom.filters var gets a new filter of type "color".
- /// DM crashes in some circumstances of this but we, as an extension :^), should try not to.
- /// So, this exists as a way for the appearance to remember whether it's coloured by .color, specifically. - ///
- /// - /// The reason we don't just take the slow path and always use this filter is not just for optimization,
- /// it's also for parity! See for more. - ///
[ViewVariables] public readonly ColorMatrix ColorMatrix = ColorMatrix.Identity; /// The Transform property of this appearance, in [a,d,b,e,c,f] order @@ -79,11 +71,8 @@ public sealed class ImmutableIconAppearance : IEquatable PixelOffset + PixelOffset2; - private int? _storedHashCode; - private readonly SharedAppearanceSystem? appearanceSystem; - public void MarkRegistered(){ - registered = true; + _registered = true; } //this should only be called client-side, after network transfer @@ -100,9 +89,8 @@ public void ResolveOverlays(SharedAppearanceSystem appearanceSystem) { _underlayIDs = null; } - public ImmutableIconAppearance(MutableIconAppearance appearance, SharedAppearanceSystem? serverAppearanceSystem) { - appearanceSystem = serverAppearanceSystem; + _appearanceSystem = serverAppearanceSystem; Name = appearance.Name; Icon = appearance.Icon; @@ -259,7 +247,6 @@ public ImmutableIconAppearance(NetIncomingMessage buffer, IRobustSerializer seri Filters = []; Verbs =[]; - var property = (IconAppearanceProperty)buffer.ReadByte(); while (property != IconAppearanceProperty.End) { switch (property) { @@ -636,9 +623,8 @@ public int ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serialize } ~ImmutableIconAppearance() { - if(registered) - appearanceSystem!.RemoveAppearance(this); + if(_registered) + _appearanceSystem!.RemoveAppearance(this); } - } diff --git a/OpenDreamShared/Dream/MutableIconAppearance.cs b/OpenDreamShared/Dream/MutableIconAppearance.cs index bf7a1cfa20..cf83026ad8 100644 --- a/OpenDreamShared/Dream/MutableIconAppearance.cs +++ b/OpenDreamShared/Dream/MutableIconAppearance.cs @@ -1,16 +1,8 @@ using Robust.Shared.Maths; -using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Lidgren.Network; -using Robust.Shared.Network; -using System.IO; -using Robust.Shared.IoC; -using OpenDreamShared.Rendering; -using Robust.Shared.Enums; -using Robust.Shared.Utility; namespace OpenDreamShared.Dream; @@ -120,6 +112,7 @@ public MutableIconAppearance(MutableIconAppearance appearance) { Transform[i] = appearance.Transform[i]; } } + public override bool Equals(object? obj) => obj is MutableIconAppearance appearance && Equals(appearance); public bool Equals(MutableIconAppearance? appearance) { @@ -239,7 +232,6 @@ public override int GetHashCode() { hashCode.Add(overlay.GetHashCode()); } - foreach (var underlay in Underlays) { hashCode.Add(underlay.GetHashCode()); } @@ -372,7 +364,6 @@ public enum IconAppearanceProperty : byte { Filters, Verbs, Transform, - Id, End } diff --git a/OpenDreamShared/Network/Messages/MsgNewAppearance.cs b/OpenDreamShared/Network/Messages/MsgNewAppearance.cs index 901c987b19..e08f7fce13 100644 --- a/OpenDreamShared/Network/Messages/MsgNewAppearance.cs +++ b/OpenDreamShared/Network/Messages/MsgNewAppearance.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Lidgren.Network; +using Lidgren.Network; using OpenDreamShared.Dream; using Robust.Shared.Network; using Robust.Shared.Serialization; diff --git a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs index fbe900bafd..3d732d8669 100644 --- a/OpenDreamShared/Rendering/SharedAppearanceSystem.cs +++ b/OpenDreamShared/Rendering/SharedAppearanceSystem.cs @@ -6,7 +6,6 @@ namespace OpenDreamShared.Rendering; public abstract class SharedAppearanceSystem : EntitySystem { - public abstract ImmutableIconAppearance MustGetAppearanceById(int appearanceId); public abstract void RemoveAppearance(ImmutableIconAppearance appearance); @@ -14,7 +13,6 @@ public abstract class SharedAppearanceSystem : EntitySystem { public sealed class NewAppearanceEvent(int appearanceId, MutableIconAppearance appearance) : EntityEventArgs { public int AppearanceId { get; } = appearanceId; public MutableIconAppearance Appearance { get; } = appearance; - } [Serializable, NetSerializable] From bf40bba10a19455623b5370e9d101ecc97708026 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Fri, 18 Oct 2024 13:38:48 +0100 Subject: [PATCH 50/54] fix a couple runtimes on paradise --- OpenDreamRuntime/AtomManager.cs | 15 ++++++++++----- OpenDreamRuntime/Objects/Types/DreamList.cs | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index acdff4896c..ec07018d92 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -535,7 +535,6 @@ public void SetSpriteAppearance(Entity ent, MutableIconAppea } public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasing easing, int loop, AnimationFlags flags, int delay, bool chainAnim, Action animate) { - //TODO: should handle filters MutableIconAppearance appearance; EntityUid targetEntity; DMISpriteComponent? targetComponent = null; @@ -554,10 +553,16 @@ public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasi targetEntity = EntityUid.Invalid; turfId = turf.Appearance.GetHashCode() + 1; appearance = turf.Appearance.ToMutable(); - //} else if (atom is DreamObjectArea area) { - //?????? - // appearance = area.Appearance.ToMutable(); - // area appearance should be an overlay on turfs, so could maybe get away with animating that? + } else if (atom is DreamObjectArea area) { + return; + //TODO: animate area appearance + //area appearance should be an overlay on turfs, so could maybe get away with animating that? + } else if (atom is DreamObjectClient client) { + return; + //TODO: animate client appearance + } else if (atom is DreamObjectFilter filter) { + return; + //TODO: animate filters } else throw new ArgumentException($"Cannot animate appearance of {atom}"); diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index b4fe67f98d..2c4789822d 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -725,7 +725,7 @@ public override void RemoveValue(DreamValue value) { return; _atomManager.UpdateAppearance(_owner, appearance => { - GetOverlaysList(appearance).Remove(_appearanceSystem.MustGetAppearanceById(overlayAppearance.GetHashCode())); //because the hashcode for mutable == immutable, we can use it directly + GetOverlaysList(appearance).Remove(_appearanceSystem.AddAppearance(overlayAppearance, registerApearance:false)); }); } From f4d793dc6ac9a974dd3cc94d292d132e8df17506 Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sat, 19 Oct 2024 20:10:30 +0100 Subject: [PATCH 51/54] add turf anim test --- TestGame/map_z1.dmm | 6 ++- TestGame/renderer_tests.dm | 84 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/TestGame/map_z1.dmm b/TestGame/map_z1.dmm index 899ae1e80e..6ba6898417 100644 --- a/TestGame/map_z1.dmm +++ b/TestGame/map_z1.dmm @@ -36,8 +36,10 @@ "J" = (/obj/order_test_target,/turf,/area) "K" = (/obj/complex_overlay_test,/turf,/area) "L" = (/obj/float_layer_test,/turf,/area) +"N" = (/obj/plaque/animation_turf_test,/turf,/area) "O" = (/obj/plaque/animation_test,/turf,/area) "R" = (/turf/blue,/area/withicon) +"S" = (/obj/button/animation_turf_test,/turf,/area) "X" = (/turf,/area/withicon) "Z" = (/obj/button/animation_test,/turf,/area) @@ -45,8 +47,8 @@ bbbbbbbbbbbbbbbbbbbbbb bedciklwopsuaaaaaaaaab bfghjmnxqrtvaaaaaaaaab -byADFGZaaaaaaaaaaaaaab -bzBEHIOaaaaaaaaaaaaaab +byADFGZSaaaaaaaaaaaaab +bzBEHIONaaaaaaaaaaaaab baaaaaaaaaaaaaaaaaaaab baaaaaaaaaaaaaaaaaaaab baaaaaaaaaaaaaaaaaaaab diff --git a/TestGame/renderer_tests.dm b/TestGame/renderer_tests.dm index 45c414786a..4e806e75c0 100644 --- a/TestGame/renderer_tests.dm +++ b/TestGame/renderer_tests.dm @@ -350,9 +350,93 @@ i++; if(i>8) i = 0 + /obj/plaque/animation_test data = "

Animation Test

Click the button to apply a series of animations to your mob

" + +/obj/button/animation_turf_test + name = "Animation Turf Test" + desc = "Click me to animate the turfs around you!" + var/i = 0 + + push() + if(i==0) + //grow and fade + usr << "grow and fade" + for(var/turf/T in range(src, 2)) + animate(T, transform = matrix()*2, alpha = 0, time = 5) + animate(transform = matrix(), alpha = 255, time = 5) + sleep(5) + if(i==1) + //spin + usr << "spin" + for(var/turf/T in range(src, 2)) + animate(T, transform = turn(matrix(), 120), time = 2, loop = 5) + animate(transform = turn(matrix(), 240), time = 2) + animate(transform = null, time = 2) + sleep(14) + if(i==2) + //colour + usr << "colour" + for(var/turf/T in range(src, 2)) + animate(T, color="#ff0000", time=5) + animate(color="#00ff00", time=5) + animate(color="#0000ff", time=5) + animate(color="#ffffff", time=5) + sleep(20) + if(i==3) + //colour matrix + usr << "colour matrix" + for(var/turf/T in range(src, 2)) + animate(T, color=list(0,0,1,0, 1,0,0,0, 0,1,0,0, 0,0,0,1, 0,0,0,0), time=5) + animate(color=list(0,1,0,0, 0,0,1,0, 1,0,0,0, 0,0,0,1, 0,0,0,0), time=5) + animate(color=list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0), time=5) + sleep(15) + if(i==4) + //parallel + usr << "parallel" + for(var/turf/T in range(src, 2)) + animate(T, color="#ff0000", time=4) + animate(T, transform = turn(matrix(), 120), time = 2, flags=ANIMATION_PARALLEL) + animate(transform = turn(matrix(), 240), time = 2) + animate(color="#ffffff", transform = null, time = 2) + sleep(6) + if(i==5) + //easings + usr << "easings" + for(var/turf/T in range(src, 2)) + animate(T, transform = matrix()*2, time = 5, easing=BACK_EASING) + animate(transform = matrix(), time = 5, easing=BOUNCE_EASING) + animate(transform = matrix()*2, time = 5, easing=ELASTIC_EASING) + animate(transform = matrix(), time = 5, easing=QUAD_EASING) + animate(transform = matrix()*2, time = 5, easing=CUBIC_EASING) + animate(transform = matrix(), time = 5, easing=SINE_EASING) + animate(transform = matrix()*2, time = 5, easing=CIRCULAR_EASING) + animate(transform = matrix(), time = 5, easing=JUMP_EASING) + if(i==6) + usr << "relative color" + for(var/turf/T in range(src, 2)) + animate(T, color="#ff0000", time=5, flags=ANIMATION_RELATIVE) + animate(color="#00ff00", time=5, flags=ANIMATION_RELATIVE) + animate(color="#0000ff", time=5, flags=ANIMATION_RELATIVE) + if(i==7) + usr << "relative transform" + for(var/turf/T in range(src, 2)) + animate(T, transform = matrix()*2, time = 5, flags=ANIMATION_RELATIVE) + animate(transform = matrix()*0.5, time = 5, flags=ANIMATION_RELATIVE) + if(i==8) + usr << "more relative tests" + for(var/turf/T in range(src, 2)) + animate(T, alpha=-125, pixel_x=16, time = 5, flags=ANIMATION_RELATIVE) + animate(alpha=125, pixel_x=-16, time = 5, flags=ANIMATION_RELATIVE) + i++; + if(i>8) + i = 0 + +/obj/plaque/animation_turf_test + data = "

Animation Turf Test

Click the button to apply a series of animations to the turfs your mob

" + //render order sanity checks /obj/order_test icon = 'icons/hanoi.dmi' From 7d981384553600562ce71716c5c901a25a564082 Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sat, 19 Oct 2024 22:36:49 +0100 Subject: [PATCH 52/54] need to make animated turfs have their own id somehow --- OpenDreamClient/Rendering/ClientAppearanceSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs index 051b6d2a7b..e08e594c20 100644 --- a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs +++ b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs @@ -82,7 +82,7 @@ public void OnNewAppearance(MsgNewAppearance e) { private void OnAnimation(AnimationEvent e) { if(e.Entity == NetEntity.Invalid && e.TurfId is not null) { //it's a turf or area - if(_turfIcons.TryGetValue(e.TurfId.Value, out var turfIcon)) + if(_turfIcons.TryGetValue(e.TurfId.Value-1, out var turfIcon)) LoadAppearance(e.TargetAppearanceId, targetAppearance => { turfIcon.StartAppearanceAnimation(targetAppearance.ToMutable(), e.Duration, e.Easing, e.Loop, e.Flags, e.Delay, e.ChainAnim); }); From 80da1eaf720f6a1ce1ffa4ed9dbdac8d238e870a Mon Sep 17 00:00:00 2001 From: amylizzlep Date: Sun, 20 Oct 2024 19:31:09 +0100 Subject: [PATCH 53/54] turf animations can go in another PR --- OpenDreamRuntime/AtomManager.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index ec07018d92..9e606453ee 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -551,7 +551,6 @@ public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasi appearance = MustGetAppearance(atom).ToMutable(); } else if (atom is DreamObjectTurf turf) { targetEntity = EntityUid.Invalid; - turfId = turf.Appearance.GetHashCode() + 1; appearance = turf.Appearance.ToMutable(); } else if (atom is DreamObjectArea area) { return; @@ -573,9 +572,9 @@ public void AnimateAppearance(DreamObject atom, TimeSpan duration, AnimationEasi // Don't send the updated appearance to clients, they will animate it DMISpriteSystem.SetSpriteAppearance(new(targetEntity, targetComponent), appearance, dirty: false); } else if (atom is DreamObjectTurf turf) { - //this is basically the only time it's okay to set turf.Appearance outside of DreamMapManager.SetTurfAppearance() - //because we don't want to notify the client of the appearance change - turf.Appearance = AppearanceSystem!.AddAppearance(appearance); + //TODO: turf appearances are just set to the end appearance, they do not get properly animated + _dreamMapManager.SetTurfAppearance(turf, appearance); + turfId = appearance.GetHashCode()+1; } else if (atom is DreamObjectArea area) { //fuck knows, this will trigger a bunch of turf updates to? idek } From aa3a990dc8a1b83205f3ecb66515e6579810e5e8 Mon Sep 17 00:00:00 2001 From: amylizzle Date: Mon, 21 Oct 2024 13:53:42 +0100 Subject: [PATCH 54/54] fixed it! --- OpenDreamRuntime/AtomManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index 9e606453ee..e942eec318 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -518,7 +518,7 @@ public void SetAtomAppearance(DreamObject atom, MutableIconAppearance appearance DMISpriteSystem.SetSpriteAppearance(new(movable.Entity, movable.SpriteComponent), appearance); } else if (atom is DreamObjectImage image) { if(image.IsMutableAppearance) - image.MutableAppearance = appearance; + image.MutableAppearance = new(appearance); //this needs to be a copy else DMISpriteSystem.SetSpriteAppearance(new(image.Entity, image.SpriteComponent!), appearance); } else if (atom is DreamObjectArea area) {