169 lines
10 KiB
Go
169 lines
10 KiB
Go
package methods
|
||
|
||
//
|
||
// Vehicle summon/ownership wire contracts for client 4229938. Field order is the wire
|
||
// order, verified 1:1 against lua/LuaGen/AutoGen/RPCSerializeAuto.lua
|
||
// (WriteVehicleClientInfo, WriteLogicVehicleClientInfo, WriteSummonVehicleResult,
|
||
// WritePlayerVehicleClientDetail, WriteRaidVehicleSeatInfo, WriteNewClientBoardingInfo,
|
||
// WriteBoardingExtInfo, WritePlayerVehicleDriveStateInfo, WriteRaidVehicleSyncData,
|
||
// WriteVehicleClientPart) and ClientToGameSceneDelegate.lua arg serializers.
|
||
// New-vs-4091149 deltas already applied: LogicVehicleClientInfo.VehicleSpoonName (tail)
|
||
// and PlayerVehicleClientDetail.IsPersistent (tail).
|
||
//
|
||
//
|
||
// Stock vehicle HP notification; normal SyncUnitHp only handles BaseUnit.
|
||
// Ported from donor_7k (T1-16 载具骇入): CanHack() requires CurrentHp > 0, and
|
||
// SyncSpawnVehicle carries no HP fields, so this packet is mandatory for hackability.
|
||
//
|
||
//
|
||
// Server notify body: SyncUnitBoardingInfo (68699595) — NPC 的座位挂载句柄
|
||
// (客户端 LogicAgentAttachVehicleHandle.Setup:原生座位偏移/旋转 + attach 句柄每帧跟随车辆)。
|
||
// <para>★ 字段顺序 = wire 顺序,2026-09-30 逐字段对照客户端
|
||
// ux_game_all.dump.cs:22762 ClientBoardingInfo 核过:<br/>
|
||
// ulong EntityId / ulong VehicleUId / byte SeatIndex / UXVector3 PositionOffset /
|
||
// UXVector3 RotationOffset / bool CanBeEjected / bool UseSpecificAction /
|
||
// uint ActionGroup / uint ActionId。<br/>
|
||
// 根签名是 SyncUnitBoardingInfo(ClientBoardingInfo info, bool force)
|
||
// ⇒ 见 SyncUnitBoardingInfo4229938。</para>
|
||
// <para>⛑ 客户端把这个 mid 标成 CSharpOnly(RPCDeserializeAuto.lua:1153),
|
||
// Lua 侧没有明文 Reader ⇒ 只能按 dump.cs 字段布局核(规则 239 的客户端取证走 C# 侧)。
|
||
// 1(FF) + 8 + 8 + 1 + 12 + 12 + 1 + 1 + 4 + 4 = 52 B。</para>
|
||
//
|
||
//
|
||
// Server notify: SyncUnitBoardingInfo (68699595) 全包 = info + force。
|
||
// 合计 53 B。⛑ 捐赠端 v0.5-2 PoliceVehicle.cs:82-91 是手写字节
|
||
// (U8(255) + U64 + U64 + U8 + F32×6 + Bool×2 + U32×2 + Bool(true)),
|
||
// 与这里序列化出来的结果逐字节相同(已核)。
|
||
// ⛑ 解除挂载时 VehicleUId = 0 ⇒ 客户端同时清掉 attach 与原生座位注册。
|
||
//
|
||
// Server notify: SyncPlayerMoveToDriveSeat (68778293).
|
||
// Server notify: SyncPlayerExitVehicle (exit leg).
|
||
//
|
||
// 上下车驱动状态。C2S:AskPlayerStartEnterOrExitVehicle(67225310) /
|
||
// AskPlayerFinishEnterOrExitVehicle(67211905);S2C:
|
||
// SyncPlayerStartEnterOrExitVehicl / SyncPlayerVehicleStateChange /
|
||
// SyncPlayerFinishEnterOrExitVehic(GameRouter.VehicleStory.cs 的强制上下车)。
|
||
// ⛑⛑ 2026-10-03:**不是 Inline**。客户端
|
||
// ClientToGameSceneDelegate.lua:1317 / :1352 用的是
|
||
// SerializeBase.WriteComplex(writer, syncdata, WritePlayerVehicleDriveStateInfo, "syncdata", false)
|
||
// ⇒ 顶层 struct 前面有一个 presence 字节(0xFF)。
|
||
// 实机包体(14:46:43 上车,35B)逐字节对齐:
|
||
// <code>
|
||
// FF presence(有值)
|
||
// 64 00 00 00 00 00 00 00 Pid = 100
|
||
// 01 EnterOrLeave(上车=1 / 下车=0)
|
||
// 01 00 34 F6 8D 40 CC 71 VehicleEntityId = 8200000000000000001
|
||
// 00 00 00 00 SeatIndex = 0
|
||
// 00 IfForce
|
||
// 00 00 00 00 OpenDoorTypeId
|
||
// 00 00 00 00 OpenDoorActionSpeed
|
||
// 00 00 00 00 OpenDoorActionClipLength
|
||
// </code>
|
||
// 以前标 Inline = true ⇒ 少读那 1 字节 ⇒ 尾部剩 1 字节 ⇒ TryGetArgs 失败,
|
||
// 日志常年 AskPlayerStartEnterOrExitVehicle undecodable bytes=35,后果是
|
||
// **服务端根本没处理过上下车**(CurrentVehicleId / LastDrivenVehicleId /
|
||
// 「车流车坐进去先摘出车道流」这些后续逻辑全没跑)。
|
||
// ⛑ 同步影响:S2C 那三包也用这个 DTO,改后它们同样带 presence 字节 —— 这是按
|
||
// 「C2S 与 S2C 由同一份契约生成」的对称性推断,需要实机确认强制上下车按钮仍正常。
|
||
// 回滚:还原 .bak-20261003-drivestate。
|
||
//
|
||
// Client invoke arg: AskPublicSwitchToPublicScene (63491547).
|
||
// Client invoke arg: AskEnterRaidByMapEntrance (63510026).
|
||
// Aether vehicle init (SyncAetherAIInitDatas, 68704070). Lists stay empty.
|
||
// Never serialized (all Aether init lists are empty); satisfies the serializer.
|
||
// --- ClientTrafficIntersectionInitInfo (dump.cs:1743618) ------------------------------------
|
||
//
|
||
// ★ 这是 AetherAIInitData.Intersections 的真实元素类型。我们此前一直用上面的空 stub,
|
||
// 等于永远发「零个路口」—— 客户端的路口/信号灯系统从来没有拿到过初始化数据。
|
||
//
|
||
// dump 内存布局:基类 TrafficIntersectionPeriodInfo 在 0x10/0x11/0x12,派生在 0x18/0x20/0x24
|
||
// (即内存里基类在前)。但历史笔记记的 wire 顺序是「派生先、基类后」。
|
||
// 两者可能不一致(UxSerializer 按声明顺序写,与内存布局无关),所以两个版本都留着,
|
||
// 由配置 `initVariants` 选哪一个 —— 探测阶段两边都试,看客户端日志哪边有反应。
|
||
//
|
||
// MassTrafficIntersectionState: Start=0 / Transition=1 / Finishing=2。
|
||
// 派生字段在前:InstanceId, ZoneIndex, CurrentState, 然后基类三个周期 byte。
|
||
// 基类字段在前(照 dump 内存偏移 0x10/0x11/0x12 排)。
|
||
// top-int7 framing + 真路口(派生先)。
|
||
// top-int7 framing + 真路口(基类先)。
|
||
//
|
||
// Same four fields, but NOT Inline: UxSerializer emits the single-argument 0xFF object
|
||
// marker first. The 4229938 reader consumes that marker before RaidId (see the note on
|
||
// AetherAIInitData4229938), so this is the framing to use.
|
||
//
|
||
//
|
||
// ★★ 唯一实测正确的 SyncAetherAIInitDatas(68704070) 线格式(2026-09-20 客户端验证)。
|
||
// 0xFF 对象标记 + u32 RaidId + bool HasZoneGraph + i32 ZoneStorageDataHandle
|
||
// + 0xFF list presence + Int7(count+1)。空路口表 ⇒ 共 12 字节:
|
||
// `FF 188B6301 01 611ABBC1 FF 01`(donor `--probe-aether` 输出一致)。
|
||
// 别换成 Int32 count 版(AetherAIInitDataTop4229938),客户端会 capacity 负数崩溃。
|
||
//
|
||
// Bool-first field order, no object marker (diagnostic variant).
|
||
// Client notify arg: AskInteractCmd (67390408). Logged + accepted.
|
||
// Client notify: ReportDrivingVehicle (67528706).
|
||
// Client notify: AskVehicleNitroValue (67533682).
|
||
// Client invoke: AskVehicleNitro (67037029).
|
||
// Client notify: AskModifyVehicleTopSpeed (67920147).
|
||
// Client notify: AskVehicleHorn (67983272).
|
||
// Client notify: AskVehicleContactDamage (67589692).
|
||
// Full contact-damage struct (WriteVehicleContactDamageData order).
|
||
// Client invoke: AskGetVehicleRadioContent (67725622).
|
||
//
|
||
// AskGetVehicleRadioContent(67725622) 的返回体 = UX.Game.RadioRandomContent
|
||
// (TypeDefIndex 33514,字段顺序取自 il2cpp dump,与 DefaultReturnCatalog 的既有 15 字节
|
||
// 全零回包 FF 00 00 00 00 00 … 完全对齐 —— 1 字节 complex 标记 + 1+4+4+4+1)。
|
||
// 所以这里不能 Inline = true:inline 会省掉那个 0xFF,客户端
|
||
// Base.ReadComplex(reader, Auto.Reader[594]) 就会把 ContentType 当成标记字节读掉。
|
||
// 字段语义(RadioPlayerManager.GetVoicesByContent):
|
||
// ContentType 1=Ad 2=News 3=Chat 4=Comment(3/4 都查 RadioContentDJs)5=SpecialNews
|
||
// ContentId 主内容 id(按 ContentType 查对应表拿 SoundId)
|
||
// StartContentId 开场口播 id(查 RadioContentDJs;广告/新闻为 0)
|
||
// EndContentId 收尾口播 id(同上)
|
||
// MarkType 1=Middle 2=End —— 决定串词插在歌的哪个 cue 上
|
||
//
|
||
//
|
||
// Client notify: AskSwitchVehicleRadio (67959772),参数一个 u32(电台序号)。
|
||
// 客户端 RadioPlayerManager.SwitchRadio / SwitchTargetRadio 本地已经切完了才发这帧,
|
||
// 服务端只做记录(切台本身没有任何服务端权威状态)。
|
||
//
|
||
// Client notify: SyncChangeSafeArea (63310118).
|
||
// Client invoke: SyncChangeBuilding (63495099).
|
||
// Client invoke: SyncChangeIndoor (63927817).
|
||
// --- 路口相位更新 SyncAetherAIIntersectionUpdateDa (68819947) -------------------------------
|
||
//
|
||
// ★★ 2026-10-02 从捐赠端 PrivateServerPor 移植(`VehicleAiTaskMethods.cs`)。这是「让红绿灯真的亮」
|
||
// 的那一半:init 只把路口表交给客户端,灯柱要按相位变色还得周期发这条包。
|
||
//
|
||
// ⛑⛑ 契约来源(与 init 那对类型同源,互相印证):
|
||
// dump.cs TypeDefIndex 32648/32650:
|
||
// class TrafficIntersectionPeriodInfo { byte CurrentPeriodIndex; byte NextPeriodIndex; byte RailPeriodIndex; }
|
||
// class ClientTrafficIntersectionPeriodUpdateInfo : TrafficIntersectionPeriodInfo
|
||
// { ulong IntersectionIndex /*0x18*/; MassTrafficIntersectionState CurrentState /*0x20*/; }
|
||
// 声明顺序 = **派生字段在前、基类三 byte 在后**,与主线 `TrafficIntersectionInitInfoDerived4229938`
|
||
// 完全同构(后者是 InstanceId/ZoneIndex/CurrentState 在前 + 三个周期 byte)。
|
||
//
|
||
// ⛑ 这条包在 `RPCDeserializeAuto.lua` 里是 `CSharpOnly`(第 1157 行 `midToExportOption[68819947]`)
|
||
// ⇒ **lua 侧没有 Reader 可对照**(硬规则 209/239 在这里不适用),只能按 dump 定序。
|
||
//
|
||
// 客户端消费方:`AetherCity.Common.AetherIntersectionManager.OnSyncIntersectionPeriodUpdateInfo`。
|
||
// MassTrafficIntersectionState: Start=0 / Transition=1 / Finishing=2。
|
||
type MassTrafficIntersectionState4229938 byte
|
||
|
||
const (
|
||
MassTrafficIntersectionState4229938_Start MassTrafficIntersectionState4229938 = 0
|
||
MassTrafficIntersectionState4229938_Transition MassTrafficIntersectionState4229938 = 1
|
||
MassTrafficIntersectionState4229938_Finishing MassTrafficIntersectionState4229938 = 2
|
||
)
|
||
|
||
//
|
||
// 68819947 的顶层:只有一个 data(与捐赠端逐字一致)。
|
||
// ⛑⛑ **必须 Inline**:捐赠端 `VehicleAiTaskMethods.cs:18` 就是 `[UxContract(Inline = true)]`,
|
||
// 顶**不写**对象标记;`data` 字段自己是 `[UxContract]`(非 Inline)⇒ 由它写那一个 `FF`。
|
||
// 我第一版漏了 Inline ⇒ 变成 `FF FF …`,整包偏移 1 字节,客户端会解歪。
|
||
// 与 init 的 `AetherAIInitData`(非 Inline,根上写一个 FF)**总标记数都是 1**,互相印证。
|
||
//
|
||
//
|
||
// 68819947 的载荷。**字段顺序 = wire 顺序,别动。**
|
||
// IntersectionIndex 取 CSV 第一列 zone_index(配置 intersectionIndexSource)。
|
||
//
|