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

Open
wants to merge 64 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
64 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
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)
Fixed Show fixed Hide fixed
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) {
Fixed Show fixed Hide fixed
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);
Fixed Show fixed Hide fixed
}
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;



}
Fixed Show fixed Hide fixed
}
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]
Fixed Show fixed Hide fixed
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