fix: Fixes pathfinding prebake and pathfinding multi-fallthrough. (#2478)
## Summary Fixes the pathfinding step-cache (`.swb`) prebake so it bakes **once** and skips when a valid cache already exists, instead of re-baking on every boot. The root cause was the staleness fingerprint hashing mutable in-memory tile data rather than the on-disk files. This PR makes the fingerprint a pure function of the client data files and separates dynamic multis (houses/boats) from the static cache. > This branch builds on the `ConfigurePrompts` first-boot-prompt unification (commit `5df8d0bd`, also included here) — that commit accounts for the `ServerConfiguration.cs` and `dev-docs/server-lifecycle.md` changes in the diff. ## The bug With `pathfinding.prebakeMaps` set, the cache re-baked on **every** boot. The `.swb` staleness fingerprint hashed the live `TileData.LandTable`/`ItemTable` flags, which the server patches at runtime (`ItemFixes`, `LOSBlocker`, `PotionKeg`, `CTF`) at nondeterministic lifecycle points (Initialize-phase methods share a priority; static ctors fire lazily). So a fingerprint stamped at runtime (`[PathBake`) never matched the one recomputed during startup `Initialize()`, and the cache rebaked every time. ## Changes **1. Fingerprint the files, not the in-memory tables** (`fix`) Hash `tiledata.mul` (cached, computed once) plus the per-map `.mul`/`.uop` files — never the runtime-mutated `TileData` tables. The fingerprint is now lifecycle-stable. Existing `.swb` files rebake once after deploy, then stay stable. **2. Compute the fingerprint once per boot** (`refactor`) `Configure()`'s `AutoLoadAtStartup()` already opens and fingerprint-validates a reader for every up-to-date `.swb`. `Initialize()` now skips baking any map that already has an open reader (`StepCache.HasLazyReader`) instead of recomputing the fingerprint a second time. **3. Bake static-only; route multis to the live path** (`refactor`) Multis (houses/boats) are dynamic, so they're no longer baked into the static chunk cache — they were tagged with `BuiltMultisVersion`, a non-persisted session counter, which made persisting them unsafe (false matches / wasted re-bakes). - Chunks bake land + `statics.mul` only. - At query time, any cell whose sector (or its 1-cell halo) contains a multi routes to `Fallthrough_Multi` → the existing live, multi-aware `CheckMovement` path. The halo prevents a cell proposing a walkable edge into a neighbouring wall; interior (multi-free) cells pay one sector lookup. - Adds `Sector.HasMultis` (one engine accessor); `.swb` format → v9 (rejects old multi-baked files); new `Fallthrough_Multi` telemetry. - Behaviour-preserving: multis use the same live path the engine used before the cache existed. **4. Comment polish** (`style`) — no behaviour change. ## Testing All green: **92** pathfinding (incl. a new fingerprint-stability test and a multi-halo fallthrough test), **423** UOContent, **708** Server. ## Follow-ups (not in this PR) - **Background bake worker** — make `[PathBake` and the boot prebake non-blocking (game thread serves tile reads to an off-thread worker). - **Per-multi MCL cache** — cache walkability in each multi's own frame (keyed by multiID, movement-invariant) so houses/boats get a fast path instead of the live fallback. - **House-pathfinding equivalence tests** — the one area not yet covered by a dedicated automated test; multi pathing is currently correct by delegation to the live path.
This commit is contained in:
parent
eec37edd67
commit
9d26a44a28
11 changed files with 281 additions and 107 deletions
|
|
@ -5,8 +5,11 @@ namespace Server.Engines.Pathing.Cache;
|
|||
|
||||
/// <summary>
|
||||
/// Computes static-only walkability for a single cell — the per-cell, per-direction
|
||||
/// "can step" mask and destination Z, based purely on land + statics + multis. Mirrors
|
||||
/// <see cref="MovementImpl"/>.Check minus the item and mobile collision phases.
|
||||
/// "can step" mask and destination Z, based purely on land + statics.mul tiles (NOT
|
||||
/// multis). Mirrors <see cref="MovementImpl"/>.Check minus the item and mobile collision
|
||||
/// phases. Multis (houses, boats) are intentionally excluded: they're dynamic content, so
|
||||
/// cells they cover route to the live movement path via <see cref="StepCache"/>'s
|
||||
/// multi-halo fallthrough rather than being baked into the static chunk cache.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Bakes two rule sets per cell: walker (canSwim=false, cantWalk=false) and swim-only
|
||||
|
|
@ -56,7 +59,7 @@ public static class StepProbe
|
|||
zs[count++] = landCenter;
|
||||
}
|
||||
|
||||
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y))
|
||||
foreach (var tile in map.Tiles.GetStaticTiles(x, y))
|
||||
{
|
||||
if (count >= zs.Length)
|
||||
{
|
||||
|
|
@ -141,7 +144,7 @@ public static class StepProbe
|
|||
cand[count++] = landCenter;
|
||||
}
|
||||
|
||||
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y))
|
||||
foreach (var tile in map.Tiles.GetStaticTiles(x, y))
|
||||
{
|
||||
if (count >= cand.Length)
|
||||
{
|
||||
|
|
@ -270,7 +273,7 @@ public static class StepProbe
|
|||
}
|
||||
|
||||
// Otherwise scan statics for a wet surface.
|
||||
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y))
|
||||
foreach (var tile in map.Tiles.GetStaticTiles(x, y))
|
||||
{
|
||||
var data = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
|
||||
if (data.Wet)
|
||||
|
|
@ -312,7 +315,7 @@ public static class StepProbe
|
|||
isSet = true;
|
||||
}
|
||||
|
||||
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y))
|
||||
foreach (var tile in map.Tiles.GetStaticTiles(x, y))
|
||||
{
|
||||
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
|
||||
var calcTop = tile.Z + id.CalcHeight;
|
||||
|
|
@ -377,7 +380,7 @@ public static class StepProbe
|
|||
|
||||
int testTop;
|
||||
|
||||
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y))
|
||||
foreach (var tile in map.Tiles.GetStaticTiles(x, y))
|
||||
{
|
||||
var itemData = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
|
||||
var notWater = !itemData.Wet;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue