ModernUO/Projects/UOContent/Items/Containers
Kamron Batman c1442aff3e
fix: Stop treasure chest guardian spawn farming via stack splits (#2568)
### Summary

Players reported an exploit: decipher a treasure map, then run a ClassicUO/Razor organizer agent that pulls the gold out of the chest in small amounts. Each pull spawned more monsters, turning one chest into an unbounded farmable spawn generator.

### Root cause

`TreasureMapChest.OnItemLifted` grants a 10% guardian spawn roll per first-time-lifted item, deduplicated by the instance-keyed `_lifted` set. But a partial lift goes through `Mobile.LiftItemDupe`, which re-adds the stack remainder to the chest as a **brand-new item instance** (engine-side `AddItem`, bypassing the `CheckHold` block on refilling). Every subsequent pull lifts an instance the `_lifted` set has never seen, so each one re-rolls the 10% spawn chance:

- A level 4 chest holds 4,000 gold → pulled coin by coin, ~400 spawned creatures (plus more from reagent stacks), hands-free, per chest.
- Spawns use `guardian: false`, so nothing tracks or caps them.
- Legit full-stack looting yields roughly 5–8 bonus spawns per chest for comparison.

The code is inherited from RunUO, so descendant shards likely share the hole.

### Fix

Mark every item that enters the chest **after the initial fill** as already lifted, via an `OnItemAdded` override gated by a non-serialized `_filled` flag (set at the end of the constructor and in `[AfterDeserialization]`). Ordering makes this exact: `LiftItemDupe` re-adds the remainder *before* the chest's `OnItemLifted` runs, so the lifted original still gets its one legitimate roll while the remainder is pre-marked.

This also covers packing items *into* the chest (e.g., merging gold back in to lift it out again) and bounce-backs — anything not part of the original loot can never grant a spawn roll.

### Tests

- `PartialLift_MarksSplitRemainderAsLifted` — drives the real `Mobile.Lift` path with a 1-coin pull and asserts the split remainder is marked (failed before the fix).
- `ItemAddedAfterFill_IsMarkedLifted` — post-fill additions are marked (failed before the fix).
- `OriginalFillLoot_IsNotMarkedLifted` — original loot keeps spawn-roll eligibility.

Full `UOContent.Tests` suite: 701 passed.
2026-08-10 09:01:29 -07:00
..
Fillable Containers chore: Use var everywhere (#2294) 2025-12-27 16:47:28 -08:00
BaseTreasureChest.cs feat: Adds SerializedCommandProperty (#1249) 2022-11-14 18:24:33 -08:00
Container.cs feat: Adds Feature Flag System (#2328) 2026-02-07 12:02:57 -08:00
FurnitureContainer.cs fix: Optimizes items to use default weights. (Part 1) (#2240) 2025-07-24 14:44:39 -07:00
LockableContainer.cs chore: Use var everywhere (#2294) 2025-12-27 16:47:28 -08:00
MarkContainer.cs fix: Adds command help, webpage, and fixes issues with other commands (#1669) 2024-02-10 00:19:19 -08:00
ParagonChest.cs fix: Optimizes OPL using string interpolation (#1041) 2022-06-02 10:09:53 -07:00
SalvageBag.cs fix: Fixes container enumeration not recycling pooled arrays (#2341) 2026-02-17 09:54:32 -08:00
Strongbox.cs fix: Items dropped on the ground never decay (#2536) 2026-07-16 18:51:04 -07:00
TrappableContainer.cs feat: Adds SerializedCommandProperty (#1249) 2022-11-14 18:24:33 -08:00
TrappableContainer.ExecuteTrap.cs fix: Fixes crafter deserialize, cleans up bandages, and codegens misc items (#1350) 2023-02-25 00:54:12 -08:00
TreasureChest.cs fix: Updates serialization to use v2.0 (#998) 2022-04-17 08:02:15 -07:00
TreasureMapChest.cs fix: Stop treasure chest guardian spawn farming via stack splits (#2568) 2026-08-10 09:01:29 -07:00