feat: Upgrade server with HyNB v2.4 modular architecture, minigames, admin GM web panel, companion summons, and car radio
This commit is contained in:
1 parent
74e2963a12
commit
3e5adf1dc5
1571 files changed
+336468
-6348
No files matched your search
@@ -0,0 +1,96 @@
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user