116 lines
6.0 KiB
Go
116 lines
6.0 KiB
Go
package routers
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
)
|
|
|
|
var _ = sync.Mutex{}
|
|
var _ = context.Background
|
|
|
|
//
|
|
// ROUTE A race-start experiment (§59.14 §7.9): push a single SyncGameGroundZoneInfo
|
|
// (68004609) carrying a RacingZoneInfo and watch what the client does with it.
|
|
// The question this answers is the only remaining unknown in the racing chain:
|
|
// <code>
|
|
// 68004609 → C# GameSceneToClientImpl.SyncGameGroundZoneInfo (dump.cs:184816)
|
|
// → lua GameSceneToClientImpl.SyncGameGroundZoneInfo (lua:1135)
|
|
// → Racing branch → gChallengeManager:OnSyncRacingZoneInfo (lua:526)
|
|
// → ...and then? lua never calls L50.Spoon.CarRaceManager.Instance:SyncCarRaceGame
|
|
// </code>
|
|
// Either the C# hop stops at the C# CarRaceManager itself (in which case this single packet is
|
|
// enough to stage the grid, run the countdown and start the lap clock), or the race only runs when
|
|
// a Spoon graph calls SyncCarRaceGame (IDMAP 49071), in which case nothing happens and
|
|
// Route A stays blocked on the Spoon md5 problem.
|
|
// Observable outcomes (see VEHICLE-RACING-INVESTIGATION.md §7.9):
|
|
// • cars appear on the track / countdown panel / AI dots on the minimap (gMapCsApi.OnCarRaceStart)
|
|
// ⇒ (a) C#-internal wiring — Route A is unblocked, only participants + settlement remain.
|
|
// • absolutely nothing, and no CarRaceManager line in Player.log
|
|
// ⇒ (b) Spoon-graph driven — Route A needs the Spoon md5.
|
|
// Nothing here is wired into normal gameplay: the only entry point is the debug HTTP API
|
|
// (/api/racing/zone-probe), so this is inert until deliberately fired.
|
|
//
|
|
//
|
|
// Options surfaced to the debug panel / HTTP caller. cfgId is a
|
|
// RacingDriverTrackInformationConfig Id (the "Id" column, NOT an EventId).
|
|
//
|
|
func GameRouter_RacingZoneProbe_RacingZoneProbeStatus(ctx context.Context, ) any {
|
|
// Router implementation for GameRouter_RacingZoneProbe_RacingZoneProbeStatus
|
|
var zero any; return zero
|
|
}
|
|
|
|
//
|
|
// Build the AI opponent grid for a racing zone. Returns BOTH:
|
|
// • AIVehicleInfos — the plain dict (vehicleInstanceId → cfgId) that drives the minimap
|
|
// blue dots (client GetAllVehicleAIIds reads AIVehicleInfos); and
|
|
// • ParticipantInfos — real AI opponents, each a GameGroundParticipantInfo4229938
|
|
// (0xFF marker + 8 base fields), which is the element shape the client's own
|
|
// Auto.WriteRacingZoneInfo uses. (R203: the earlier RacingParticipantInfo/TypeMark=14
|
|
// shape desynced the packet by 28 B/participant.)
|
|
// Returns empty lists when ≤ 0 (byte-compatible with the pre-AI probe).
|
|
// Opponent cfgIds come from the track's HighAI/MiddleAI/LowAI pools (RacingTrackRow); if the row is
|
|
// missing or the pools are empty, no AI is produced.
|
|
//
|
|
func GameRouter_RacingZoneProbe_BuildRacingAiGrid(ctx context.Context, trackCfgId uint32, count int32, realVehicles []any) any {
|
|
// Router implementation for GameRouter_RacingZoneProbe_BuildRacingAiGrid
|
|
var zero any; return zero
|
|
}
|
|
|
|
//
|
|
// One AI opponent entry, typed as the BASE GameGroundParticipantInfo — which is what the
|
|
// client's own Auto.WriteRacingZoneInfo writes for this list
|
|
// (Base.WriteComplexWrap(Auto.WriteGameGroundParticipantInfo, …)): each element is
|
|
// 0xFF (present marker) + 8 base fields, no TypeMark.
|
|
// is the SCENE ENTITY id (it is also what the client keys
|
|
// AIVehicleInfos by) and is the VehicleConfig the car
|
|
// was built from. It lands in VehicleInstanceId / AIAgentInfo.VehicleId; the
|
|
// Racing-only extras (AiVehicleCfgId / Lap / RacingTotalTimeMilli …) are NOT
|
|
// part of this shape — R203 proved adding them desyncs the packet by 28 B per participant.
|
|
//
|
|
func GameRouter_RacingZoneProbe_BuildRacingParticipant(ctx context.Context, agentInstanceId uint64, aiCfgId uint32, vehicleCfgId uint32, seatIndex int32) any {
|
|
// Router implementation for GameRouter_RacingZoneProbe_BuildRacingParticipant
|
|
var zero any; return zero
|
|
}
|
|
|
|
//
|
|
// Build the payload the client's Auto.Dispatch[449] expects: the RacingZoneInfo
|
|
// discriminator (0x0F, emitted by [UxContract(TypeMark = 15)]) followed by the Racing
|
|
// fields and then the GameGroundZoneInfo fields.
|
|
// When > 0 the grid is populated with that many AI opponents
|
|
// (cfgIds from the track's HighAI/MiddleAI/LowAI pools); when 0 or omitted the grid stays empty,
|
|
// which is also the byte-compatible original probe. The LinkAIAgentInfo contract is now declared
|
|
// (RacingZoneMethods.cs), so a non-empty grid serialises correctly.
|
|
// <para>
|
|
// / are unix seconds for
|
|
// GameGroundZoneCountDownInfo (dump.cs:1918571). They drive the client's countdown panel
|
|
// and therefore the vehicle-control release — an all-zero countdown is what left the player in
|
|
// an undrivable car on 2026-09-22 (see GameRouter.RacingAutoStart.cs). Both default to 0 so the
|
|
// debug /api/racing/zone-probe payload stays byte-identical to the original 71-byte probe.
|
|
// </para>
|
|
//
|
|
func GameRouter_RacingZoneProbe_BuildRacingZoneProbe(ctx context.Context, cfgId uint32, taskId uint32, zoneState byte, syncReason byte, zoneSessionId any, gadgetUId uint64, startTime uint32, gameTimeEnd uint32, prepareEnd uint32, displayEnd uint32, aiCount int32, realAiVehicles []any) any {
|
|
// Router implementation for GameRouter_RacingZoneProbe_BuildRacingZoneProbe
|
|
var zero any; return zero
|
|
}
|
|
|
|
//
|
|
// Human-readable sanity check for the caller-supplied cfgId, so a bad id is caught before it
|
|
// reaches the client (an unknown cfgId would make InitAetherClientData a no-op and
|
|
// silently produce a "nothing happened" result that looks like outcome (b)).
|
|
//
|
|
func GameRouter_RacingZoneProbe_ValidateRacingCfgId(ctx context.Context, cfgId uint32) any {
|
|
// Router implementation for GameRouter_RacingZoneProbe_ValidateRacingCfgId
|
|
var zero any; return zero
|
|
}
|
|
|
|
//
|
|
// A null complex: exactly one 0x00 byte. The client's
|
|
// GameSceneToClientImpl.SyncGameGroundZoneInfo treats a nil zone as "tear down the
|
|
// current zone", which is also the only way to undo the probe without relogging.
|
|
//
|
|
func GameRouter_RacingZoneProbe_BuildRacingZoneClearBody(ctx context.Context, ) []byte {
|
|
// Router implementation for GameRouter_RacingZoneProbe_BuildRacingZoneClearBody
|
|
return nil
|
|
}
|
|
|