feat(pathfinding): .swb format v8 compact index (#3b) (#2471)
## Summary Phase #3b (final roadmap item), stacked on #2470. Compacts the index trailer from 20 to 8 bytes/chunk. Trammel: 19.2 MB → 17.9 MB. Roadmap total: 565 MB → 17.9 MB (−96.8%). ## Details - Trailer stores `{ u32 packedKey = (ChunkX << 16) | ChunkY, u32 recordLength }` per chunk, in record write order; the file offset is dropped and reconstructed by cumulative recordLength from HeaderSize. - No record reordering, no varint; fixed-stride, TryReadChunk unchanged. - Also simplifies the accumulated `.swb` code comments across the stack. - Format v8; v7 files rejected and re-baked once. ## Tests v8 multi-chunk round-trip (cumulative offset reconstruction) + the v6/v7 suite; full pathfinding suite green; Release build clean.
This commit is contained in:
parent
c265bcbb5e
commit
c7aaf33de9
16 changed files with 960 additions and 868 deletions
|
|
@ -1,155 +1,152 @@
|
|||
using System;
|
||||
using CalcMoves = Server.Movement.Movement;
|
||||
|
||||
namespace Server
|
||||
namespace Server;
|
||||
|
||||
public class PathFollower
|
||||
{
|
||||
public class PathFollower
|
||||
private static bool Enabled;
|
||||
private static readonly TimeSpan RepathDelay = TimeSpan.FromSeconds(2.0);
|
||||
|
||||
private readonly Mobile m_From;
|
||||
private int m_Index;
|
||||
private DateTime m_LastPathTime;
|
||||
private Point3D m_Next, m_LastGoalLoc;
|
||||
private MovementPath m_Path;
|
||||
|
||||
public PathFollower(Mobile from, IPoint3D goal)
|
||||
{
|
||||
private static bool Enabled;
|
||||
private static readonly TimeSpan RepathDelay = TimeSpan.FromSeconds(2.0);
|
||||
m_From = from;
|
||||
Goal = goal;
|
||||
}
|
||||
|
||||
private readonly Mobile m_From;
|
||||
private int m_Index;
|
||||
private DateTime m_LastPathTime;
|
||||
private Point3D m_Next, m_LastGoalLoc;
|
||||
private MovementPath m_Path;
|
||||
public MoveMethod Mover { get; set; }
|
||||
|
||||
public PathFollower(Mobile from, IPoint3D goal)
|
||||
public IPoint3D Goal { get; }
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
Enabled = ServerConfiguration.GetOrUpdateSetting("pathfinding.enable", true);
|
||||
}
|
||||
|
||||
public MoveResult Move(Direction d) =>
|
||||
Mover?.Invoke(d, true) ?? (m_From.Move(d) ? MoveResult.Success : MoveResult.Blocked);
|
||||
|
||||
public Point3D GetGoalLocation() => (Goal as Item)?.GetWorldLocation() ?? new Point3D(Goal);
|
||||
|
||||
public void Advance(ref Point3D p, int index)
|
||||
{
|
||||
if (m_Path?.Success == true)
|
||||
{
|
||||
m_From = from;
|
||||
Goal = goal;
|
||||
}
|
||||
var dirs = m_Path.Directions;
|
||||
|
||||
public MoveMethod Mover { get; set; }
|
||||
|
||||
public IPoint3D Goal { get; }
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
Enabled = ServerConfiguration.GetOrUpdateSetting("pathfinding.enable", true);
|
||||
}
|
||||
|
||||
public MoveResult Move(Direction d) =>
|
||||
Mover?.Invoke(d, true) ?? (m_From.Move(d) ? MoveResult.Success : MoveResult.Blocked);
|
||||
|
||||
public Point3D GetGoalLocation() => (Goal as Item)?.GetWorldLocation() ?? new Point3D(Goal);
|
||||
|
||||
public void Advance(ref Point3D p, int index)
|
||||
{
|
||||
if (m_Path?.Success == true)
|
||||
if (index >= 0 && index < dirs.Length)
|
||||
{
|
||||
var dirs = m_Path.Directions;
|
||||
|
||||
if (index >= 0 && index < dirs.Length)
|
||||
{
|
||||
CalcMoves.Offset(dirs[index], ref p);
|
||||
}
|
||||
CalcMoves.Offset(dirs[index], ref p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ForceRepath()
|
||||
public void ForceRepath()
|
||||
{
|
||||
m_Path = null;
|
||||
}
|
||||
|
||||
public bool CheckPath()
|
||||
{
|
||||
if (!Enabled)
|
||||
{
|
||||
m_Path = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CheckPath()
|
||||
var goal = GetGoalLocation();
|
||||
|
||||
if (m_Path != null && (m_Path.Success && goal == m_LastGoalLoc || m_LastPathTime + RepathDelay > Core.Now) &&
|
||||
!(m_Path.Success && Check(m_From.Location, m_LastGoalLoc, 0)))
|
||||
{
|
||||
if (!Enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var goal = GetGoalLocation();
|
||||
m_LastPathTime = Core.Now;
|
||||
m_LastGoalLoc = goal;
|
||||
|
||||
if (m_Path != null && (m_Path.Success && goal == m_LastGoalLoc || m_LastPathTime + RepathDelay > Core.Now) &&
|
||||
!(m_Path.Success && Check(m_From.Location, m_LastGoalLoc, 0)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_Path = new MovementPath(m_From, goal);
|
||||
|
||||
m_LastPathTime = Core.Now;
|
||||
m_LastGoalLoc = goal;
|
||||
m_Index = 0;
|
||||
m_Next = m_From.Location;
|
||||
|
||||
m_Path = new MovementPath(m_From, goal);
|
||||
Advance(ref m_Next, m_Index);
|
||||
|
||||
m_Index = 0;
|
||||
m_Next = m_From.Location;
|
||||
return true;
|
||||
}
|
||||
|
||||
Advance(ref m_Next, m_Index);
|
||||
public static bool Check(Point3D loc, Point3D goal, int range) =>
|
||||
Utility.InRange(loc, goal, range) && (range > 1 || (loc.Z - goal.Z).Abs() < 16);
|
||||
|
||||
public bool Follow(bool run, int range)
|
||||
{
|
||||
var goal = GetGoalLocation();
|
||||
Direction d;
|
||||
|
||||
if (Check(m_From.Location, goal, range))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool Check(Point3D loc, Point3D goal, int range) =>
|
||||
Utility.InRange(loc, goal, range) && (range > 1 || (loc.Z - goal.Z).Abs() < 16);
|
||||
var repathed = CheckPath();
|
||||
|
||||
public bool Follow(bool run, int range)
|
||||
if (!(Enabled && m_Path.Success))
|
||||
{
|
||||
var goal = GetGoalLocation();
|
||||
Direction d;
|
||||
|
||||
if (Check(m_From.Location, goal, range))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var repathed = CheckPath();
|
||||
|
||||
if (!(Enabled && m_Path.Success))
|
||||
{
|
||||
d = m_From.GetDirectionTo(goal, run);
|
||||
m_From.SetDirection(d);
|
||||
|
||||
return Move(d) is MoveResult.Success or MoveResult.SuccessAutoTurn
|
||||
&& Check(m_From.Location, goal, range);
|
||||
}
|
||||
|
||||
d = m_From.GetDirectionTo(m_Next, run);
|
||||
d = m_From.GetDirectionTo(goal, run);
|
||||
m_From.SetDirection(d);
|
||||
var res = Move(d);
|
||||
|
||||
if (res == MoveResult.Blocked)
|
||||
return Move(d) is MoveResult.Success or MoveResult.SuccessAutoTurn && Check(m_From.Location, goal, range);
|
||||
}
|
||||
|
||||
d = m_From.GetDirectionTo(m_Next, run);
|
||||
m_From.SetDirection(d);
|
||||
var res = Move(d);
|
||||
|
||||
if (res == MoveResult.Blocked)
|
||||
{
|
||||
if (repathed)
|
||||
{
|
||||
if (repathed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Path = null;
|
||||
CheckPath();
|
||||
m_Path = null;
|
||||
CheckPath();
|
||||
|
||||
if (!m_Path!.Success)
|
||||
{
|
||||
d = m_From.GetDirectionTo(goal);
|
||||
m_From.SetDirection(d);
|
||||
|
||||
return Move(d) is MoveResult.Success or MoveResult.SuccessAutoTurn
|
||||
&& Check(m_From.Location, goal, range);
|
||||
}
|
||||
|
||||
d = m_From.GetDirectionTo(m_Next);
|
||||
if (!m_Path!.Success)
|
||||
{
|
||||
d = m_From.GetDirectionTo(goal);
|
||||
m_From.SetDirection(d);
|
||||
|
||||
if (Move(d) == MoveResult.Blocked)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Move(d) is MoveResult.Success or MoveResult.SuccessAutoTurn && Check(m_From.Location, goal, range);
|
||||
}
|
||||
|
||||
if (m_From.X == m_Next.X && m_From.Y == m_Next.Y)
|
||||
d = m_From.GetDirectionTo(m_Next);
|
||||
m_From.SetDirection(d);
|
||||
|
||||
if (Move(d) == MoveResult.Blocked)
|
||||
{
|
||||
if (m_From.Z == m_Next.Z)
|
||||
{
|
||||
++m_Index;
|
||||
Advance(ref m_Next, m_Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Path = null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return Check(m_From.Location, goal, range);
|
||||
}
|
||||
|
||||
if (m_From.X == m_Next.X && m_From.Y == m_Next.Y)
|
||||
{
|
||||
if (m_From.Z == m_Next.Z)
|
||||
{
|
||||
++m_Index;
|
||||
Advance(ref m_Next, m_Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Path = null;
|
||||
}
|
||||
}
|
||||
|
||||
return Check(m_From.Location, goal, range);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue