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
Ananta-ps/pkg/protocol/methods/racing_zone_methods.go
T

90 lines
6.1 KiB
Go
Raw Normal View History

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.
//