using Ananta.SDK.Network; using Ananta.SDK.Serialization; using Ananta.Server.Protocol.Client4229938; namespace Ananta.Server.Handlers.Game; internal sealed partial class GameRouter { // Hold the officer's initial visual X/Z during the door animation, as // requested by the user. Do NOT move her to police point43/44. The native // controller and passenger bindings remain untouched. private static async Task PoliceOfficerBoardingPosition4229938(TcpSession session, PoliceRuntime4229938 runtime, ulong owner, bool restore = false) { var passenger = runtime.PendingBoarding; if (!restore) { if (owner == 0 || passenger == null || passenger.Seat is not (2 or 3) || !runtime.Npcs.TryGetValue(runtime.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; ulong vehicleId = passenger?.Vehicle ?? 0; var script = $$""" local ok, err = pcall(function() local prefix = '[LIXI-BOARD] officer-inplace-v2 ' _G.__lixiOfficerBoardPosition = _G.__lixiOfficerBoardPosition or {} local key = '{{runtime.Agent}}' local saved = _G.__lixiOfficerBoardPosition[key] if {{(restore ? "true" : "false")}} then if saved then saved.stop() end return end if saved then return end local get, call = ReflectionGetProperty, ReflectionCallMethod if type(get) ~= 'function' or type(call) ~= 'function' then return end local id = ulong.new({{(uint)owner}}, {{owner >> 32}}) local vid = ulong.new({{(uint)vehicleId}}, {{vehicleId >> 32}}) local player = gCS.SceneDataMgr.GetUnit(id) local car = LX6.Drive.DriveManager.Instance:GetBaseVehicle(vid) if not player or not player.MeshRoot or not car then return end local moduleType = 'LX6.Drive.VehicleInteractModule' local actionType = 'LX6.Drive.Interact.VehicleInteractAction' local officerType = 'LX6.Drive.Interact.PlayerEscortToVehicleAction' local module = get('LX6.Drive.BaseVehicle','interactModule',car) local action = module and call(moduleType,'GetUnitCurrentAction',module,id) if not action then return end local kind = get(actionType,'interactType',action) if kind ~= 10 and tostring(kind) ~= 'PoliceEscortToVehicle' then return end if call(officerType,'GetInteractSeat',action) ~= {{passenger?.Seat ?? 0}} then return end local root = player.MeshRoot local parent, pos = root.parent, root.localPosition if not parent then return end local original = UnityEngine.Vector3.New(pos.x,pos.y,pos.z) local initial = parent:TransformPoint(original) local anchorX, anchorY, anchorZ = initial.x,initial.y,initial.z local hook = 'lixi-officer-board-position-' .. key local s = {started=UnityEngine.Time.realtimeSinceStartup, stopped=false,applied=false} s.stop = function(reason) if s.stopped then return end s.stopped = true if s.hook == 'camera' then pcall(function() gCameraUtils.RemoveCameraUpdateEvent(hook) end) end if s.timer then pcall(function() s.timer:Stop() end) end if s.applied then pcall(function() root.localPosition=original end) end if _G.__lixiOfficerBoardPosition[key] == s then _G.__lixiOfficerBoardPosition[key]=nil end if reason then print_error(prefix .. 'cleanup: ' .. reason .. ' applied=' .. tostring(s.applied)) end end s.tick = function() if s.stopped then return end local good, failure = pcall(function() local now = UnityEngine.Time.realtimeSinceStartup if now-s.started >= 15 or gCS.SceneDataMgr.GetUnit(id) ~= player or player.MeshRoot ~= root or root.parent ~= parent or LX6.Drive.DriveManager.Instance:GetBaseVehicle(vid) ~= car then s.stop('timeout/unit changed'); return end -- Never leak into walking, driving or the successful alighting action. if call(moduleType,'GetUnitCurrentAction',module,id) ~= action or call(officerType,'GetInteractSeat',action) ~= {{passenger?.Seat ?? 0}} then s.stop('action/seat changed'); return end local phase = call(actionType,'GetActionState',action) local pending = phase == 1 or tostring(phase) == 'Pending' local running = phase == 2 or tostring(phase) == 'Running' if not pending and not running then s.stop('phase=' .. tostring(phase)); return end -- Install before the boarding reaction, including Pending: -- the previous Running-only gate never reached 'applied' in -- the 00:49:54 test. Keep the original start location instead -- of chasing a configured door point or smoothing toward it. local base = parent:TransformPoint(original) local dx,dz = anchorX-base.x,anchorZ-base.z if dx ~= dx or dz ~= dz or dx*dx+dz*dz > 2.25 or base.y ~= base.y or math.abs(anchorY-base.y) > 1 then s.stop('position safety limit'); return end -- Only cancel horizontal root drift. Preserve native height, -- body rotation and the skeletal door-opening animation. local corrected = parent:InverseTransformPoint(UnityEngine.Vector3.New(anchorX,base.y,anchorZ)) root.localPosition = corrected if not s.applied then print_error(prefix .. 'applied seat={{passenger?.Seat ?? 0}} phase=' .. tostring(phase) .. ' anchorXZ=' .. tostring(anchorX) .. ',' .. tostring(anchorZ)) end s.applied=true end) if not good then s.stop(); print_error(prefix .. 'stopped: ' .. tostring(failure)) end end _G.__lixiOfficerBoardPosition[key]=s local installed=false if gCameraUtils and gCameraUtils.AddCameraUpdateEvent and gCameraUtils.RemoveCameraUpdateEvent then s.hook='camera' installed=pcall(function() gCameraUtils.AddCameraUpdateEvent(hook,s.tick) end) if not installed then pcall(function() gCameraUtils.RemoveCameraUpdateEvent(hook) end);s.hook=nil end end if not installed and Timer and Timer.New then installed=pcall(function() s.timer=Timer.New(s.tick,0.02,-1,true);s.timer:Start() end) if installed then s.hook='timer' end end if not installed then s.stop(); error('no camera/timer hook') end print_error(prefix .. 'installed seat={{passenger?.Seat ?? 0}} hook=' .. s.hook) s.tick() end) if not ok then print_error('[LIXI-BOARD] officer-inplace-v2 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) { session.Log.Warn($"[LIXI-BOARD] officer position unavailable: {ex.GetType().Name}"); } } }