91 lines
6.1 KiB
Go
91 lines
6.1 KiB
Go
package methods
|
||
|
||
//
|
||
// GameGround zone payloads — the party/minigame framework (darts / bowling / BBQ / ChineseChess /
|
||
// Racing / …) that vehicles racing is a *zone* of, not a standalone feature (§59).
|
||
// Only the Racing branch is modelled here, because that is the only zone the server currently
|
||
// needs to PUSH (SyncGameGroundZoneInfo 68004609) for the Route-A race-start experiment.
|
||
// ─────────────────────────────── WIRE FACTS (verified, do not "tidy" the order) ───────────────
|
||
// Source of truth = the client's own serializer, RPCSerializeAuto.lua
|
||
// (function Auto.WriteRacingZoneInfo, line 7474). Field order is copied from it verbatim.
|
||
// ⇒ The payload is a COMPLEX (non-Inline) contract, so [UxContract(TypeMark = 15)] makes
|
||
// UxSerializer emit a leading **0x0F (15)** discriminator byte in front of the fields.
|
||
// The client's polymorphic dispatch table for the zone method (68004609) then routes 0x0F to
|
||
// its RacingZoneInfo reader. Getting this byte wrong does not throw — the client silently reads
|
||
// the zone as a plain base zone and nothing happens.
|
||
// ⇒ Field ORDER is Racing-declared-first, then base — the OPPOSITE of C# inheritance layout
|
||
// (dump.cs shows CfgId at 0x50, i.e. after the base fields, but the wire is generated from the
|
||
// lua writer, which emits CfgId first). Therefore this contract MUST stay FLAT: do not refactor
|
||
// it into a base class, because UxSerializer orders fields by metadata token, which
|
||
// would put the base fields first and shift every byte.
|
||
// ⛔ CORRECTION (R203, 2026-09-30): earlier revisions of this header cited
|
||
// Auto.Dispatch[449] / Concrete[449][15] = Auto.Reader[1403] and
|
||
// Auto.Dispatch[476] for ParticipantInfos. Those indices are WRONG — Auto.Reader[476]
|
||
// is the mahjong-hand reader and Auto.Reader[1403] is an unrelated vehicle-target struct.
|
||
// Do not trust a reader/dispatch index unless the client Lua function it names actually reads the
|
||
// fields you expect. ParticipantInfos element shape is now settled by byte-level proof against
|
||
// the client's own writer — see _gomoku_uxcheck R1–R6.
|
||
// Order reference: Drmk.Proxy/proxy/lua/RPCSerializeAuto.lua Auto.WriteRacingZoneInfo (line 7474).
|
||
//
|
||
//
|
||
// GameGroundZoneCountDownInfo — Auto.Reader[482].
|
||
// Framed as a STRUCT (Base.WriteStruct): **no 0xFF marker**, just 4 × uint32.
|
||
//
|
||
//
|
||
// RacingResultData — Auto.Reader[450], one entry per finished participant.
|
||
// Complex framing (the client reads it through Base.ReadComplex), so each item carries the
|
||
// default 0xFF marker. Field order copied from Auto.WriteRacingResultData.
|
||
//
|
||
//
|
||
// GameGroundParticipantInfo — the element type of RacingZoneInfo.ParticipantInfos. The client's
|
||
// own Auto.WriteRacingZoneInfo writes each element via
|
||
// Base.WriteComplexWrap(Auto.WriteGameGroundParticipantInfo, "GameGroundParticipantInfo", true),
|
||
// which puts a 0xFF present marker in front of the fields (this type is flat, so 0xFF is what
|
||
// UxContract emits by default — no TypeMark needed).
|
||
// Wire order (verified against Auto.WriteGameGroundParticipantInfo, RPCSerializeAuto.lua:3984):
|
||
// Pid → NpcCultivationId → AIAgentInfo (nullable complex) → AgentInstanceId →
|
||
// VehicleInstanceId → SeatIndex → IsReady → IsPlayAgain.
|
||
// AIAgentInfo carries the AI opponent's identity so the client can spawn + nameplate AI cars.
|
||
// It is nullable: a null value serialises as a single presence byte (0), which is exactly what the
|
||
// empty grid produced before the AI grid was wired in. The grid itself is built by
|
||
// GameRouter.BuildRacingAiGrid (RacingZoneProbe.cs).
|
||
//
|
||
//
|
||
// LinkAIAgentInfo — AI opponent identity, nested (nullable) inside GameGroundParticipantInfo.
|
||
// Wire order verified against Auto.WriteLinkAIAgentInfo (RPCSerializeAuto.lua:5454):
|
||
// Uid → FightSpiritId → Fashion(nullable LinkAIFashionInfo) → Nickname(nullable string) →
|
||
// NameId → AvatarImageId → VehicleId.
|
||
// Fashion is sent null from the server (presence byte 0) — that skips the deep
|
||
// OtherPlayerSpiritWearFashionsInfo.WearInfo graph entirely and is still enough for the client
|
||
// to spawn + drive the AI car. WearInfo (in LinkAIFashionInfo) therefore only exists to satisfy
|
||
// the contract graph and reuses the auto-generated OtherPlayerSpiritWearFashionsInfo (the
|
||
// wire-flattened, authoritative form).
|
||
//
|
||
//
|
||
// LinkAIFashionInfo — nested (nullable) inside LinkAIAgentInfo. The client only reads it when
|
||
// Fashion is non-null, which the server never sends, so WearInfo here exists solely to
|
||
// keep the contract graph resolvable. Type reuses Auto.OtherPlayerSpiritWearFashionsInfo.
|
||
// Wire order: SuitId → WearInfo(nullable Auto.OtherPlayerSpiritWearFashionsInfo).
|
||
//
|
||
//
|
||
// RacingParticipantInfo — the Racing-specific SUBTYPE of GameGroundParticipantInfo.
|
||
// ⛔ NOT used by RacingZoneInfo.ParticipantInfos. The client's own zone writer
|
||
// (Auto.WriteRacingZoneInfo, RPCSerializeAuto.lua:7474) types the list elements as the BASE
|
||
// GameGroundParticipantInfo via Base.WriteComplexWrap(Auto.WriteGameGroundParticipantInfo, …),
|
||
// i.e. each element is 0xFF + 8 base fields, with NO TypeMark. Putting this subtype in that
|
||
// list emitted 0x0E + 13 fields per element and desynced the packet (R203).
|
||
// This class is kept only because Auto.WriteRacingParticipantInfo exists in the client's
|
||
// serializer; do not wire it into the zone payload.
|
||
// ⚠️ Also note the field list below does NOT match Auto.WriteRacingParticipantInfo: the client
|
||
// writer has 12 fields (AiVehicleCfgId → CheckPointIndex → Lap → RacingTotalTimeMilli → Pid →
|
||
// NpcCultivationId → AIAgentInfo → AgentInstanceId → VehicleInstanceId → SeatIndex → IsReady →
|
||
// IsPlayAgain) and has no CurrentVehicleInstanceId; this contract has 13. Anything that
|
||
// does use this class must first be re-verified field-by-field against that writer.
|
||
//
|
||
//
|
||
// RacingZoneInfo — payload of SyncGameGroundZoneInfo (68004609).
|
||
// 🔴 TypeMark = 15 is load-bearing (see the file header): it is the discriminator byte the
|
||
// client's Auto.Dispatch[449] uses to pick Auto.Reader[1403].
|
||
// 🔴 Field order = wire order = declaration order. Do not reorder, do not extract a base class.
|
||
//
|