fix: drop the decay reset stamp when an item loses eligibility
A stamped item that was re-frozen, re-hidden, spawner-linked, or moved into a container programmatically kept its stamp - and the CompactInfo holding it - for as long as it stayed ineligible. UpdateDecayRegistration's ineligible branch now clears the stamp; every re-eligibility path re-anchors, so only the allocation was at stake. The guarded setter makes the hot-path cost a field read and two compares, with no allocation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
8067a7d0aa
commit
046452d7c7
2 changed files with 69 additions and 0 deletions
|
|
@ -374,6 +374,69 @@ public class DecayRegistrationTests
|
|||
}
|
||||
}
|
||||
|
||||
// Losing decay eligibility makes the stamp meaningless; it must be dropped so the
|
||||
// CompactInfo is not held for as long as the item stays ineligible.
|
||||
[Fact]
|
||||
public void ItemBecomingIneligible_DropsTheDecayResetStamp()
|
||||
{
|
||||
var start = Core._now;
|
||||
|
||||
try
|
||||
{
|
||||
var item = new Item(0x1234);
|
||||
item.MoveToWorld(new Point3D(115, 100, 0), Map.Felucca);
|
||||
|
||||
item.Movable = false;
|
||||
Core._now = start + TimeSpan.FromDays(30);
|
||||
item.Movable = true;
|
||||
|
||||
Assert.NotEqual(default, item.DecayResetTime);
|
||||
|
||||
item.Movable = false;
|
||||
|
||||
Assert.Equal(default, item.DecayResetTime);
|
||||
|
||||
item.Delete();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Core._now = start;
|
||||
}
|
||||
}
|
||||
|
||||
// Moving a stamped item into a container programmatically (no drop, no SetLastMoved)
|
||||
// must also drop the stamp.
|
||||
[Fact]
|
||||
public void StampedItemAddedToContainer_DropsTheDecayResetStamp()
|
||||
{
|
||||
var start = Core._now;
|
||||
|
||||
try
|
||||
{
|
||||
var pack = new Container(0xE75);
|
||||
pack.MoveToWorld(new Point3D(116, 100, 0), Map.Felucca);
|
||||
|
||||
var item = new Item(0x1234);
|
||||
item.MoveToWorld(new Point3D(117, 100, 0), Map.Felucca);
|
||||
|
||||
item.Movable = false;
|
||||
Core._now = start + TimeSpan.FromDays(30);
|
||||
item.Movable = true;
|
||||
|
||||
Assert.NotEqual(default, item.DecayResetTime);
|
||||
|
||||
pack.AddItem(item);
|
||||
|
||||
Assert.Equal(default, item.DecayResetTime);
|
||||
|
||||
pack.Delete();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Core._now = start;
|
||||
}
|
||||
}
|
||||
|
||||
// A raw Map assignment (e.g. a GM changing Map through props) is a move: it must enroll
|
||||
// an untracked item for decay instead of leaving it to linger forever.
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -2429,6 +2429,12 @@ public partial class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropert
|
|||
{
|
||||
DecayScheduler.Register(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No countdown to anchor while ineligible; drop the stamp so the CompactInfo
|
||||
// can collapse. Re-eligibility always re-anchors.
|
||||
DecayResetTime = default;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLastMoved()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue