Fixes#2682. Thanks @DatMoshu for the detailed report and repro.
While fixing it I audited the other map, item and multi enumerators for the same class of defect. None repeats #2682 itself, but four others turned up. All are fixed here.
## Fixes
- **`StaticTileEnumerator` (#2682)**: `SetMulti` returned `SetTile()` for the first multi whose bounds contained the point. If that multi had no tile at the cell, the enumeration ended and every later multi in the sector was skipped. Movement and line of sight lost a floor that stood inside another multi's bounding box. It now moves on to the next multi.
- **`MultiBoundsEnumerator`**: deduplication used one static `HashSet` that every new query cleared. A query run inside another made the outer query skip multis or return them twice. Each multi is now yielded only from the sector that holds its `Location`; the bounds filter guarantees that sector is visited. A per-enumerator set covers a multi that isn't registered in that sector (components changed after placement). Normal queries allocate nothing.
- **`Item`/`Mobile`/`ClientDistanceEnumerator`**: ring sectors were bounded only by `sx >= _sectorStartX`. Ring cells past the map edge resolved to the shared invalid sector, once per cell. Entities at off-map coordinates were returned more than once, and sectors outside the query were created lazily. The check now covers all four sides. Results match the bounds enumerators, which never returned off-map entities.
- **`BaseBoat.MovingEntitiesEnumerator`**: `GetMovingEntities` handles a null map, but the enumerator constructor still called `boat.Map.GetMobilesInBounds` and threw. A boat with no map now gets empty enumerators.
- **`BaseDoor.ChainEnumerator`**: the walk stopped only on returning to the start door. Links are set in pairs, so `[ChainLink A B C` followed by `[Link B C` leaves A→B→C→B, and enumerating that chain never ended. The walk now stops on any door it has already yielded. Only reachable when `UseChainedFunctionality` is true, which is off in base and in all shipped content.
## Testing
- A regression test for each fix except the boat null guard. Each failed before its fix and passes after.
- Server.Tests 910/910. UOContent.Tests 1185 passed, 2 skipped.
- Test note: the `MultiComponentList(List<MultiTileEntry>)` constructor drops entries after the first whose `Flags` are 0. The new test multis use non-zero flags.
## Summary
Folds **113** hand-written `[SerializableProperty]` members into plain `[SerializableField]` declarations using the v4 setter hooks — value coercion/vetoes via `allowFieldChange`, post-change side effects via `fieldChanged` (whose `oldValue` parameter covers the old-house/old-sender unsubscribe patterns). Net **-450 lines** of setter boilerplate.
```cs
// before
[SerializableProperty(1)]
[CommandProperty(AccessLevel.GameMaster)]
public int Charges
{
get => _charges;
set
{
_charges = Math.Clamp(value, 0, MaxCharges);
InvalidateProperties();
this.MarkDirty();
}
}
// after
[SerializableField(1, allowFieldChange: nameof(AllowChargesChange))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
[InvalidateProperties]
private int _charges;
private bool AllowChargesChange(ref int value)
{
value = Math.Clamp(value, 0, MaxCharges);
return true;
}
```
## How sites were selected
A classifier parsed all 204 `[SerializableProperty]` sites and converted only those matching strict shapes: getter is exactly `get => _field;`, the assignment comes first (after at most an equality guard), and relocated side effects contain no `return`, no `value` mutation, and no field re-assignment. Everything else was left alone deliberately:
- **~34 custom getters** (fallback defaults like `_x == -1 ? Default : _x`, self-healing refs) — no setter hook can express these.
- **~35 pre-assignment logic** (durability Unscale/Scale sandwiches, old-state captures like PotionKeg's pile weight).
- **virtual/override members, name-mismatched backing fields (`m_`), exotic semantics** (guards' `Focus` does work on *equal* assignment; `ChampionSpawn.Active` never assigns its field).
Five sites the classifier refused were converted by hand where the hooks fit cleanly: `ReceiverCrystal.Sender`, `PlayerVendor.House`, `PlayerBarkeeper.House` (old-value unsubscribe via `oldValue`), `BaseSuit.AccessLevel` (its existing virtual `OnAccessLevelChanged` already had the exact callback shape), and `DyeTub.DyedHue` (a true veto: `AllowDyedHueChange(ref int value) => _redyable`).
## Verification
- Build: **0 errors, 0 warnings**.
- **Schema regeneration produces zero Migrations changes** — the conversion is wire- and schema-neutral by construction (same orders, types, and property names), and CI's schema diff check enforces it.
- **835 + 708 tests green.**
## Behavioral notes (all strict improvements, called out for review)
- Generated setters skip everything when the incoming value equals the current one; a few converted setters previously re-ran side effects on equal assignment (redundant `Update()`-style refreshes).
- Generated setters always `MarkDirty()` on change; several converted setters never did (e.g. `DyeTub.DyedHue`, `MorphItem` ranges) — their changes only persisted if something else dirtied the entity. Those latent persistence bugs are fixed by construction.
### Summary
Adds the command [TrackLeaks to enable tracking item/mobiles that have been deleted but still have dangling references. Requires adding the _TRACK_LEAKS_ define constant during build.
### Summary
- Fixes `[AdvancedSearch` being accessible by players 😱
- Adds `[GenCommands` to generate the same commands html page on https://muo.gg/commands.
- Fixes `[helpinfo` so all commands properly show up!
> [!WARNING]
> ### Developer Warning:
> Commands must now be registered in the `Configure` bootup phase.
> If a command is not registered early enough, it may not be available to systems like [helpinfo
> that cache their information.
> [!NOTE]
> ### Developer Note:
> Various commands related to generating content have been changed to _Developer_ and above access level.
### Screenshots
<img width="673" alt="image" src="https://github.com/modernuo/ModernUO/assets/3953314/b105b5c9-5eb4-4ace-93ff-1bfb31e7132f">
<img width="547" alt="image" src="https://github.com/modernuo/ModernUO/assets/3953314/e97487e8-47a5-4aa7-89cc-9fe3deda584d">
## BREAKING CHANGE
- Deletes `map.GetObjectsInRange` and `map.GetObejctsInBounds`
### Notes
Developers are expected to enumerate mobiles and items separately now using `map.GetMobilesInRange` and `map.GetItemsInRange`. This helps keep the code streamlined so we don't have to maintain multiple copies of ref struct enumerators that do the same thing.
### Fixes
- [X] Fixes bug with planks closing
- [X] Fixes issue with iterating items/mobiles from a null map
### Summary
Eliminates `IPooledEnumerable<T>` and `eable.Free()` from `Map` for mobiles. This drastically simplifies code that iterates in range, for example:
```cs
foreach (var m in m.GetMobilesInRange(5))
{
}
```
The code above no longer requires an eable and calling `Free()`.
- [X] Fixed several locations where an NPC that was damaged would cause a server crash.
- [X] Removed an unnecessary allocation in guard fake calls (NPCs calling guards on you)
- [X] Fixes damage precision loss in Poison Strike Spell
- [X] BogThing no longer attempts to "search" for boglings to eat when it is at full health
### Summary
Eliminates `IPooledEnumerable<T>` and `eable.Free()` from `Map` for items. This drastically simplifies code that iterates in range, for example:
```cs
foreach (var item in m.GetItemsInRange(5))
{
}
```
The code above no longer requires an eable and calling `Free()`.
- [X] Fixes an issue where a buffer smaller than 8 bytes would not double with enough space in some cases.
- [X] Fixes an issue with dupe copying the savebuffer reference (ugh).
- [X] Streamlines the IGenericWriter API to use better generics.
- [X] Streamlines the IGenericReader API to use better generics.
- [X] Forces `tidying` of a List/HashSet to be done externally since Writers/Readers should not have side effects.
- [X] Fixes an issue where Tidying a list didn't TrimExcess, causing memory leaks.
- [X] Reverted the meaning of `World.Running` to specifically refer to any world state post world loading.
- NOTE: Do not use this if you want to block on world saves. Instead use checks against `WorldState.Saving` states.
- [X] Fixes an issue with serializing negative DateTime deltas.
- [X] Fixes a potential issue with serializing non-UTC DateTime.
Bumps release version
- [X] Fixes several bugs
- [X] Updates more ordinal issues
- [X] Cleans up the code a bit
- [X] Turns classes static that should have been
- [X] Changes TcpServer.Instances to a HashSet
Bumps release version
- [X] Changes effect packets
- [X] Updates playing sounds
- [X] Updates Effects class to have more options so there are less direct calls to building the packets
- [X] Changes bonding status packet