using Ananta.SDK.Network; using Ananta.SDK.Serialization; using Ananta.Server.Protocol.Client4229938; namespace Ananta.Server.Handlers.Game; internal sealed partial class GameRouter { // Experimental visual-only correction for the recorded PoliceCar02a case. // Never offset the entity root, seat attachment, or player model. private static async Task PoliceAlightVisualHeight4229938(TcpSession session, ulong agent, bool restore, ulong vehicle = 0) { if (!session.Items.TryGetValue(PoliceGateSessionKey4229938, out var value) || value is not TcpSession gate || gate.ClosedToken.IsCancellationRequested) return; var script = $$""" local ok, err = pcall(function() _G.__lixiAlightHeight = _G.__lixiAlightHeight or {} local key = '{{agent}}' local saved = _G.__lixiAlightHeight[key] if {{(restore ? "true" : "false")}} then _G.__lixiAlightHeight[key] = nil if saved and saved.stop then saved.stop() elseif saved and saved.root then saved.root.localPosition = saved.position end return end if saved then return end local npc = gCS.SceneDataMgr.GetUnit(ulong.new({{(uint)agent}}, {{agent >> 32}})) if not npc or not npc.MeshRoot then return end local root = npc.MeshRoot local pos = root.localPosition local car = LX6.Drive.DriveManager.Instance:GetBaseVehicle(ulong.new({{(uint)vehicle}}, {{vehicle >> 32}})) if not car then return end local transform = car.gameObject.transform local function relativePosition() local p = npc.Position -- BaseUnit.Position is UXVector3 (uppercase X/Y/Z), NOT -- Unity Vector3. The latter is returned by the transform. return transform:InverseTransformPoint(UnityEngine.Vector3.New(p.X,p.Y,p.Z)) end local start = relativePosition() -- User-confirmed normal visual position is the original mesh -- offset PLUS 0.414 m, not an inferred vehicle/root height gap. -- Hold this full correction through the stationary door phase. -- This calibration is ONLY enabled for 81004027 / 4065053 below. local hook = 'lixi-alight-height-' .. key local s = {root=root, position=UnityEngine.Vector3.New(pos.x,pos.y,pos.z), started=UnityEngine.Time.realtimeSinceStartup, progress=0, stopped=false} s.stop = function() 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 pcall(function() root.localPosition = s.position end) if _G.__lixiAlightHeight[key] == s then _G.__lixiAlightHeight[key] = nil end end s.tick = function() if s.stopped then return end local success, failure = pcall(function() if UnityEngine.Time.realtimeSinceStartup - s.started >= 20 or gCS.SceneDataMgr.GetUnit(ulong.new({{(uint)agent}}, {{agent >> 32}})) ~= npc or npc.MeshRoot ~= root then s.stop(); return end local p = relativePosition() local dx, dz = p.x-start.x, p.z-start.z local distance = math.sqrt(dx*dx + dz*dz) -- Door-opening time does not lower the model. Only real -- vehicle-relative exit movement consumes compensation. -- Never raise it again if the authored action steps back. s.progress = math.max(s.progress, math.max(0, math.min(1, (distance-0.10)/0.90))) local u = s.progress local fade = 1-u*u*(3-2*u) root.localPosition = UnityEngine.Vector3.New(s.position.x,s.position.y+0.414*fade,s.position.z) end) if not success then s.stop() print_error('[LIXI-ANIM] height-fixed0414-v2 stopped: ' .. tostring(failure)) end end _G.__lixiAlightHeight[key] = s local installed = false -- Verified in this client's FPM log: hook=camera. LateUpdateBeat -- is absent in this runtime and must not be a prerequisite. 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 update hook') end s.tick() if not s.stopped then print_error('[LIXI-ANIM] height-fixed0414-v2 installed hook=' .. s.hook .. ' offset=0.414 agent=' .. key) end end) if not ok then print_error('[LIXI-ANIM] visual-height failed: ' .. tostring(err)) end """; await gate.NotifyAsync(MethodId.SyncDoToLuaMaster, new UxWriter().UxString(script).ToArray(), session.ClosedToken); } private static async Task BeginPoliceAlightVisualHeight4229938(TcpSession session, uint child, ulong agent, ulong vehicle) { try { var vehicles = GetVehicleRuntimeState(session); lock (vehicles.SyncRoot) if (!vehicles.Vehicles.TryGetValue(vehicle, out var car) || car.ConfigId != 81004027) return; // Caller already holds the police gate. Queue the visual controller // before the native handoff; a one-second delay exposes the root snap. if (!PoliceRuntimes.TryGetValue(session, out var r)) return; if (r.Child != child || r.Agent != agent || !r.Alighting || !r.Npcs.TryGetValue(agent, out var npc) || npc.Template != 4065053) return; await PoliceAlightVisualHeight4229938(session, agent, restore: false, vehicle: vehicle); } catch (OperationCanceledException) { } catch (Exception ex) { session.Log.Warn($"[LIXI-ANIM] height correction stopped: {ex.GetType().Name}"); } } }