Skip to content

Commit 8111e5c

Browse files
committed
Ensure that the animation list and lookup are kept appropriately in sync AscensionGameDev#2538
1 parent 38d0d21 commit 8111e5c

File tree

7 files changed

+146
-102
lines changed

7 files changed

+146
-102
lines changed

Intersect.Client.Core/Entities/Animation.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,7 @@ public void DisposeNextDraw()
304304
mDisposeNextDraw = true;
305305
}
306306

307-
public bool Disposed()
308-
{
309-
return disposed;
310-
}
307+
public bool IsDisposed => disposed;
311308

312309
public void SetPosition(float worldX, float worldY, int mapx, int mapy, Guid mapId, Direction dir, int z = 0)
313310
{

Intersect.Client.Core/Entities/Critter.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,10 @@ public Critter(MapInstance map, byte x, byte y, MapCritterAttribute att) : base(
2121

2222
//setup Sprite & Animation
2323
Sprite = att.Sprite;
24-
var anim = AnimationBase.Get(att.AnimationId);
25-
if (anim != null)
24+
25+
if (AnimationBase.TryGet(att.AnimationId, out var animationDescriptor))
2626
{
27-
var animInstance = new Animation(anim, true);
28-
Animations.Add(animInstance);
27+
TryAddAnimation(new Animation(animationDescriptor, true));
2928
}
3029

3130
//Define Location

Intersect.Client.Core/Entities/Entity.cs

Lines changed: 120 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics.CodeAnalysis;
12
using Intersect.Client.Core;
23
using Intersect.Client.Entities.Events;
34
using Intersect.Client.Entities.Projectiles;
@@ -28,10 +29,8 @@ public partial class Entity : IEntity
2829
{
2930
public int AnimationFrame { get; set; }
3031

31-
//Entity Animations
32-
public readonly List<Animation> Animations = [];
33-
34-
public readonly Dictionary<AnimationSource, Animation> AnimationsBySource = [];
32+
private readonly List<Animation> _animations = [];
33+
private readonly Dictionary<AnimationSource, Animation> _animationsBySource = [];
3534

3635
//Animation Timer (for animated sprites)
3736
public long AnimationTimer { get; set; }
@@ -67,6 +66,8 @@ public partial class Entity : IEntity
6766

6867
private Guid[] _equipment = new Guid[Options.Instance.Equipment.Slots.Count];
6968

69+
public bool HasAnimations => _animations.Count > 0;
70+
7071
public Guid[] Equipment
7172
{
7273
get => _equipment;
@@ -375,7 +376,7 @@ public virtual void Load(EntityPacket? packet)
375376
}
376377
}
377378

378-
foreach (var anim in Animations)
379+
foreach (var anim in _animations)
379380
{
380381
animsToClear.Add(anim);
381382
if (!anim.InfiniteLoop)
@@ -405,7 +406,7 @@ public virtual void Load(EntityPacket? packet)
405406
}
406407
}
407408

408-
ClearAnimations(animsToClear);
409+
RemoveAnimations(animsToClear);
409410
AddAnimations(animsToAdd);
410411

411412
Vital = packet.Vital;
@@ -479,11 +480,11 @@ public virtual void Load(EntityPacket? packet)
479480
}
480481
}
481482

482-
public void AddAnimations(List<AnimationBase> anims)
483+
public void AddAnimations(List<AnimationBase> animationDescriptors)
483484
{
484-
foreach (var anim in anims)
485+
foreach (var animationDescriptor in animationDescriptors)
485486
{
486-
Animations.Add(new Animation(anim, true, false, -1, this));
487+
TryAddAnimation(new Animation(animationDescriptor, true, false, -1, this));
487488
}
488489
}
489490

@@ -515,16 +516,19 @@ public virtual bool IsAllyOf(Player en)
515516
return false;
516517
}
517518

518-
public void ClearAnimations(List<Animation>? anims)
519+
public void ClearAnimations() => RemoveAnimations(_animations);
520+
521+
public void RemoveAnimations(IEnumerable<Animation> animations, bool dispose = true)
519522
{
520-
anims ??= Animations;
521-
if (anims.Count > 0)
523+
var animationsToRemove = animations.ToArray();
524+
foreach (var animation in animationsToRemove)
522525
{
523-
for (var i = 0; i < anims.Count; i++)
526+
if (dispose)
524527
{
525-
anims[i].Dispose();
526-
_ = Animations.Remove(anims[i]);
528+
animation.Dispose();
527529
}
530+
531+
_ = TryRemoveAnimation(animation);
528532
}
529533
}
530534

@@ -537,7 +541,7 @@ public virtual void Dispose()
537541
_ = RenderList.Remove(this);
538542
}
539543

