**Follow-on to #2639 (merged). References a local IORingGroup `1.0.13-preview.11` pack until 1.0.13 (modernuo/IORingGroup#15) is published; do not merge before that switch.**
## Summary
Consumes IORingGroup's lean base pools (modernuo/IORingGroup#15): both network pools now start with one slab, grow a slab at a time with the population, and trim idle slabs back after quiet periods.
- Fixes the transport's send-pool cap: previously only 1024 of the 4096 connections could get a send buffer; connection 1025 was closed at accept.
- Network memory at boot drops from about 96 MB to about 10 MB at the defaults; a full 4096 logged-in connections is about 1.25 GB of base buffers plus the growth budget.
- New settings: `network.initialBufferSlabs` (default 1; slabs of each pool held from boot and the trim floor) and `network.maxBufferSlabs` (default 128; divides the connection maximum into slabs, 32 connections per slab). Both are coerced with a warning; the same value feeds the ring table and the manager so they cannot drift.
- The Debug-only maintenance line includes base-pool capacity and releases.
- `dev-docs/server-requirements.md` rewrites the network memory story and adds the two settings.
## Pre-auth buffers
Every connection starts on the transport's platform-minimum buffers (4 KB receive, 4 KB send) instead of the base pools. It is promoted to full-size buffers (64 KB receive, `network.sendBufferSize` send) when the game server verifies its account — the point where `NetState.Account` is assigned in the `GameServer_AwaitingGameServerLogin` or `GameServer_LoggedIn` state, so a verdict that lands after the parser has moved on still promotes. Nothing ever moves back. The login-server pass stays on the small buffers for its whole lifetime.
Before credentials verify, nothing promotes: the 4 KB send ring is the entire pre-auth send budget, and a connection that overruns it is dropped as exhausted, exactly as the receive side drops a packet header declaring more bytes than the receive buffer can hold (new guard in `HandlePacket`; it also closes the old 65535-byte edge on 64 KB buffers). The stock login sequence sends under 2 KB. After credentials verify, the send path promotes on demand if it ever needs to (unbudgeted, outside the memory ceiling and the shrink bookkeeping — promotion is not growth), and the oversize-packet guard waits on a pending receive promotion or retries a stalled one once for a verified account before disconnecting. A completion that fills the receive buffer arms no receive, so `HandleReceive` now calls `RingSocket.ResumeReceive()` after the parse loop — at 4 KB a burst of small packets fills the buffer in one completion.
Net effect: a flood of unauthenticated connections tops out at about 32 MB across the full 4096-connection cap where the platform minimum is 4 KB (the transport's retained slabs and the base pools used by logged-in players are separate), and never allocates a base-pool slab.
Platform note: on Windows Server 2012 R2 / 2016 the transport's legacy mapping path floors at 64 KB: the pre-auth receive pool is off there (its base is 64 KB), while the pre-auth send buffer starts at 64 KB under the 256 KB base. The server logs the effective sizes at startup.
## Testing
Server.Tests (905) and UOContent.Tests green on the preview pack (one pre-existing `FamiliarAITests` failure from #2644 reproduces on `main`, tracked separately). New tests cover both coercions, that the ring's registration table equals `RequiredRegisteredBuffers` for the configured values, promotion on game-server auth and on a late account, no promotion on the login server, pre-credential overrun ending in exhaustion, post-credential on-demand promotion, the oversize-packet guard through loopback (error, wait, retry with an account, and a promotion made pending mid-parse), and receiving again after a burst fills the initial buffer.
When an AI debug-message cooldown crosses the signed tick-counter boundary, DebugInterpolatedStringHandler can emit before the deadline or suppress a message after it. Both constructors compare absolute tick values.
Use subtraction-based deadline comparisons in both constructors, following dev-docs/tick-counts.md. This is a two-line production change; message formatting, cooldown duration, and gameplay behavior are unchanged.
Adds 35 regression cases exercising the actual handler and DebugSayFormatted:
- Both constructor overloads, with debugging enabled and disabled.
- Before/at/after deadlines on positive and negative clocks.
- Future and expired deadlines across signed wraparound.
- Cooldown rearming across wraparound and buffer clearing.
- A compiler-generated interpolated call through the public extension method.
Validation on Linux / .NET 10, based on clean upstream 35e3a31b4:
- Release build: 0 warnings, 0 errors.
- New tests against unchanged production code: 28 passed, 7 failed.
- With the fix: 35 passed, including without client map data (no skips).
- Selected AI/pet suites: 134 passed / 7 failed before; 141 passed / 0 failed after.
Tests use synthetic entities; no running shard or world saves. The faulty comparisons were reverified on upstream main before submission. Windows execution remains unverified. The tests exercise explicitly assigned deadlines; deadline initialization is outside this patch.
Addresses only the DebugInterpolatedStringHandler comparison item in #2627; it does not close the other audit items.
Two bugs found while reworking #2653.
**Custom flags throw.** `SyncStaticFlag` is a switch expression with no discard arm, so any key that has no static behind it throws `SwitchExpressionException`. `[FeatureFlag mykey create <category> <desc>` crashes in `CreateOrUpdateFlag`, and once such a key is in `flags.json`, every boot's `SyncAllStaticFlags()` throws mid-iteration — caught and logged as "Failed to load feature flags", but stock flags later in dictionary order never sync. Added `_ => enabled`.
**Removing a flag turns some features on.** `RemoveFlag` hardcoded `SyncStaticFlag(flagKey, true)`. For `speedhack_detection` and `insurance` the real default is `false` / `insurance.enable`, so deleting the flag enabled the feature. It now syncs the removed flag's `DefaultEnabled`, which #2653 seeds from the static.
Verified: `dotnet build Projects/UOContent/UOContent.csproj -c Release`, 0 `CS` diagnostics (post-build copy to `Distribution/` was blocked by a running server instance).
## Summary
Profiling with thousands of creatures in range of each other showed `BaseAI.IsInvalidFactionTarget` checking Ethereal Voyage and active Honor on every acquisition candidate and then calling `BaseCreature.IsEnemy`, which checked both again. This makes `IsEnemy` the single authority and removes the duplicate per-pair work.
## Changes
- **`BaseCreature.IsEnemy`**: the Ethereal Voyage check sat below the `m is not BaseCreature` early return, so it never reached players — the only mobiles that cast it. It is hoisted next to the Honor veto. `GetMaster()` was called three times per creature pair (inside `Ethics.Player.Find(m, true)` and twice at the bottom); it is now computed once.
- **`BaseAI.IsInvalidFactionTarget`**: reduced to `IsFriend` / `IsEnemy` / `CanBeHarmful`. The removed `Combatant != m` Honor exception was dead code — `IsEnemy` vetoed Honor unconditionally on the following line.
- **`Ethics.Player.Find`** / **`TransformationSpellHelper.GetContext`**: small simplifications on the same path.
## Behavior
- `ShouldAcquireOnApproach` and `OnAggressiveAction` only consult `IsEnemy`, so movement-triggered acquisition now respects Ethereal Voyage for players (previously a player under Ethereal Voyage walking past a monster was still acquired on approach).
- `BaseFactionGuard.IsEnemy` does not call base, so faction guards no longer skip honoring/voyaging enemy-faction players. Accepted: both mechanics describe monsters, not guards.
- `HealerAI`/`BerserkAI`/`PredatorAI` (`bFacFriend`) callers are unaffected — `IsFriend` already required a `BaseCreature`, so players were excluded before these checks ran. `MilitiaFighter`/`MilitiaCanoneer` return `false` for all players, so they are unaffected too.
## Testing
- `dotnet build` clean.
- Pure predicate reorder; no new tests.