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_taxi.go
T

232 lines
12 KiB
Go
Raw 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
//
// 出租车(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"`
}