Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Appearance Weakrefs #1815

Merged
merged 95 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
0cd7159
well it compiles...
amylizzle May 28, 2024
8dd14ad
plus one for you
amylizzle May 28, 2024
b985a60
work you fucker
amylizzle May 29, 2024
f2bf4b1
close, but no cigar
amylizzle May 29, 2024
a3d195d
gotcha
amylizzle May 29, 2024
68c058e
linter and cleanup
amylizzle May 29, 2024
24651fb
fine, be like that
amylizzle May 29, 2024
f32412a
break everything
amylizzle May 29, 2024
8487c93
I am confused and bewlidered
amylizzle May 29, 2024
f40b43b
Merge branch 'master' into appearanceRefCounts
amylizzle May 29, 2024
8839dfd
Merge branch 'master' into appearanceRefCounts
amylizzle Jun 3, 2024
b9fcfc9
lint
amylizzle Jun 7, 2024
189d3ef
more lint
amylizzle Jun 7, 2024
b96cf5e
Merge branch 'master' into appearanceRefCounts
amylizzle Jun 7, 2024
1b03b2d
gross
amylizzle Jun 10, 2024
1fb5f03
move a couple broken tests
amylizzle Jun 10, 2024
b2bb950
lint
amylizzle Jun 10, 2024
93df5d4
more lint
amylizzle Jun 10, 2024
6d8e331
Merge remote-tracking branch 'upstream/master' into appearanceRefCounts
amylizzle Jun 14, 2024
6e1a02d
Update OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs
amylizzle Jun 14, 2024
79826de
Merge branch 'master' into appearanceRefCounts
amylizzle Jul 9, 2024
a5e76dd
area appearance, but memory churn
amylizzle Jul 9, 2024
0216403
Merge branch 'master' into appearanceRefCounts
amylizzle Oct 6, 2024
be94ae0
immutable start
amylizzle Oct 6, 2024
45a66c6
nobody keeps iconappearances anymore, only ids
amylizzle Oct 6, 2024
ab2ecfc
moreeee
amylizzle Oct 7, 2024
faa731d
faster area appearance editing
amylizzle Oct 7, 2024
d414565
bits
amylizzle Oct 7, 2024
294e84b
most of lists
amylizzle Oct 7, 2024
e6ea8e3
server compiles
amylizzle Oct 7, 2024
8b32fec
that is surprisingly not that broken
amylizzle Oct 7, 2024
2f9a7e6
fix order of ops on sprite creation
amylizzle Oct 7, 2024
cd5268b
fix turfs
amylizzle Oct 7, 2024
9eb2e54
fix one test
amylizzle Oct 7, 2024
d84c875
oops
amylizzle Oct 7, 2024
85ffe1c
weakrefs motherfucker
amylizzle Oct 11, 2024
ca1f6b7
bugs
amylizzle Oct 11, 2024
bd3f0e1
fixed it
amylizzle Oct 12, 2024
1561128
tracking down weird bugs
amylizzle Oct 12, 2024
5a991da
half done turf & area anims
amylizzle Oct 12, 2024
fc84fe9
Merge branch 'master' into appearanceRefCounts
amylizzle Oct 12, 2024
e2cd921
/mutable_appearance
amylizzle Oct 13, 2024
ed74230
fix colormatrix hashes
amylizzle Oct 13, 2024
babc6fe
enormous amount of linting
amylizzle Oct 13, 2024
b2d76af
missed one
amylizzle Oct 13, 2024
9db67b0
missed another
amylizzle Oct 13, 2024
2674ef7
gross
amylizzle Oct 13, 2024
774880a
move tests to integrated test
amylizzle Oct 13, 2024
04bee7d
don't love this
amylizzle Oct 13, 2024
9cc8285
Merge branch 'master' into appearanceRefCounts
amylizzle Oct 13, 2024
0485fe2
you know what? this PR should be larger
amylizzle Oct 13, 2024
897b62d
fun adventures in abusing networking
amylizzle Oct 14, 2024
b1f59e1
idk why this is necessary
amylizzle Oct 14, 2024
fea8e19
Merge branch 'master' into appearanceRefCounts
amylizzle Oct 14, 2024
16ca9de
overlays are confusing
amylizzle Oct 16, 2024
4391316
Overlays are immutable
amylizzle Oct 16, 2024
f5e4c14
shoulda just done it this way the first time
amylizzle Oct 16, 2024
db99b72
housekeeping
amylizzle Oct 18, 2024
bf40bba
fix a couple runtimes on paradise
amylizzle Oct 18, 2024
f4d793d
add turf anim test
amylizzle Oct 19, 2024
7d98138
need to make animated turfs have their own id somehow
amylizzle Oct 19, 2024
80da1ea
turf animations can go in another PR
amylizzle Oct 20, 2024
aa3a990
fixed it!
amylizzle Oct 21, 2024
4bc02c7
Merge branch 'master' into appearanceRefCounts
amylizzle Oct 22, 2024
e367a0f
reduce churn
amylizzle Oct 28, 2024
ea79f29
oh that should be locked
amylizzle Oct 28, 2024
104fa56
Merge branch 'master' into appearanceRefCounts
amylizzle Oct 29, 2024
02d4001
try caching mutables on the client?
amylizzle Oct 29, 2024
47a644c
just use immutables instead of doing shitloads of copying
amylizzle Oct 29, 2024
100150e
fix dir inheritence
amylizzle Oct 30, 2024
3d3feb9
remove that
amylizzle Oct 30, 2024
c18de10
don't double copy appearances
amylizzle Oct 30, 2024
c63f765
Merge branch 'master' into appearanceRefCounts
amylizzle Nov 8, 2024
2a11400
fix client image bugs
amylizzle Nov 11, 2024
f69f4be
Merge remote-tracking branch 'upstream/master' into appearanceRefCounts
amylizzle Nov 28, 2024
6945e90
fix merge
amylizzle Nov 28, 2024
dd9bb44
Merge branch 'master' into appearanceRefCounts
amylizzle Nov 30, 2024
b8ea258
Merge branch 'master' into appearanceRefCounts
amylizzle Dec 4, 2024
ccd1651
rename appearances
amylizzle Dec 26, 2024
425cf9b
no more hashes
amylizzle Dec 27, 2024
7e75397
lint roller
amylizzle Dec 27, 2024
de0e32d
try a thing
amylizzle Dec 29, 2024
b0b3981
Merge branch 'master' into appearanceRefCounts
amylizzle Dec 29, 2024
13b01fd
fix failing to GC
amylizzle Dec 29, 2024
ed8b9f0
remove collect call
amylizzle Dec 29, 2024
52c6f55
remove the TTL queue
amylizzle Dec 29, 2024
ab39b71
Only register movables' appearances when in PVS range
wixoaGit Dec 29, 2024
719f315
fix
amylizzle Dec 29, 2024
dce94c8
Add the ability to debug turf icons
wixoaGit Jan 1, 2025
3a8c33b
Fix off-by-one in turf icon debugging
wixoaGit Jan 2, 2025
5593112
Update the area overlay when changing a turf's area
wixoaGit Jan 2, 2025
732301b
Mark `ColorMatrix.Equals()` as pure
wixoaGit Jan 3, 2025
4b548c5
Make MutableAppearance disposable, allowing for pooling
wixoaGit Jan 3, 2025
26be28e
Code cleanup
wixoaGit Jan 5, 2025
1e54318
Fix some NREs
wixoaGit Jan 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions OpenDreamClient/Rendering/ClientAppearanceSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ internal sealed class ClientAppearanceSystem : SharedAppearanceSystem {
public override void Initialize() {
SubscribeNetworkEvent<AllAppearancesEvent>(OnAllAppearances);
SubscribeNetworkEvent<NewAppearanceEvent>(OnNewAppearance);
SubscribeNetworkEvent<RemoveAppearanceEvent>(e => _appearances.Remove(e.AppearanceId));
SubscribeNetworkEvent<AnimationEvent>(OnAnimation);
SubscribeLocalEvent<DMISpriteComponent, WorldAABBEvent>(OnWorldAABB);
}
Expand Down
18 changes: 14 additions & 4 deletions OpenDreamRuntime/AtomManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public sealed class AtomManager {
private ServerVerbSystem VerbSystem => _verbSystem ??= _entitySystemManager.GetEntitySystem<ServerVerbSystem>();
private ServerAppearanceSystem? _appearanceSystem;
private ServerVerbSystem? _verbSystem;
private DMISpriteSystem DMISpriteSystem => _dmiSpriteSystem ??= _entitySystemManager.GetEntitySystem<DMISpriteSystem>();
private DMISpriteSystem? _dmiSpriteSystem;

// ReSharper disable ForCanBeConvertedToForeach (the collections could be added to)
public IEnumerable<DreamObjectAtom> EnumerateAtoms(TreeEntry? filterType = null) {
Expand Down Expand Up @@ -191,7 +193,7 @@ public EntityUid CreateMovableEntity(DreamObjectMovable movable) {
var entity = _entityManager.SpawnEntity(null, new MapCoordinates(0, 0, MapId.Nullspace));

DMISpriteComponent sprite = _entityManager.AddComponent<DMISpriteComponent>(entity);
sprite.SetAppearance(GetAppearanceFromDefinition(movable.ObjectDefinition));
DMISpriteSystem.SetSpriteAppearance(new(entity, sprite), GetAppearanceFromDefinition(movable.ObjectDefinition));

_entityToAtom.Add(entity, movable);
return entity;
Expand Down Expand Up @@ -473,7 +475,6 @@ public bool TryGetAppearance(DreamObject atom, [NotNullWhen(true)] out IconAppea
public void UpdateAppearance(DreamObject atom, Action<IconAppearance> update) {
var appearance = MustGetAppearance(atom);
appearance = (appearance != null) ? new(appearance) : new(); // Clone the appearance

update(appearance);
SetAtomAppearance(atom, appearance);
}
Expand All @@ -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<DMISpriteComponent> 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<IconAppearance> animate) {
if (atom is not DreamObjectMovable movable)
return; //Animating non-movables is unimplemented TODO: should handle images and maybe filters
Expand All @@ -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);

Expand Down Expand Up @@ -591,6 +600,7 @@ public IconAppearance GetAppearanceFromDefinition(DreamObjectDefinition def) {
}

_definitionAppearanceCache.Add(def, appearance);
AppearanceSystem.IncreaseAppearanceRefCount(appearance);
return appearance;
}

Expand Down
1 change: 1 addition & 0 deletions OpenDreamRuntime/DreamManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ public string CreateRef(DreamValue value) {
refType = RefType.DreamAppearance;
_appearanceSystem ??= _entitySystemManager.GetEntitySystem<ServerAppearanceSystem>();
idx = (int)_appearanceSystem.AddAppearance(appearance);
_appearanceSystem.IncreaseAppearanceRefCount(appearance);
} else if (value.TryGetValueAsDreamResource(out var refRsc)) {
refType = RefType.DreamResource;
idx = refRsc.Id;
Expand Down
40 changes: 32 additions & 8 deletions OpenDreamRuntime/Objects/Types/DreamObjectImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
namespace OpenDreamRuntime.Objects.Types;

public sealed class DreamObjectImage : DreamObject {
public IconAppearance? Appearance;
private IconAppearance? _appearance;
public IconAppearance? Appearance { get => _appearance; set => SetAppearance(value); }

private DreamObject? _loc;
private DreamList _overlays;
Expand Down Expand Up @@ -42,33 +43,43 @@
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);
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(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;
}
}
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) {
Expand Down Expand Up @@ -111,7 +122,7 @@
Appearance = newAppearance;
if(_entity != EntityUid.Invalid) {
DMISpriteComponent sprite = EntityManager.GetComponent<DMISpriteComponent>(_entity);
sprite.SetAppearance(Appearance!);
AtomManager.SetSpriteAppearance(new(_entity, sprite), Appearance);
}
break;
case "loc":
Expand Down Expand Up @@ -206,10 +217,12 @@
}
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<DMISpriteComponent>(_entity);
sprite.SetAppearance(Appearance!);
AtomManager.SetSpriteAppearance(new(_entity, sprite), Appearance);
}
break;
}
Expand All @@ -231,12 +244,23 @@
if(_entity == EntityUid.Invalid) {
_entity = EntityManager.SpawnEntity(null, new MapCoordinates(0, 0, MapId.Nullspace));
DMISpriteComponent sprite = EntityManager.AddComponent<DMISpriteComponent>(_entity);
sprite.SetAppearance(Appearance!);
if(Appearance is not null)
AtomManager.SetSpriteAppearance(new(_entity, sprite), Appearance);
}
return _entity;
}

