fix: stop items from insta-decaying when decay eligibility is restored without a move (#2583)
## Summary A GM flipping `Movable` back on for a long-frozen item made it vanish within one scheduler tick. The setter registered the item with a deadline computed from its stale `LastMoved`, so `ProcessActiveQueue` deleted it almost immediately. The pre-#2311 save-time sweep had the same semantics, just hidden behind the save cadence. The same failure existed for `Visible` and `Spawner` transitions. `LastMoved` is deliberately left meaning actual movement — it feeds vendor inventory expiry and house moving-crate checks — so the fix does not rewrite it for state changes. ## Changes - **`DecayResetTime`** (CompactInfo-backed): the decay countdown runs from the later of `LastMoved` and this stamp. `RestartDecay()` stamps it only when the item can decay and the stamp extends the current deadline, so hot paths with a fresh `LastMoved` allocate nothing. - **`Movable`/`Visible`/`Spawner` setters** call `RestartDecay()` instead of registering a stale deadline. - **Region-refusal retry** in `DecayScheduler` uses `RestartDecay()` instead of rewriting `LastMoved`. - **Persistence**: the stamp survives save/load as a `WriteDeltaTime` delta under `SaveFlag.DecayReset` (to become `WriteAnchoredTime` once the save-time anchor is ported) (Item serialization v10), so a restart mid-window no longer deletes the item. - **`LastMoved` setter** drops a superseded stamp so the `CompactInfo` can collapse instead of being held (~40 bytes) forever. - **Raw `Map` setter** now counts as a move for parentless items: it stamps `LastMoved` and updates decay registration, closing the gap where an item moved out of `Map.Internal` via the setter never decayed. - **`LiftItemDupe`**: the remainder of a partially lifted *ground* stack was placed via raw `Location`/`Map` assignments and never enrolled for decay (lingering-trash leak since #2311) — now enrolled via the Map setter. Parented remainders get their map from `AddItem` (parent first, then map), so container splits never transit the scheduler.
This commit is contained in:
parent
fd27b7a3c9
commit
971d7b6a77
4 changed files with 397 additions and 10 deletions
|
|
@ -311,7 +311,7 @@ public class DecayScheduler : Timer
|
|||
|
||||
if (timeUntilDecay > _bucketInterval)
|
||||
{
|
||||
// Item was moved (SetLastMoved called) - re-bucket or move to overflow
|
||||
// Deadline was pushed out (SetLastMoved/RestartDecay) - re-bucket or move to overflow
|
||||
if (timeUntilDecay > _totalBucketSpan)
|
||||
{
|
||||
// Extended beyond total span - move to overflow
|
||||
|
|
@ -429,7 +429,7 @@ public class DecayScheduler : Timer
|
|||
{
|
||||
// Refused by the region. Restart the clock rather than dropping the item, which has
|
||||
// already left the queue; re-registering as-is would spin on a due time in the past.
|
||||
item.SetLastMoved();
|
||||
item.RestartDecay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue