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}"); } } }