Files
Ananta-ps/Ananta.Server/Ananta.Handlers/Game/GameRouter.PoliceAnimationTrace.cs
T

105 lines
6.6 KiB
C#

using Ananta.SDK.Network;
using Ananta.SDK.Serialization;
using Ananta.Server.Protocol.Client4229938;
namespace Ananta.Server.Handlers.Game;
internal sealed partial class GameRouter
{
// Diagnostic only. Do not change seat attachments, transforms, authority or
// animation state here. Short, bounded samples identify visual/root mismatch.
private static async Task TracePoliceVehicleAnimation4229938(TcpSession session,
uint child, ulong agent, ulong vehicle, int seat, bool boarding)
{
try
{
if (!session.Items.TryGetValue(PoliceGateSessionKey4229938, out var value)
|| value is not TcpSession gate)
{
session.Log.Warn("[LIXI-ANIM] diagnostic skipped: gate session unavailable");
return;
}
static string U64(ulong id) => FormattableString.Invariant($"ulong.new({(uint)id}, {id >> 32})");
for (int sample = 0; sample < 7; sample++)
{
if (sample > 0) await Task.Delay(1000, session.ClosedToken);
if (gate.ClosedToken.IsCancellationRequested
|| !PoliceRuntimes.TryGetValue(session, out var runtime)) return;
await runtime.Gate.WaitAsync(session.ClosedToken);
bool active;
try { active = runtime.Child == child && runtime.Agent == agent; }
finally { runtime.Gate.Release(); }
if (!active) return;
var owner = GetStateIfExists(session)?.ActiveSpiritUnitId ?? 0;
var script = $$"""
local ok, err = pcall(function()
local prefix = '[LIXI-ANIM] {{(boarding ? "board" : "alight")}} sample={{sample}} agent={{agent}} vehicle={{vehicle}} seat={{seat}} '
local npc = gCS.SceneDataMgr.GetUnit({{U64(agent)}})
local car = LX6.Drive.DriveManager.Instance:GetBaseVehicle({{U64(vehicle)}})
-- Normal print can be filtered out of Player.log. The
-- exported logger used by existing Lua diagnostics is
-- intentionally used here; this is not a gameplay error.
if not npc or not car then print_error(prefix .. 'unit/vehicle unavailable'); return end
print_error(prefix .. 'npcWorld=' .. tostring(npc.Position)
.. ' npcLocal=' .. tostring(npc.LocalPosition)
.. ' npcYaw=' .. tostring(npc.WorldEulerY)
.. ' vehicleWorld=' .. tostring(car.Position)
.. ' vehicleYaw=' .. tostring(car.EulerY))
if {{(boarding ? "true" : "false")}} and ReflectionGetProperty and ReflectionCallMethod then
local traced, detail = pcall(function()
local player = gCS.SceneDataMgr.GetUnit({{U64(owner)}})
if not player or not player.MeshRoot then return end
local m = ReflectionGetProperty('LX6.Drive.BaseVehicle','interactModule',car)
if not m then return end
local idx = ReflectionCallMethod('LX6.Drive.VehicleInteractModule','GetInteractPointIndexByTag',m,41+{{seat}})
if not idx or idx < 0 then return end
local target = ReflectionCallMethod('LX6.Drive.VehicleInteractModule','GetInteractWorldPos',m,idx)
local angles = ReflectionCallMethod('LX6.Drive.VehicleInteractModule','GetInteractEularOffset',m,idx)
local p = player.Position
local mesh = player.MeshRoot
print_error(prefix .. 'officer-v3 player=' .. tostring(p)
.. ' rootYaw=' .. tostring(player.WorldEulerY)
.. ' meshWorld=' .. tostring(mesh.position)
.. ' meshYaw=' .. tostring(mesh.eulerAngles.y)
.. ' meshLocal=' .. tostring(mesh.localPosition)
.. ' target=' .. tostring(target)
.. ' targetYaw=' .. tostring(car.EulerY+angles.y)
.. ' deltaXYZ=' .. tostring(target.x-p.X) .. ',' .. tostring(target.y-p.Y) .. ',' .. tostring(target.z-p.Z))
end)
if not traced then print_error(prefix .. 'officer-v3 unavailable: ' .. tostring(detail)) end
end
if {{sample}} == 0 then
local count = 0
local function walk(t, path, depth)
if depth > 6 or count >= 256 then return end
count = count + 1
local name = string.lower(t.name)
if string.find(name, 'seat', 1, true) or string.find(name, 'escort', 1, true)
or string.find(name, 'door', 1, true) or string.find(name, 'gangster', 1, true) then
print_error(prefix .. 'anchor=' .. path .. ' world=' .. tostring(t.position)
.. ' local=' .. tostring(t.localPosition) .. ' angles=' .. tostring(t.eulerAngles))
end
for i = 0, t.childCount - 1 do
local c = t:GetChild(i)
walk(c, path .. '/' .. c.name, depth + 1)
end
end
walk(car.gameObject.transform, car.gameObject.name, 0)
end
end)
if not ok then print_error('[LIXI-ANIM] diagnostic unavailable: ' .. tostring(err)) end
""";
await gate.NotifyAsync(MethodId.SyncDoToLuaMaster,
new UxWriter().UxString(script).ToArray(), session.ClosedToken);
if (sample == 0)
session.Log.Info($"[LIXI-ANIM] trace-v2 sent agent={agent} vehicle={vehicle} seat={seat} boarding={boarding}; read Player.log for samples (diagnostic severity only)");
}
}
catch (OperationCanceledException) { }
catch (Exception ex)
{
session.Log.Warn($"[LIXI-ANIM] diagnostic stopped: {ex.GetType().Name}");
}
}
}