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

170 lines
9.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package routers
import (
"context"
"sync"
)
var _ = sync.Mutex{}
var _ = context.Background
//
// 赛车检查点门(R247 · 2026-10-01)—— 服务端显式摆放。
// 症状:整条赛道上一道绿框门都没有(用户:「开始赛车了,没有看到绿框门」)。
// 为什么没有:门是 gadget 对
// Racing_challenge01_CheckPoint / Racing_challenge01_CheckPointEnd,
// 官方**按检查点位置在运行时 spawn**;而检查点来自
// RacingDriverTrackInformationConfig.RaidSpoonName 指向的 Spoon 图 ——
// 这张图**从未合并进客户端**(SpoonMd5="" ⇒ SpoonRaidMgr 走
// #NoCreateIssue),所以官方那条链一次都没执行过,门**从来没人 spawn**。
// 不是坏了,是没装。
// 为什么必须服务端摆:R244 已经试过「客户端 Lua 自己
// gResourceManager:LoadAssetWithCallBack + GameObject.Instantiate」,
// 实机证据是 16/16 全部实例化成功、go0.active=true、scale=1/1/1、
// children=2、全程**零** Load asset FAILED —— 但**肉眼一道都看不见**。
// 这与「家具全无 / 健身器材 / 摊位补给」是同一个病根:**客户端不会自己实例化这类物件**,
// 必须由服务端经 gadget 通道下发。
// 通道:SpawnGadgetAsync(健身器材 / PropRestock / 摊位补给都走它,实测可见)。
// ⛔ iScale 必须传 0:100(GM 面板默认)出来是 transform scale (0,0,1)
// 的**零缩放空壳、没有 prefab** —— 正是保龄球球瓶「有物理有音效但看不见」的根因。
// 坐标:客户端 R225 已用 LoadRaceTrackData 采出 16 个检查点(沿弧长等分),
// R247 把它们与**服务端那套 pathId** 一起落到 config/racing-gates.json。
// 服务端读表摆放 —— 与健身器材 _gym_capture.py 的「离线采样 → 落配置 → 服务端摆」完全同构。
// 拿不到表时:在玩家四周摆 4 道「验证信标」。它的价值是把
// 「通道对不对」和「坐标对不对」两个变量解耦:看得见信标 ⇒ 通道没问题,只差坐标。
// 回滚:world.content.racingGates.enabled = false(不用重编译)。
//
// R255:把内置官方表变成摆位计划(含 YawOffset 修正)。
//
// ★★★ R256 —— **默认改用官方自己的检查点 prefab**(RacingGateCheckPointPathId),
// 不再用 R253 选的 Jobrace_gongmen(拱门)。
// <para>为什么换回去:config/racing-gates.json 里(客户端探针写的)
// 每个检查点本来就是 pathId 884611825 / 终点 271608686 ——
// 也就是 Res/Prefab/Gadget/Racing_challenge01_CheckPoint.prefab。
// 是 R253 起用 PathIdOverride=64493331 把它**覆盖**成了拱门。</para>
// <para>R254 的注释写过「884611825 资源从未随包 ⇒ 隐形」,**该判断已被推翻**:
// 关卡块 vfc_1479 里官方**实实在在地摆了 16 个该 prefab**
// (+2 个 ...End,见 _r256_gate_instances.py / _r190_racing_checkpoints.json)
// ⇒ 客户端一定能渲染它,资源必然随包。
// 当轮"看不见"的真因是 **yaw 算错**(见下)把门转到了错误方向/位置。</para>
// <para>切换开关:racingGates.pathIdOverride。
// 0(默认)= 官方检查点 prefab(终点自动用 ...End);
// 64493331 = 回退到 R253~R255 的拱门(此时终点同 prefab)。</para>
//
func GameRouter_RacingGates_BuildOfficialTrackGates(ctx context.Context, cfg any) []any {
// Router implementation for GameRouter_RacingGates_BuildOfficialTrackGates
return nil
}
//
// ⛔⛔ R258 定案:这个开关 = 「我们自己造的门宽」,不是官方数据 ⇒ 默认必须关。
// 用户的两条纠正(R258,决定了本开关的命运):
// 「<i>路宽不一定代表门宽啊,不要查错方向了</i>」+「<i>赛道的宽度也是根据检查点的宽度来计算的</i>」
// ⇒ 门宽是「门」这件物件自带的,赛道按它建;所以**没有**需要我们填的宽度,
// 也就不该有 pairMirror / pairOffset 这两个旋钮。
// R257 把它们打开,等于「我们自己在给门调宽」—— 已被明确叫停。
// R258 的官方摆位判据(这才是「一个实例够不够」的正确证法):
// gym_tools/_r190_racing_checkpoints.json 里官方静态摆了 **14 个**
// Racing_challenge01_CheckPoint,两两**最近距离 131.69 m、完全不成对**
// ⇒ 官方就是「**一个检查点摆一个实例**」。
// 再加上 gadget 注册表(vfc_907)里带「拱门」语义的物件**只有一个**
// (Jobrace_gongmen 64493331,其 prefab 自带 _left/_right/_up)
// ⇒ **一扇门 = 一个实例**,宽度由 prefab 自身决定,我们没有可填的数。
// R254 的假设(已推翻):看到画面只有一根立柱 + 顶端向内弯折 ⇒ 认为
// 一个 Jobrace_gongmen 实例只是**半扇拱门**,要同坐标摆两个(yaw 差 180°)才成整扇。
// R255d 的实测证据(推翻它):
// ① 资产扫描(`gym_tools/_r255c_gate_prefab_scan.py`,全库 1682 块)——
// vfc_1583 里 **同一个 prefab** 自带三件套:
// gp_arch_gongmen_blue_left(+`_left_lod2`)、
// gp_arch_gongmen_blue_right(+`_right_lod12`)、
// gp_arch_gongmen_blue_up(+`_up_lod`)⇒ **一个实例就是完整一扇门**。
// R254 当时看到的「只有右半」,最可能是**左半被棕榈树 / 那栋黑楼挡住**。
// ② 打开本开关后用户实测原话:「**门叠在一块了**」——
// 因为两个完整门被摆在**同一坐标**,只能互相 z-fighting 重叠。
// 结论:`pairMirror` 保持 **false**(配置 world.content.racingGates.pairMirror)。
// <para>⛔ R258 更正本条:R257 把 A、B 拆到对侧(-ox/-oz)本身没错,
// 但**整个「成对」模型是自造的**(见上)。官方每检查点只摆 1 个实例
// ⇒ 一扇门 = 一个 prefab,不需要第二个实例,也就没有「柱到中线的距离」要调。
// 所以 pairOffset 的真实语义只是「把整道门横向平移」,与「门宽」无关。</para>
//
func GameRouter_RacingGates_ExpandGatePairs(ctx context.Context, plan []any, cfg any) []any {
// Router implementation for GameRouter_RacingGates_ExpandGatePairs
return nil
}
// 同一个 session 只提醒一次「表还没有」——缺采集文件是设置步骤,不是运行错误。
func GameRouter_RacingGates_RacingGateNoticed(ctx context.Context, session *GameSession) bool {
// Router implementation for GameRouter_RacingGates_RacingGateNoticed
return true
}
//
// 在一局赛车开始时摆门。由 RunRacingAutoStartAsync 在 DISPLAY 推出去之后调用
// (倒计时 3 秒期间门就该在了)。
//
func GameRouter_RacingGates_SpawnRacingGatesAsync(ctx context.Context, session *GameSession, state any, trackId uint32, taskId uint32) any {
// Router implementation for GameRouter_RacingGates_SpawnRacingGatesAsync
var zero any; return zero
}
// R294:撤这一局可能摆过的**所有**赛车门 pathId。
func GameRouter_RacingGates_RacingGatePathIdSet(ctx context.Context, cfg any) []any {
// Router implementation for GameRouter_RacingGates_RacingGatePathIdSet
return nil
}
//
// R294:撤掉**第 个检查点**那对门(A/B 两半)。
// 返回撤掉的个数(0 = 这个点本来就没摆 / 已经撤过)。
//
func GameRouter_RacingGates_RemoveCheckpointGatesAsync(ctx context.Context, session *GameSession, state any, cpIndex int32) int32 {
// Router implementation for GameRouter_RacingGates_RemoveCheckpointGatesAsync
return 0
}
//
// R312:**真正的按坐标撤门**(纯执行,不含任何策略/宽限期/挂起逻辑)。
// <para>抽出来是为了让「R311 撤门宽限期」和「R312 前 N 个门延迟到第 N+1 个通过再撤」
// 两条策略都能调它,而**不会互相重入** —— 否则 `cpIndex &lt; keepFirstN` 的那两个点
// 会在递归里被**再次挂起** ⇒ 永远撤不掉(自己给自己下套)。</para>
//
func GameRouter_RacingGates_RemoveCheckpointGatesCoreAsync(ctx context.Context, session *GameSession, state any, cpIndex int32, gateCfg any) int32 {
// Router implementation for GameRouter_RacingGates_RemoveCheckpointGatesCoreAsync
return 0
}
// R294:撤掉这一局摆过的**全部**赛车门(比赛结束 / 完成上报时调)。
func GameRouter_RacingGates_RemoveAllRacingGatesAsync(ctx context.Context, session *GameSession, state any) int32 {
// Router implementation for GameRouter_RacingGates_RemoveAllRacingGatesAsync
return 0
}
//
// 读客户端 R247 落下的检查点表。文件不存在 / 解析失败都只是「还没有坐标」,
// 退回信标模式,绝不抛。
//
func GameRouter_RacingGates_LoadRacingGateTable(ctx context.Context, session *GameSession, cfg any) []any {
// Router implementation for GameRouter_RacingGates_LoadRacingGateTable
return nil
}
//
// 「验证信标」:在玩家四周各摆一道门。用途只有一个 ——
// 把「通道对不对」与「坐标对不对」解耦。看得见 ⇒ 通道没问题,只差那张坐标表。
//
func GameRouter_RacingGates_BuildRacingGateBeacons(ctx context.Context, state any, cfg any) []any {
// Router implementation for GameRouter_RacingGates_BuildRacingGateBeacons
return nil
}
// 一条待摆放的门。
type RacingGatePlacement struct {
PathId int32 `json:"pathId"`
X float32 `json:"x"`
Y float32 `json:"y"`
Z float32 `json:"z"`
Yaw float32 `json:"yaw"`
Label string `json:"label"`
}