540-
ClearAnimations(null);
544+
ClearAnimations();
541545
GC.SuppressFinalize(this);
542546
mDisposed = true;
543547
}
@@ -758,6 +762,7 @@ public virtual bool Update()
758762
{
759763
for (var z = 0; z < Options.Instance.Equipment.Slots.Count; z++)
760764
{
765+
var equipmentAnimation = EquipmentAnimations[z];
761766
if (Equipment[z] != Guid.Empty && (this != Globals.Me || MyEquipment[z] < Options.Instance.Player.MaxInventory))
762767
{
763768
var itemId = Guid.Empty;
@@ -774,47 +779,30 @@ public virtual bool Update()
774779
itemId = Equipment[z];
775780
}
776781

777-
var itm = ItemBase.Get(itemId);
778-
AnimationBase? anim = null;
779-
if (itm != null)
782+
if (ItemBase.TryGet(itemId, out var itemDescriptor) &&
783+
itemDescriptor.EquipmentAnimation is { } animationDescriptor)
780784
{
781-
anim = itm.EquipmentAnimation;
782-
}
783-
784-
if (anim != null)
785-
{
786-
if (EquipmentAnimations[z] != null &&
787-
(EquipmentAnimations[z]!.MyBase != anim || EquipmentAnimations[z]!.Disposed()))
785+
if (equipmentAnimation != null &&
786+
(equipmentAnimation.MyBase != animationDescriptor || equipmentAnimation.IsDisposed))
788787
{
789-
EquipmentAnimations[z]!.Dispose();
790-
_ = Animations.Remove(EquipmentAnimations[z]!);
788+
TryRemoveAnimation(equipmentAnimation, dispose: true);
791789
EquipmentAnimations[z] = null;
792790
}
793791

794-
if (EquipmentAnimations[z] == null)
795-
{
796-
EquipmentAnimations[z] = new Animation(anim, true, true, -1, this);
797-
Animations.Add(EquipmentAnimations[z]!);
798-
}
792+
equipmentAnimation = new Animation(animationDescriptor, true, true, -1, this);
793+
EquipmentAnimations[z] = equipmentAnimation;
794+
_animations.Add(equipmentAnimation);
799795
}
800-
else
796+
else if (equipmentAnimation != null)
801797
{
802-
if (EquipmentAnimations[z] != null)
803-
{
804-
EquipmentAnimations[z]!.Dispose();
805-
_ = Animations.Remove(EquipmentAnimations[z]!);
806-
EquipmentAnimations[z] = null;
807-
}
798+
TryRemoveAnimation(equipmentAnimation, dispose: true);
799+
EquipmentAnimations[z] = null;
808800
}
809801
}
810-
else
802+
else if (equipmentAnimation != null)
811803
{
812-
if (EquipmentAnimations[z] != null)
813-
{
814-
EquipmentAnimations[z]!.Dispose();
815-
_ = Animations.Remove(EquipmentAnimations[z]!);
816-
EquipmentAnimations[z] = null;
817-
}
804+
TryRemoveAnimation(equipmentAnimation, dispose: true);
805+
EquipmentAnimations[z] = null;
818806
}
819807
}
820808
}
@@ -840,30 +828,30 @@ public virtual bool Update()
840828

841829
CalculateOrigin();
842830

843-
List<Animation>? animsToRemove = null;
844-
foreach (var animInstance in Animations)
831+
List<Animation>? disposedAnimations = null;
832+
foreach (var animation in _animations)
845833
{
846-
animInstance.Update();
834+
animation.Update();
847835

848836
//If disposed mark to be removed and continue onward
849-
if (animInstance.Disposed())
837+
if (animation.IsDisposed)
850838
{
851-
animsToRemove ??= [];
852-
animsToRemove.Add(animInstance);
839+
disposedAnimations ??= [];
840+
disposedAnimations.Add(animation);
853841
continue;
854842
}
855843

856844
if (IsStealthed || IsHidden)
857845
{
858-
animInstance.Hide();
846+
animation.Hide();
859847
}
860848
else
861849
{
862-
animInstance.Show();
850+
animation.Show();
863851
}
864852

865-
var animationDirection = animInstance.AutoRotate ? Dir : default;
866-
animInstance.SetPosition(
853+
var animationDirection = animation.AutoRotate ? Dir : default;
854+
animation.SetPosition(
867855
(int)Math.Ceiling(Center.X),
868856
(int)Math.Ceiling(Center.Y),
869857
X,
@@ -873,12 +861,9 @@ public virtual bool Update()
873861
);
874862
}
875863

876-
if (animsToRemove != null)
864+
if (disposedAnimations != null)
877865
{
878-
foreach (var anim in animsToRemove)
879-
{
880-
_ = Animations.Remove(anim);
881-
}
866+
RemoveAnimations(disposedAnimations);
882867
}
883868

884869
mLastUpdate = Timing.Global.Milliseconds;
@@ -888,6 +873,79 @@ public virtual bool Update()
888873
return true;
889874
}
890875

