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

97 lines
5.9 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
{
// Read-only boarding target audit. Reapplying the same native target had no
// effect in the user's recording; do not keep resetting EntrySystem.Setup.
private static async Task PreparePoliceOfficerBoarding4229938(
TcpSession session, PoliceRuntime4229938 runtime, ulong owner)
{
if (owner == 0 || runtime.PendingBoarding is not { } passenger
|| passenger.Seat is not (2 or 3)
|| !runtime.Npcs.TryGetValue(passenger.Agent, out var npc) || npc.Template != 4065053)
return;
var vehicles = GetVehicleRuntimeState(session);
lock (vehicles.SyncRoot)
if (!vehicles.Vehicles.TryGetValue(passenger.Vehicle, out var car) || car.ConfigId != 81004027)
return;
if (!session.Items.TryGetValue(PoliceGateSessionKey4229938, out var gateValue)
|| gateValue is not TcpSession gate || gate.ClosedToken.IsCancellationRequested) return;
var script = $$"""
local ok, err = pcall(function()
local prefix = '[LIXI-BOARD] officer-audit-v3 '
-- These are registered by ReflectionUtils.RegisterToLua in the
-- client (0x148BB780). C# public does NOT imply direct Lua export.
local get = ReflectionGetProperty
local call = ReflectionCallMethod
if type(get) ~= 'function' or type(call) ~= 'function' then
print_error(prefix .. 'skipped: reflection bridge unavailable'); return
end
local vehicleType = 'LX6.Drive.BaseVehicle'
local moduleType = 'LX6.Drive.VehicleInteractModule'
local actionType = 'LX6.Drive.Interact.VehicleInteractAction'
local officerType = 'LX6.Drive.Interact.PlayerEscortToVehicleAction'
local owner = ulong.new({{(uint)owner}}, {{owner >> 32}})
local player = gCS.SceneDataMgr.GetUnit(owner)
local vehicle = LX6.Drive.DriveManager.Instance:GetBaseVehicle(ulong.new({{(uint)passenger.Vehicle}}, {{passenger.Vehicle >> 32}}))
if not player or not vehicle then print_error(prefix .. 'skipped: unit unavailable'); return end
local module = get(vehicleType,'interactModule',vehicle)
if not module then print_error(prefix .. 'skipped: no interact module'); return end
local action = call(moduleType,'GetUnitCurrentAction',module,owner)
if not action then print_error(prefix .. 'skipped: no officer vehicle action'); return end
-- VehicleInteractType.PoliceEscortToVehicle=10. A late gate
-- message must never repoint driving, alighting or other actions.
local kind = get(actionType,'interactType',action)
if kind ~= 10 and tostring(kind) ~= 'PoliceEscortToVehicle' then
print_error(prefix .. 'skipped: action=' .. tostring(kind)); return
end
if call(officerType,'GetInteractSeat',action) ~= {{passenger.Seat}} then
print_error(prefix .. 'skipped: seat changed'); return
end
local phase = call(actionType,'GetActionState',action)
if phase ~= 1 and phase ~= 2 and tostring(phase) ~= 'Pending' and tostring(phase) ~= 'Running' then
print_error(prefix .. 'skipped: action already ending'); return
end
-- VehicleInteractPointTagConfig: 41..44=EscortNpc_Police_0..3.
-- 51..54 belong to the NPC and must not be used for the officer.
local tag = 41 + {{passenger.Seat}}
local point = call(moduleType,'GetInteractPointIndexByTag',module,tag)
if point == nil or point < 0 then print_error(prefix .. 'skipped: no police point'); return end
local target = call(moduleType,'GetInteractWorldPos',module,point)
local p = player.Position -- UXVector3 uses uppercase fields.
local dx, dz = target.x-p.X, target.z-p.Z
local distance = math.sqrt(dx*dx + dz*dz)
-- This refresh is for the nearby door, not a new path through
-- the vehicle from the opposite side or a stale scene request.
if distance ~= distance or distance > 1.5 or math.abs(target.y-p.Y) > 1 then
print_error(prefix .. 'skipped: target too far distance=' .. tostring(distance)); return
end
local trigger = get('LX6.Drive.Interact.VehicleTriggerInteractAction','interactTrigger',action)
local oldTag = call(moduleType,'GetTriggerMapToInteractPointTag',module,trigger)
-- Log confirmed oldTag==policeTag==43: resetting this same
-- destination did not fix the visual door mismatch. Observe only.
print_error(prefix .. 'observed seat={{passenger.Seat}} oldTag=' .. tostring(oldTag)
.. ' policeTag=' .. tostring(tag) .. ' distance=' .. tostring(distance))
end)
if not ok then print_error('[LIXI-BOARD] officer-audit-v3 failed: ' .. tostring(err)) end
""";
try
{
await gate.NotifyAsync(MethodId.SyncDoToLuaMaster,
new UxWriter().UxString(script).ToArray(), session.ClosedToken);
}
catch (OperationCanceledException) when (session.ClosedToken.IsCancellationRequested) { throw; }
catch (Exception ex)
{
// Keep the original native boarding response available if the gate
// is absent; this helper must never strand a reserved passenger.
session.Log.Warn($"[LIXI-BOARD] officer entry refresh unavailable: {ex.GetType().Name}");
}
}
}