using System.IO;
using Server.Engines.Pathing.Cache;
using Server.Tests.Terrain;
using Xunit;
namespace Server.Tests.Pathfinding;
[Collection("Sequential Pathfinding Tests")]
public class StepCacheFingerprintTests
{
///
/// Regression: the cache fingerprint must hash the on-disk tiledata.mul, NOT the mutable
/// in-memory tables. The server patches item flags/heights at runtime
/// (ItemFixes, LOSBlocker, PotionKeg, CTF, ...) at nondeterministic lifecycle points, so a
/// fingerprint taken over the live tables depended on WHEN it was computed: a runtime
/// [PathBake stamped one value into the .swb and the next startup's Initialize() recomputed a
/// different one, marking the bake stale and re-baking on every boot. Hashing the file makes
/// the fingerprint a pure function of the client's tile data, immune to those mutations.
///
[Fact]
public void Fingerprint_IgnoresRuntimeTileDataMutation()
{
const int mapId = 1; // Trammel — loaded by the test bootstrap.
var before = StepCacheFile.ComputeFingerprint(mapId);
const int probeId = TestTiles.Wall;
var original = TileData.ItemTable[probeId].Flags;
try
{
// Mutate an in-memory item flag the way ItemFixes/CTF/etc. do at runtime. XOR
// guarantees the value actually changes regardless of the current flag state.
TileData.ItemTable[probeId].Flags ^= TileFlag.NoShoot;
Assert.NotEqual(original, TileData.ItemTable[probeId].Flags); // sanity: mutation took
var after = StepCacheFile.ComputeFingerprint(mapId);
Assert.Equal(before, after);
}
finally
{
TileData.ItemTable[probeId].Flags = original;
}
}
///
/// The other half of the contract: the fingerprint does change when the file does, so a client patch
/// that shifts one tile flag invalidates a bake made against the old file.
///
[Fact]
public void TileDataFileHash_ChangesWhenOneFlagChanges()
{
var directory = Directory.CreateTempSubdirectory("modernuo-fingerprint-").FullName;
try
{
var original = SyntheticTileData.WriteTo(Directory.CreateDirectory(Path.Combine(directory, "a")).FullName);
var rewritten = SyntheticTileData.WriteTo(Directory.CreateDirectory(Path.Combine(directory, "b")).FullName);
var patched = SyntheticTileData.WriteTo(
Directory.CreateDirectory(Path.Combine(directory, "c")).FullName,
flags => flags[TestTiles.Wall] ^= TileFlag.NoShoot
);
Assert.Equal(StepCacheFile.HashTileDataFile(original), StepCacheFile.HashTileDataFile(rewritten));
Assert.NotEqual(StepCacheFile.HashTileDataFile(original), StepCacheFile.HashTileDataFile(patched));
}
finally
{
Directory.Delete(directory, true);
}
}
}