876+
public bool TryAddAnimation(Animation animation, AnimationSource animationSource = default)
877+
{
878+
if (animationSource != default)
879+
{
880+
if (_animationsBySource.TryGetValue(animationSource, out var existingAnimation))
881+
{
882+
if (!TryRemoveAnimation(existingAnimation, animationSource))
883+
{
884+
return false;
885+
}
886+
}
887+
}
888+
889+
_animationsBySource[animationSource] = animation;
890+
_animations.Add(animation);
891+
return true;
892+
}
893+
894+
public bool TryRemoveAnimation(AnimationSource animationSource, bool dispose = false) => TryRemoveAnimation(
895+
animationSource: animationSource,
896+
dispose: dispose,
897+
animation: out _
898+
);
899+
900+
public bool TryRemoveAnimation(AnimationSource animationSource, [NotNullWhen(true)] out Animation? animation) =>
901+
TryRemoveAnimation(animationSource: animationSource, dispose: false, animation: out animation);
902+
903+
public bool TryRemoveAnimation(
904+
AnimationSource animationSource,
905+
bool dispose,
906+
[NotNullWhen(true)] out Animation? animation
907+
)
908+
{
909+
if (!_animationsBySource.Remove(animationSource, out animation))
910+
{
911+
return false;
912+
}
913+
914+
_animations.Remove(animation);
915+
916+
if (dispose)
917+
{
918+
animation.Dispose();
919+
}
920+
921+
return true;
922+
}
923+
924+
public bool RemoveAnimationIfExists(AnimationSource animationSource, bool dispose = false)
925+
{
926+
return !_animationsBySource.ContainsKey(animationSource) ||
927+
TryRemoveAnimation(animationSource: animationSource, dispose: dispose);
928+
}
929+
930+
public bool TryRemoveAnimation(Animation animation, bool dispose = false) => TryRemoveAnimation(
931+
animation: animation,
932+
animationSource: animation.Source,
933+
dispose: dispose
934+
);
935+
936+
public bool TryRemoveAnimation(Animation animation, AnimationSource animationSource, bool dispose = false)
937+
{
938+
var removedFromSourceLookup = animationSource != default && _animationsBySource.Remove(animationSource);
939+
var removedFromList = _animations.Remove(animation);
940+
941+
if (dispose)
942+
{
943+
animation.Dispose();
944+
}
945+
946+
return removedFromList || removedFromSourceLookup;
947+
}
948+
891949
public virtual int CalculateAttackTime()
892950
{
893951
//If this is an npc we don't know it's attack time. Luckily the server provided it!

Intersect.Client.Core/Entities/Events/Event.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ protected override Pointf CenterOffset
3636
switch (Graphic.Type)
3737
{
3838
case EventGraphicType.None:
39-
return Animations.Count == 0 ? Pointf.Empty : Pointf.UnitY * Options.Instance.Map.TileHeight / 2f;
39+
return HasAnimations ? Pointf.UnitY * Options.Instance.Map.TileHeight / 2f : Pointf.Empty;
4040

4141
case EventGraphicType.Sprite:
4242
return base.CenterOffset;
@@ -264,7 +264,7 @@ public override float GetTop(int overrideHeight = -1)
264264
break;
265265

266266
case EventGraphicType.None:
267-
heightScale = Animations.Count > 0 ? 1 : 0.5f;
267+
heightScale = HasAnimations ? 1 : 0.5f;
268268
break;
269269

270270
default:

Intersect.Client.Core/Entities/Resource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public override void Dispose()
137137
RenderList = null;
138138
}
139139

140-
ClearAnimations(null);
140+
ClearAnimations();
141141
GC.SuppressFinalize(this);
142142
mDisposed = true;
143143
}

Intersect.Client.Core/Maps/MapInstance.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public void MarkLoadFinished()
7777

7878
IReadOnlyList<IMapAnimation> IMapInstance.Animations => LocalAnimations.Values.ToList();
7979

80-
public Dictionary<Guid, Entity> LocalEntities { get; set; } = new Dictionary<Guid, Entity>();
80+
public Dictionary<Guid, Entity> LocalEntities { get; } = [];
8181

8282
IReadOnlyList<IEntity> IMapInstance.Entities => LocalEntities.Values.ToList();
8383

@@ -284,7 +284,7 @@ public void Update(bool isLocal)
284284

285285
foreach (var (animationId, animation) in LocalAnimations)
286286
{
287-
if (animation.Disposed())
287+
if (animation.IsDisposed)
288288
{
289289
LocalAnimations.TryRemove(animationId, out _);
290290
}
@@ -757,7 +757,10 @@ public void AddTileAnimation(
757757

758758
private void HideActiveAnimations()
759759
{
760-
LocalEntities?.Values.ToList().ForEach(entity => entity?.ClearAnimations(null));
760+
foreach (var entity in LocalEntities.Values.ToList())
761+
{
762+
entity.ClearAnimations();
763+
}
761764
foreach (var anim in LocalAnimations)
762765
{
763766
anim.Value?.Dispose();
@@ -1577,9 +1580,9 @@ public void AddEvent(Guid evtId, EventEntityPacket packet)
15771580
}
15781581
}
15791582

1580-
public new static MapInstance Get(Guid id)
1583+
public static new MapInstance? Get(Guid id)
15811584
{
1582-
return MapInstance.Lookup.Get<MapInstance>(id);
1585+
return Lookup.Get<MapInstance>(id);
15831586
}
15841587

15851588
public static bool TryGet(Guid id, out MapInstance instance)

0 commit comments

Comments
 (0)