This repository has been archived on 2026-10-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files

168 lines
10 KiB
Go
Raw Permalink Normal View History

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)。
//