public void SetAppearance(IconAppearance? appearance) {
if(appearance is not null)
AppearanceSystem!.IncreaseAppearanceRefCount(appearance);
if(_appearance is not null)
AppearanceSystem!.DecreaseAppearanceRefCount(_appearance);
_appearance = appearance;
}

protected override void HandleDeletion() {
if(_appearance is not null)
AppearanceSystem!.DecreaseAppearanceRefCount(_appearance);
if(_entity != EntityUid.Invalid) {
EntityManager.DeleteEntity(_entity);
}
Expand Down
15 changes: 6 additions & 9 deletions OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,7 @@ public class DreamObjectMovable : DreamObjectAtom {

private string? ScreenLoc {
get => _screenLoc;
set {
_screenLoc = value;
if (!EntityManager.TryGetComponent<DMISpriteComponent>(Entity, out var sprite))
return;

sprite.ScreenLocation = !string.IsNullOrEmpty(value) ?
new ScreenLocation(value) :
new ScreenLocation(0, 0, 0, 0);
}
set => SetScreenLoc(value);
}

private string? _screenLoc;
Expand Down Expand Up @@ -198,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));
}
}
16 changes: 15 additions & 1 deletion OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
public readonly int X, Y, Z;
public readonly IDreamMapManager.Cell Cell;
public readonly TurfContentsList Contents;
public int AppearanceId;
public int AppearanceId { get => _appearanceId!.Value; set => SetAppearanceId(value); }

