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

292 lines
16 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
//
// 车库 / 停车(House Garage)—— 2026-09-30。
// <para>用户报的症状(3 条):① 靠近车位按 F「调取载具」能开选车面板(纯客户端 UI),
// 选完车后按 F「驶离车库」毫无后续;② 车的模型不出现在车位;③ 车库大门不开。</para>
// <para>根因(客户端 GarageManager.lua 逐行读出来的):</para>
// <list type="number">
// <item>⛔⛔ 2026-09-30 追加:真正的第一道墙在客户端配置里。
// HouseConfig[11200003].GarageIndoorId 官方写成了 301(= 绿屿公馆主体),
// 真实车库 indoor 是 302(IndoorConfig:301 绿屿公馆 / 302 绿屿公馆车库)。
// ChangeIndoor 建的是 garageIndoorIdList[GarageIndoorId] = houseId,
// 而 LoadingFinish 用当前 gMapManager.IndoorId 查它 ⇒ 站在车库里
// (实测客户端只报 332 大厅 → 302 车库,从不经过 301)永远查不到 ⇒
// CurrentHouseId 恒为 0 ⇒ SwitchCarprotVehicle 第一行
// parkinginfolist[0] 为空直接 FatalLog("换车失败,该房屋未购买或数据异常, houseId=0")
// + return ⇒ 连 AskHouseMoveParkingSpace 都发不出去(车不出、也没有「驶离车库」)。
// 5 套房里另外 4 套的 GarageIndoorId 都等于各自车库 indoor(201/300/299/379),只有绿屿错。
// 服务端改不了(CurrentHouseId / garageIndoorIdList / parkinginfolist
// 全是纯客户端状态,没有任何 RPC 能写;HouseConfig 是客户端自带表,服务端那份不下发)
// ⇒ 走客户端 fastpatch tools/garage_indoor_fix.lua(rawset(getmetatable(row),...),
// 非 BepInEx)。本文件的所有 handler 只有在那一行修好之后才可能被调用。</item>
// <item>这一族 9 条 RPC 在服务端一直是 DefaultReturnCatalog4229938 的
// shape="void" 空 body 兜底 —— 客户端发出去的请求永远等不到有意义的回应。</item>
// <item>两个关键 S2C 服务端从来不发:
// SyncHouseParking(64277819)(车位→车的清单,触发进库演出 + 车辆生成)
// 和 SyncHouseParkingEntityBinding(64060781)(每辆车的 EntityId)。</item>
// <item>LeaveGarage(行 156) 第一步就读 currentParkingInfo.ParkingVehicleUid[parkingId],
// 而这个表只能由 SyncHouseParkingEntityBinding 填 ⇒ 为空 ⇒
// 行 164 FatalLog("离开车库","ParkingVehicleUid为空") + 行 165 return ⇒
// 连 AskTeleportFromHouseGarage 都没发出去 —— 这就是「按 F 没后续」。</item>
// </list>
// <para>本文件做的事:把 9 条 RPC 全部接起来,按客户端 GarageManager 的期望回包:</para>
// <list type="bullet">
// <item>AskHouseParking / AskHouseMoveParkingSpace / GmHouseParking:
// 记账(Progress.GarageParking)+ 落盘 + 回推 SyncHouseParking(addInfoList)。
// ⛔ AskHouseMoveParkingSpace(换车 / 调取载具)还要**自己 spawn 车 + 推
// SyncHouseParkingEntityBinding** —— 客户端在这条链路上 IsSwitchingCar=true,
// SyncHouseParking 的处理会跳过进库演出,因而永远不会自己发
// AskCreateHouseParkingVehicles(详见 EnsureParkingVehiclesAsync)。
// 而 AskHouseParking(开自己的车进库)不推 SyncHouseParking:客户端回调里自己会发
// AskCreateHouseParkingVehicles(GarageManager.lua:124),再推一次会把进库演出播两遍。</item>
// <item>AskCreateHouseParkingVehicles:命门 —— 为该房每个已指派车位
// SpawnDirectAsync 出一辆车(CreateSourceType = 10 HouseParking、
// destroyPrevious:false 以便多车位共存),再回推
// SyncHouseParkingEntityBinding(填 ParkingVehicleUid)。</item>
// <item>AskDestroyHouseParkingVehicles:出库销毁本房车辆。</item>
// <item>AskTeleportToHouseGarage:只回 OK(进库演出由客户端自己播)。</item>
// <item>AskTeleportFromHouseGarage:开门(复用 PushDoorOpen + 缓存的门 id)+ 回 OK。</item>
// <item>AskHouseCancelParking / GmHouseCancelParking:取消指派 + 回推 SyncHouseParking(cancelInfoList)。</item>
// </list>
// <para>车辆落点:HouseConfig.ParkingSpaceWaypoints 是 Spoon 航点 id
// (如 1893051),服务端没有 Spoon 位置表(E:\ConfigDump 里查不到),
// 所以无法把车精确摆到车位格。退而求其次:落在玩家当前位置(进库演出刚把玩家放到车位,
// 或玩家就站在车库里),前向偏移由 gameplay.houseGarage.spawnForwardOffset 热调。
// 车一旦生成,客户端就能按 F 上车 / 驶离。</para>
// <para>回滚:gameplay.houseGarage.enabled=false(全部回空 body 兜底),
// 或把 9 条 mid 从 Enabled4229938MethodIds 删掉。两者都与改动前逐字节一致。</para>
//
// 记下最近一次车库事务的 houseId(出库传送要用来查落点)。
func GameRouter_Garage_RememberGarageHouse(ctx context.Context, session *GameSession, houseId uint32) any {
// Router implementation for GameRouter_Garage_RememberGarageHouse
var zero any; return zero
}
// 本会话最近一次车库事务的 houseId;没有则 0。
func GameRouter_Garage_LastGarageHouseId(ctx context.Context, session *GameSession) uint32 {
// Router implementation for GameRouter_Garage_LastGarageHouseId
return 0
}
//
// 玩家此刻正在开的车(entityId)。出库销毁必须跳过它 ——
// 客户端一坐进车位上的车就会走「驶离车库」流程、发 AskDestroyHouseParkingVehicles,
// 无差别销毁会把玩家正坐着的车一起拆掉(2026-09-30 实测:23:38:20 上车,
// 同秒 SyncDestroyVehicle,模型当场消失、角色保持开车姿势悬空,4 秒后
// LeaveGarage 再也找不到车辆实例 ⇒ 出库演出卡死 ⇒ 黑屏)。
// <para>三个信号取并集,都在「当前」语义上:
// CurrentVehicleId(AskPlayerStartEnterOrExitVehicle 写)、
// StoryEnterVehicleId(剧情通道 S011PlayerNetEnterVehicleRequest 写,实测走的是这条)、
// Vehicles[id].ControllerPid == 本玩家 pid。
// 不用 LastDrivenVehicleId:它下车不清,会把早先停好的车误判成「正开着」。</para>
//
func GameRouter_Garage_ResolveDrivenVehicleIds(ctx context.Context, session *GameSession) []any {
// Router implementation for GameRouter_Garage_ResolveDrivenVehicleIds
return nil
}
func GameRouter_Garage_GetGarageRuntime(ctx context.Context, session *GameSession) any {
// Router implementation for GameRouter_Garage_GetGarageRuntime
var zero any; return zero
}
// =========================================================================================
// 1) AskHouseParking(63154177) —— 「开着自己的车进车库」
// =========================================================================================
func GameRouter_Garage_OnAskHouseParking(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_Garage_OnAskHouseParking
var zero any; return zero
}
// =========================================================================================
// 2) AskHouseMoveParkingSpace(63780060) —— 「调取载具 / 换车」(用户场景的主链路)
// =========================================================================================
func GameRouter_Garage_OnAskHouseMoveParkingSpace(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_Garage_OnAskHouseMoveParkingSpace
var zero any; return zero
}
// =========================================================================================
// 3) AskHouseCancelParking(63495236) / GmHouseCancelParking(65601170) —— 按车辆 id 取消
// =========================================================================================
func GameRouter_Garage_OnAskHouseCancelParking(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_Garage_OnAskHouseCancelParking
var zero any; return zero
}
func GameRouter_Garage_OnGmHouseCancelParking(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_Garage_OnGmHouseCancelParking
var zero any; return zero
}
// =========================================================================================
// 4) GmHouseParking(65027125) —— GM:把 houseIdList[i] 的车 vehicleIdList[i] 停进去
// =========================================================================================
func GameRouter_Garage_OnGmHouseParking(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_Garage_OnGmHouseParking
var zero any; return zero
}
// =========================================================================================
// 5) AskCreateHouseParkingVehicles(63479109) —— 命门:spawn 车 + 回填 EntityId
// =========================================================================================
func GameRouter_Garage_OnAskCreateHouseParkingVehicles(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_Garage_OnAskCreateHouseParkingVehicles
var zero any; return zero
}
// =========================================================================================
// 6) AskDestroyHouseParkingVehicles(63266373) —— 出库销毁本房车辆
// =========================================================================================
func GameRouter_Garage_OnAskDestroyHouseParkingVehicles(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_Garage_OnAskDestroyHouseParkingVehicles
var zero any; return zero
}
// =========================================================================================
// 7) AskTeleportToHouseGarage(63099218) —— 进库(演出客户端自己播,回 OK 即可)
// =========================================================================================
func GameRouter_Garage_OnAskTeleportToHouseGarage(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_Garage_OnAskTeleportToHouseGarage
var zero any; return zero
}
func GameRouter_Garage_OnAskTeleportFromHouseGarage(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_Garage_OnAskTeleportFromHouseGarage
var zero any; return zero
}
//
// 出库落点推导,优先级:配置 exitLandingOverrides &gt;
// HouseConfig.VisitorExit(绝对坐标)&gt; 该房 MapentranceConfig 那一行 &gt;
// 玩家当前上报位置(原地,宁可不动也不能黑屏)。
// <para>为什么不能用客户端自己那份:出库落点客户端取的是
// HouseConfig.GarageOutWaypoint(Spoon 航点 id,服务端没有 Spoon 位置表),
// 而且 ExitTimeline 只有 1 个元素的房(素光小筑)连 Spoon 反查都不做 ⇒
// afterPosition 全零。</para>
//
func GameRouter_Garage_ResolveGarageExitLanding(ctx context.Context, houseId uint32, log any) any {
// Router implementation for GameRouter_Garage_ResolveGarageExitLanding
var zero any; return zero
}
// =========================================================================================
// helpers
// =========================================================================================
//
// 确保「本房这些车位」上各有一辆活着的车,并返回可直接推给客户端的
// SyncHouseParkingEntityBinding 列表。
// <para>复用顺序:① 同一辆车换车位(PreviousSpace &gt;= 0)时把运行时里旧车位的实体搬过来,
// 不重新生成;② 目标车位已有活着的实体直接复用;③ 都没有才 SpawnDirectAsync。</para>
// <para>两条链路都用它:AskCreateHouseParkingVehicles(进库演出结束后客户端主动要)
// 和 AskHouseMoveParkingSpace(换车 / 调取载具 —— 那条链路上客户端因为
// IsSwitchingCar 不会自己发前者,所以服务端必须自己生成)。</para>
//
func GameRouter_Garage_EnsureParkingVehiclesAsync(ctx context.Context, conn any, houseId uint32, wanted []any) []any {
// Router implementation for GameRouter_Garage_EnsureParkingVehiclesAsync
return nil
}
func GameRouter_Garage_TryReadGarageArgs(ctx context.Context, conn any, msg any, args any) bool {
// Router implementation for GameRouter_Garage_TryReadGarageArgs
return true
}
// 把 放进该房第一个空车位,返回车位号(0 基);无空位返回 -1。
// 该车已在本房某个车位时返回原位置(幂等)。调用方必须持有 world.SyncRoot。
func GameRouter_Garage_AssignFreeSpace(ctx context.Context, world any, houseId uint32, vehicleId uint32) int32 {
// Router implementation for GameRouter_Garage_AssignFreeSpace
return 0
}
// 按车辆 id 取消车位指派(跨所有房),落盘并回推 cancelInfoList。
func GameRouter_Garage_CancelVehicles(ctx context.Context, conn any, vehicleIds []any) []any {
// Router implementation for GameRouter_Garage_CancelVehicles
return nil
}
func GameRouter_Garage_PushSyncHouseParkingAsync(ctx context.Context, session *GameSession, cancel []any, add []any, log any) any {
// Router implementation for GameRouter_Garage_PushSyncHouseParkingAsync
var zero any; return zero
}
func GameRouter_Garage_PushSyncHouseParkingEntityBindingAsync(ctx context.Context, session *GameSession, binding []any, log any) any {
// Router implementation for GameRouter_Garage_PushSyncHouseParkingEntityBindingAsync
var zero any; return zero
}
// 车辆落点:玩家当前位置(+ 配置的前向偏移)。
func GameRouter_Garage_GarageSpawnPoint(ctx context.Context, state any, forwardOffset float32) Vec3 {
// Router implementation for GameRouter_Garage_GarageSpawnPoint
var zero Vec3; return zero
}
func GameRouter_Garage_IsVehicleAlive(ctx context.Context, session *GameSession, entityId uint64) bool {
// Router implementation for GameRouter_Garage_IsVehicleAlive
return true
}
//
// 本玩家正开着的、VehicleRecord.ConfigId == vehicleConfigId 的那个实体;没有则 0。
// <para>用途:玩家把车库里的车开出去、又开回来入库时复用同一个实体
// (EnsureParkingVehiclesAsync 的步骤 ②b)。出库时刻意不销毁玩家正开的车,
// 所以它仍在世界里活着;不复用就会给同一辆车生成第二份实体。</para>
// <para>锁序:调用方持有 GarageRuntimeState.SyncRoot 时本方法会再取
// VehicleRuntimeState.SyncRoot ⇒ 顺序恒为 Garage → Vehicle(与
// OnAskDestroyHouseParkingVehicles 一致),不会反向嵌套。</para>
//
func GameRouter_Garage_FindDrivenVehicleEntity(ctx context.Context, session *GameSession, vehicleConfigId uint32) uint64 {
// Router implementation for GameRouter_Garage_FindDrivenVehicleEntity
return 0
}
// 本会话运行时的车库实体表:houseId → (车位号(0 基) → 车辆 entityId)。
type GarageRuntimeState struct {
SyncRoot any `json:"syncRoot"`
EntitiesByHouse map[uint32]map[int32]uint64 `json:"entitiesByHouse"`
//
// 最近一次车库 RPC 里出现的 houseId。给「出库传送」用:AskTeleport(config=7)
// 的 PreTeleportOption 里没有 houseId(客户端只填了 timeline 资源名),
// 而它前面必定先来过一条带 houseId 的车库 RPC(AskDestroyHouseParkingVehicles 4B /
// AskCreateHouseParkingVehicles 4B / AskHouseMoveParkingSpace 的元素里),
// 所以在这里记一份。
//
LastHouseId uint32 `json:"lastHouseId"`
}
// =========================================================================================
// 8) AskTeleportFromHouseGarage(63203581) —— 出库:开门 + **推 SyncTeleport 落点** + 回 OK
// =========================================================================================
//
// 一趟出库事务待落的点(写于 OnAskTeleportFromHouseGarage,
// 消费于 ReportPostTeleportFinish 的硬设兜底 —— 与景点电梯四修同一个套路)。
//
type PendingGarageLanding struct {
HouseId uint32 `json:"houseId"`
X float32 `json:"x"`
Y float32 `json:"y"`
Z float32 `json:"z"`
Facing float32 `json:"facing"`
VehicleId uint64 `json:"vehicleId"`
}
// 出库落点 + 该填进 SyncTeleport.MapEntranceId 的入口 id。
type GarageExitLanding struct {
X float32 `json:"x"`
Y float32 `json:"y"`
Z float32 `json:"z"`
Facing float32 `json:"facing"`
EntranceId uint32 `json:"entranceId"`
Source string `json:"source"`
}