90 lines
6.1 KiB
Go
90 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.
|
|||
|
|
//
|