ModernUO/Projects/Server/Mobiles
Kamron Batman 9b35b39d0d
fix: stop stack merges and splits from laundering PlayerConstructed (#2576)
## Why

`PlayerConstructed` is per-instance provenance, and #2574 put it on every crafted item — including potions, arrows and other stackables. Stack operations were written when no item carried provenance of any kind, so they treated two piles of the same graphic as interchangeable.

**Merging** keeps the receiving stack's value. Dropping bought potions onto a crafted stack made the whole pile count as crafted; the reverse order erased it. Which one happened was decided by drag direction alone.

**Splitting** rebuilds one half in `Mobile.LiftItemDupe`, which copies a fixed list of fields rather than going through `Dupe`/`CopyProperties`. `PlayerConstructed` was not on that list, so dragging part of a pile off stripped the new half. Worth calling out: `[IgnoreDupe]` does **not** govern this path — it only applies to `Dupe()`. Reasoning "the field isn't `[IgnoreDupe]`, so it copies" is wrong here.

## Changes

- `Item.CanStackWith` compares `PlayerConstructed`, so crafted and non-crafted never merge into one indistinguishable pile.
- `Mobile.LiftItemDupe` copies `PlayerConstructed` onto the remainder, so a split cannot produce halves that disagree about what they are.

Refusing to merge is the whole fix. A stack has nowhere to record provenance, so the only coherent behaviour is to keep the two piles apart rather than pick a winner.

## What this deliberately does not do

Paths that genuinely **virtualize** an item — pouring from a `PotionKeg`, for one — rebuild it without the flag, and the result is simply treated as not crafted. That is accepted rather than worked around; the alternative is threading provenance through every count-based container, which buys little. The keg stores a `Held` int rather than a stack, so nothing there depends on merging and nothing breaks.

`CommodityDeed` is unaffected — it holds the real `Commodity` item rather than a count, so the flag rides along.

## Player-visible effect

Crafted potions and arrows will no longer stack with bought or looted ones. That is the intended invariant, and it is the reason the flag can be trusted at all.

## Tests

7 new tests in `Server.Tests`: both merge directions, the matching-provenance case, split copying, and the split/re-merge round trip.

`Server.Tests` **822 passing**, `UOContent.Tests` **701 passing**, build clean with 0 warnings.
2026-08-13 19:18:06 -07:00
..
Mods fix: Bumps deps. Updates copyrights (#2353) 2026-03-05 19:36:54 -08:00
AggressorInfo.cs fix: Cleans up core code (#1187) 2022-10-10 21:47:08 -07:00
BaseVendor.cs fix: Fixes client crash while selling items to NPC (#1296) 2022-12-05 20:16:31 -08:00
Body.cs fix: Bumps deps. Updates copyrights (#2353) 2026-03-05 19:36:54 -08:00
IHasSteps.cs feat: Adds Stamina System to overhaul overweight. (#1465) 2023-09-16 17:52:54 -07:00
IMount.cs fix: Bumps deps. Updates copyrights (#2353) 2026-03-05 19:36:54 -08:00
IVendor.cs fix: Bumps deps. Updates copyrights (#2353) 2026-03-05 19:36:54 -08:00
Mobile.cs fix: stop stack merges and splits from laundering PlayerConstructed (#2576) 2026-08-13 19:18:06 -07:00
Mobile.Enumerators.cs feat: Adds proper OSI tracking up to 120 tiles. (#2281) 2025-11-29 09:55:52 -08:00
Mobile.Messages.cs perf: Zero-alloc interpolation for SendMessage/Overhead APIs (#2434) 2026-05-03 18:04:02 -07:00
Mobile.Migrations.cs fix: Overhauls virtue system (#1376) 2023-07-19 21:43:47 -07:00
Movement.cs fix: Bumps deps. Updates copyrights (#2353) 2026-03-05 19:36:54 -08:00
Notoriety.cs fix: Cleans up core code (#1187) 2022-10-10 21:47:08 -07:00
SimplePrompt.cs fix: Bumps deps. Updates copyrights (#2353) 2026-03-05 19:36:54 -08:00
SimpleTarget.cs fix: Bumps deps. Updates copyrights (#2353) 2026-03-05 19:36:54 -08:00