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/internal/gameserver/routers/game_router_racing_task_graph.go
T

121 lines
6.7 KiB
Go
Raw Normal View History

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:
// <code>
// 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
// </code>
// TaskSpoonViewInfo = { SpoonMd5, SpRaidId, StartTaskId, EndTaskId, Alias, EventId,
// EventStartTaskId } — and the racing track table authors exactly those two keys inside
// RacingDriverTrackInformationConfig.RaidSpoonName:
// <code>
// "23300888_RacingDriver" → SpRaidId 23300888, Alias "RacingDriver"
// "spoon23301289_RacingDriver" → SpRaidId 23301289, Alias "RacingDriver"
// </code>
// 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:
// <code>
// 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")
// </code>
// 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
}