package routers import ( "context" "sync" ) var _ = sync.Mutex{} var _ = context.Background // // 原版赛车图加载通道 — SyncPlayerSingleTask(64917604) + TaskSpoonViewInfo. // WHY THIS FILE EXISTS (2026-09-23 evening, "还原大于一切") // Until now the racing Spoon graph was force-merged by a CLIENT fastpatch that injected // "<raidId>_RacingDriver" into EnterSceneInfo.SceneSpoonNames. That is NOT the original // behaviour and we can now prove it: in the only 4 official-server captures we own // (logs\packets-20260913-*.log; 09-14 onward is our own private server — the discriminator is // that SyncAetherAIVehicleAddData first appears 09-14 15:41), the official server sends // SceneSpoonNames: [] AND SceneSpoonMd5s: [] while the player stands inside the racing // raid 23300888. RaidConfig carries no spoon field either. So the racing graph is NOT delivered // as a scene spoon. // The remaining native channel is the TASK graph: // // SpoonTaskMgr (client, [ExportToLua]) // TASK_SPOON_FILE_PATH / TASK_RAID_FILE_PATH ← graphs are local files // GetSpoonTaskFilePath(spRaidId, alias, out fileName) ← keyed by (spRaidId, alias) // SyncPlayerSingleTask(taskId, TaskViewData, TaskSpoonViewInfo, TaskStateData) ← RPC entry // LoadTaskWorkActionData(taskId, taskSpoon) → GetTaskWorkActionByTask // // TaskSpoonViewInfo = { SpoonMd5, SpRaidId, StartTaskId, EndTaskId, Alias, EventId, // EventStartTaskId } — and the racing track table authors exactly those two keys inside // RacingDriverTrackInformationConfig.RaidSpoonName: // // "23300888_RacingDriver" → SpRaidId 23300888, Alias "RacingDriver" // "spoon23301289_RacingDriver" → SpRaidId 23301289, Alias "RacingDriver" // // GameRouter.Task.cs already speaks this RPC, but ONLY for the demo story task and with a // PLACEHOLDER SpoonMd5; the racing task 60000147 was never sent through it. That is the // documented gap this file closes. // WHAT IT SHOULD FIX // The 2026-09-23 probes read task.workActions=0 and, in the client log, // CarRaceManager.lua:291/417 nextRankPoint is nil + counter is nil spam at ~60/s — i.e. // the racing task has NO work actions, which is exactly what LoadTaskWorkActionData fills in. // Without them the client cannot build the checkpoint / rank-point list, so the arrow, the GPS track // and the rank logic are all dead. Those checkpoints are also the only thing that could give the // racing AI its route, which is why the AI opponents currently spawn but never drive. // IS IT ORIGINAL? // The RPC, the payload types and the key derivation are all native (client dump + client-generated // readers). What is NOT original is the CONTENT: we synthesise the task-graph reference from // RacingDriverTrackInformationConfig because the official server's task-Spoon config // (TASK_SPOON_FILE_PATH / TASK_RAID_FILE_PATH source) is server-side and we do not have // it. SpoonMd5 is therefore left empty, and the client then silently ignores the whole // push (no error, no graph, workActions = 0). The real md5 is the one genuinely missing piece. // ⛔ 已证伪的两个旧说法(别再用): // 1. 「空 md5 客户端会容忍,只警告版本不一致、照旧跑图」—— 那是**场景**通道的行为 // (SpoonRaidMgr.cs:87),**任务图**通道不是。任务图这边是静默忽略。 // 2. 「填个非空假 md5 就能逼客户端走加载分支」—— 实测**硬崩**(R211b,2026-09-30 23:05)。 // 见 RacingTaskSpoonMd5。 // SAFETY // Additive only: nothing already being sent changes, and a failure degrades to today's behaviour. // Kill switch: RacingTaskSpoonLoadEnabled. // // // Splits a RacingDriverTrackInformationConfig.RaidSpoonName into the two keys // TaskSpoonViewInfo actually wants: SpRaidId (the numeric prefix, with an optional // "spoon" word in front) and Alias (everything after the first underscore). // Real values observed in the client config: // // 23300888_RacingDriver → (23300888, "RacingDriver") // 23300999_RacingDriver → (23300999, "RacingDriver") // spoon23301289_RacingDriver → (23301289, "RacingDriver") // spoon23301305_main → (23301305, "main") // spoon23301294_23301294_Race_Chaos_Gadgets → (23301294, "23301294_Race_Chaos_Gadgets") // // Returns false (and zero/empty) when the string cannot be parsed — the caller then skips the // push rather than sending a nonsense graph key. // func GameRouter_RacingTaskGraph_TryParseRaidSpoonName(ctx context.Context, raidSpoonName any, spRaidId uint32, alias string) bool { // Router implementation for GameRouter_RacingTaskGraph_TryParseRaidSpoonName return true } // // Racing task-graph reference for a track: SpRaidId + Alias parsed from the track's // authored RaidSpoonName, plus the track's EventIds[0] for // TaskSpoonViewInfo.EventId. // The authored name is authoritative and self-contained (its numeric prefix IS the spoon raid), // so is NOT used as a filter — the caller logs it for inspection only, // because the client keys the graph on (spRaidId, alias) rather than on the player's raid. // Alias precedence (highest first): // 1. RacingTaskSpoonAliasOverride — manual A/B knob. // 2. RacingTaskSpoonVerifiedAliases — alias literally read from a client asset. // 3. parsed from RaidSpoonName (known to be the SCENE-spoon spelling, so it is only a // last resort and is logged as such). // func GameRouter_RacingTaskGraph_TryResolveRacingTaskSpoon(ctx context.Context, trackId uint32, spRaidId uint32, alias string, eventId uint32, aliasSource string) bool { // Router implementation for GameRouter_RacingTaskGraph_TryResolveRacingTaskSpoon return true } // Back-compat overload without the alias-provenance output. func GameRouter_RacingTaskGraph_TryResolveRacingTaskSpoon_2(ctx context.Context, trackId uint32, spRaidId uint32, alias string, eventId uint32) bool { // Router implementation for GameRouter_RacingTaskGraph_TryResolveRacingTaskSpoon_2 return true } // // Pushes SyncPlayerSingleTask(64917604) for one racing task so the client loads the racing // TASK graph and populates its work actions (checkpoints / rank points). // Wire (method-frame body, Inline — no root 0xFF marker; nested entities are Complex): // TaskId = uint32 // TaskInfo = TaskViewData (0xFF) // SpoonViewInfo = TaskSpoonViewInfo (0xFF) // StateData = TaskStateData (0xFF) // Best-effort: any failure is logged and swallowed so the race itself is unaffected. // func GameRouter_RacingTaskGraph_PublishRacingTaskSpoonAsync(ctx context.Context, rpcCtx *RpcContext, taskId uint32, trackId uint32, raidId uint32) any { // Router implementation for GameRouter_RacingTaskGraph_PublishRacingTaskSpoonAsync var zero any; return zero }