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

231 lines
12 KiB
Go
Raw Permalink Normal View History

package routers
import (
"context"
"sync"
)
var _ = sync.Mutex{}
var _ = context.Background
//
// 出租车(2026-09-30 新增)。逐字读过 v0.5-2 捐赠端
// GameRouter.Taxi.cs(357 行) + GameRouter.TaxiNative.cs(210 行) 后,
// 按主线自己的车辆栈重写 —— 不是照抄。
// <para>★ 从捐赠端确认的四条硬事实(都写进了注释,别推翻):</para>
// <list type="number">
// <item>目的地是配置 uint,不是车辆 uid:TaxiSystemManager 传的是
// TaxiNavigationConfig.json 的 Id(共 18 条),所以必须查表,
// 而且要校验 RaidId == 当前场景(表里混着 23300888 与 23300999 两个 raid)。</item>
// <item>只有 VehicleType == 8 的车能开出租车 UI。主线 VehicleConfig.json 里
// 正好 4 辆:81004001 / 81004004 / 81004015 / 81004038(都是 4 座)。</item>
// <item>请求是裸定长字节:Start=8B(u32 目的地 + u32 UI 距离)、End=6B(u8 传送 + u8 + u32)、
// Accelerate=0B、Status=11B(u64 + u8 + i16 停靠原因)。
// ⛑ UI 那个「距离」不信任,车费只按服务端自己的里程算。</item>
// <item>计费:Ceiling(起步价 + Max(0, 里程 - 免费里程) * 单价),
// 捐赠端原值 300 / 200m / 0.4 每米。</item>
// </list>
// <para>★ 与捐赠端的故意差异(照抄会踩坑):</para>
// <list type="bullet">
// <item>不自己拼 Cruise 包:主线 GameRouter.VehicleNav.AssignAutonomousCruiseAsync
// 发的就是同一个 AssignVehicleControl&lt;VehicleCruiseParameters&gt;,而且它还负责
// ① 抢主控权 ② 记住 token ③ 主动 Acknowledge 一次(不确认车就不动,2026-09-28 第三轮的坑)。
// ⇒ 直接复用,只多传一个速度覆盖参数。</item>
// <item>里程不自己挂钩:主线 GameRouter.UnhandledBatch.cs:41 已经在累加
// VehicleRecord.DistanceDriven。捐赠端的 RecordTaxiMovement 不需要搬。</item>
// <item>路线用 SolveRoute(车道图 Dijkstra),不是捐赠端的
// FindNavigation —— 主线本来就有,且返回 DrivePoints(车→终点正向)。</item>
// <item>不做「长按跳过 → 传送」(阶段一)。那要接主线 GameRouter.Teleport.cs
// 的 ReportPreTeleportFinish 链,耦合深;AskEndTaxiTeleport 暂留在
// GameRouter.MiscAcks.cs 当占位。</item>
// </list>
// <para>⛑ 回滚:gameplay.taxi.enabled=false ⇒ 四个 handler 全部直接回失败码
// (注册与日志仍在,可观测)。要彻底回到占位 AckVoid,把
// GameRouter.MiscAcks.cs 里那两条注释掉的 AckVoid 补回来并删掉本文件。</para>
//
//
// 能不能开出租车。判据逐条来自捐赠端 CanTakeTaxi4229938:
// 人在车上的乘客座(Seat &gt; 0,主驾是 0)、不在下车流程中、
// 车可交互且已注册 Aether、车型是出租车。
// <para>⛑⛑ 最后一条(VehicleSpawnCatalog4229938.IsTrafficVehicle)是
// 主线自己加的,捐赠端没有,但不加就一定踩坑(2026-09-30 实锤):
// 客户端 AetherVehicleManager.AddVehicleDataEntity 按 cfgId 在
// VehicleSpawnConfig 里查车辆模板,查不到就整台丢掉 ——
// Player.log 三连:VehicleInit_Drop_NoConfig: vid=… cfgId=81004001 not found in VehicleSpawn
// → Attach failed: VDE is null → Flush failed to create VDE。
// 后果是 没有 VehicleDataEntity ⇒ 车不会动、上下车没有动画
// (同一次实测:[TAXI] settled meters=0.0,车一米没走)。
// 4 辆 VehicleType == 8 里 81004001 不在那张表,只有
// 81004004 / 81004015 / 81004038 能用。</para>
//
func GameRouter_Taxi_CanTakeTaxi(ctx context.Context, runtime any, v any) bool {
// Router implementation for GameRouter_Taxi_CanTakeTaxi
return true
}
//
// 客户端认得的出租车车型(VehicleType == 8 且在 VehicleSpawnConfig 里)。
// 只用于日志/报错提示,让玩家知道该召哪一辆。
//
func GameRouter_Taxi_UsableTaxiConfigIds(ctx context.Context, ) string {
// Router implementation for GameRouter_Taxi_UsableTaxiConfigIds
return ""
}
// 车费。沿用捐赠端 TaxiFare4229938:Ceiling(Base + Max(0, d-Free) * PerMeter)。
func GameRouter_Taxi_TaxiFare(ctx context.Context, meters float64) float64 {
// Router implementation for GameRouter_Taxi_TaxiFare
return 0
}
func GameRouter_Taxi_TaxiSpeedMs(ctx context.Context, accelerated bool) float32 {
// Router implementation for GameRouter_Taxi_TaxiSpeedMs
return 0
}
// ------------------------------------------------------------------ 派单 / 收车
//
// ⛑⛑ 最关键的一步(2026-09-30 第二轮补上,之前漏了 ⇒ 所有出租车原地不动):
// 玩家在出租车 UI 里确认目的地时,客户端走
// TaxiSystemManager.StartTaxiOrder → SetTaxiVehicleControl(true) → City.SetAetherVehicleControl
// 把车切成 AetherVehicle=1;而 Cruise 与 SyncTeleportVehicle 都要求
// RaidVehicle=2 —— 捐赠端 GameRouter.TaxiNative.cs:33-35 的原话:
// Cruise and SyncTeleportVehicle need RaidVehicle=2; a controller PID alone is insufficient.
// 只发 SyncChangeVehicleController 不够:车仍归 Aether 车道模拟管,
// 位移每帧被车道覆盖 ⇒ 表现就是「轮胎在转,车在原地进行」。
// 反向验证:81004001 之前反而能开到目的地 —— 因为它不在客户端
// VehicleSpawnConfig 里、压根没建成 VDE,客户端切不动 Aether,
// 车一直留在 Raid 控制下 ⇒ Cruise 生效(代价是没有 VDE ⇒ 没有上下车动画)。
// 两台车一个能跑没动画、一个有动画不能跑,正好各缺一半,两边证据互相印证。
//
func GameRouter_Taxi_SetTaxiRaidControlAsync(ctx context.Context, session *GameSession, vehicleEntityId uint64) any {
// Router implementation for GameRouter_Taxi_SetTaxiRaidControlAsync
var zero any; return zero
}
//
// 派一次 Cruise。⛑ 顺序与 GameRouter.VehicleNav.cs 的自动驾驶一致:
// 状态包 → AI 任务(2026-09-30 §43.1 的血泪教训:顺序反了就是「车门消失/角色缺模」)。
// 派单前还要先 SetTaxiRaidControlAsync 把车从 Aether 切回 Raid。
//
func GameRouter_Taxi_DispatchTaxiAsync(ctx context.Context, session *GameSession, conn any, runtime any, trip any, path []any) any {
// Router implementation for GameRouter_Taxi_DispatchTaxiAsync
var zero any; return zero
}
func GameRouter_Taxi_StopTaxiTaskAsync(ctx context.Context, session *GameSession, runtime any, trip any) any {
// Router implementation for GameRouter_Taxi_StopTaxiTaskAsync
var zero any; return zero
}
// 取这台车的停靠原因位(0 = 没有停靠原因,可以开车)。
func GameRouter_Taxi_TaxiStopReasons(ctx context.Context, runtime any, vehicleId uint64) uint16 {
// Router implementation for GameRouter_Taxi_TaxiStopReasons
return 0
}
//
// PushVehicleAutonomousStateAsync 需要 Connection,而收车路径常常只有
// TcpSession。这里用 session 直发同一条包(IGameSceneToClient_SyncVehicleAutonomousDrivingState)。
//
func GameRouter_Taxi_PushVehicleAutonomousStateAsync2(ctx context.Context, session *GameSession, vehicleEntityId uint64, isStart bool) any {
// Router implementation for GameRouter_Taxi_PushVehicleAutonomousStateAsync2
var zero any; return zero
}
// 到达:停任务 → 推 SyncTaxiReachDestination → 结算。
func GameRouter_Taxi_TaxiArrivedAsync(ctx context.Context, session *GameSession, conn any, runtime any, trip any) any {
// Router implementation for GameRouter_Taxi_TaxiArrivedAsync
var zero any; return zero
}
//
// 扣车费。⛑ FareSettled 必须先置位再动钱包:
// 到达/手动结束/异常三条路径可能并发,重复扣钱比少扣钱难查。
//
func GameRouter_Taxi_SettleTaxiAsync(ctx context.Context, conn any, session *GameSession, runtime any, trip any, reason string) any {
// Router implementation for GameRouter_Taxi_SettleTaxiAsync
var zero any; return zero
}
// 结束行程(手动/异常/到达都走这里)。
func GameRouter_Taxi_FinishTaxiAsync(ctx context.Context, session *GameSession, conn any, runtime any, trip any, reason string) any {
// Router implementation for GameRouter_Taxi_FinishTaxiAsync
var zero any; return zero
}
// ========================================================================================
// AskStartTaxiNavigate(67363555) —— C2S Invoke,8B:u32 目的地 + u32 UI 距离(不信任)
// ========================================================================================
func GameRouter_Taxi_StartTaxi4229938(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_Taxi_StartTaxi4229938
var zero any; return zero
}
// ========================================================================================
// AskEndTaxiNavigate(67960304) —— C2S Invoke,6B:u8 传送 + u8 + u32
// ========================================================================================
func GameRouter_Taxi_EndTaxi4229938(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_Taxi_EndTaxi4229938
var zero any; return zero
}
// ========================================================================================
// AskTaxiAccelerate(67753061) —— C2S Invoke,0B
// ========================================================================================
func GameRouter_Taxi_AccelerateTaxi4229938(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_Taxi_AccelerateTaxi4229938
var zero any; return zero
}
func GameRouter_Taxi_TaxiVehicleStatus4229938(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_Taxi_TaxiVehicleStatus4229938
var zero any; return zero
}
// ------------------------------------------------------------------ 监控
//
// 每秒看一次:里程、是否到达、是否卡住。
// ⛑ 到不了就结束行程而不是无限等 —— 捐赠端是「保留行程让玩家换目的地」,
// 但那要配合跳过传送;阶段一没有跳过,卡住就按当前里程结算收工,免得钱一直挂着。
//
func GameRouter_Taxi_WatchTaxiAsync(ctx context.Context, conn any, runtime any, trip any) any {
// Router implementation for GameRouter_Taxi_WatchTaxiAsync
var zero any; return zero
}
// ------------------------------------------------------------------ 行程
type TaxiTrip4229938 struct {
VehicleId uint64 `json:"vehicleId"`
DestinationId uint32 `json:"destinationId"`
Raid uint32 `json:"raid"`
TargetX float32 `json:"targetX"`
TargetY float32 `json:"targetY"`
TargetZ float32 `json:"targetZ"`
// 起点里程(VehicleRecord.DistanceDriven)。车费 = 当前里程 - 它。
StartOdometer float64 `json:"startOdometer"`
// 上次观察到的里程,用来判断「还在不在动」。
LastOdometer float64 `json:"lastOdometer"`
Accelerated bool `json:"accelerated"`
Arrived bool `json:"arrived"`
Ended bool `json:"ended"`
FareSettled bool `json:"fareSettled"`
StallNotified bool `json:"stallNotified"`
//
// 当前是否已经下发过 Cruise 任务。对应捐赠端的 trip.Token != 0
// (主线不自己管 token,由 AssignAutonomousCruiseAsync 内部 mint)。
// ⛑ 捐赠端靠它决定「什么时候该重派」:Token == 0(从没派过/刚被停)
// 或收到 Resume(TaxiInteract=64) 才派。
//
Dispatched bool `json:"dispatched"`
StartedTick int64 `json:"startedTick"`
LastMovementTick int64 `json:"lastMovementTick"`
}
// ========================================================================================
// AskAetherAISetVehicleStatus(67541089) —— C2S **Notify**,11B:u64 uid + u8 status + i16 停靠原因
// ========================================================================================
type TaxiStopState struct {
Reasons uint16 `json:"reasons"`
}