105 lines
6.6 KiB
C#
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}");
|
|
}
|
|
}
|
|
}
|