private int? _appearanceId;

public DreamObjectTurf(DreamObjectDefinition objectDefinition, int x, int y, int z, IDreamMapManager.Cell cell) : base(objectDefinition) {
X = x;
Expand Down Expand Up @@ -63,4 +65,16 @@
break;
}
}

public void SetAppearanceId(int appearanceId) {
AppearanceSystem!.IncreaseAppearanceRefCount(appearanceId);
if (_appearanceId is not null) {
AppearanceSystem!.DecreaseAppearanceRefCount(_appearanceId.Value);
}

_appearanceId = appearanceId;



}
}
33 changes: 10 additions & 23 deletions OpenDreamRuntime/Rendering/DMISpriteComponent.cs
Original file line number Diff line number Diff line change
@@ -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;
}

26 changes: 23 additions & 3 deletions OpenDreamRuntime/Rendering/DMISpriteSystem.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,41 @@
using OpenDreamShared.Rendering;
using OpenDreamShared.Dream;
using OpenDreamShared.Rendering;
using Robust.Shared.GameStates;

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<DMISpriteComponent, ComponentGetState>(GetComponentState);
_appearance = _entitySystemManager.GetEntitySystem<ServerAppearanceSystem>();
}

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);
}

public void SetSpriteAppearance(Entity<DMISpriteComponent> 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<DMISpriteComponent> ent, ScreenLocation screenLocation) {
DMISpriteComponent component = ent.Comp;
component.ScreenLocation = screenLocation;
Dirty(ent, component);
}
}
Loading
Loading