package routers import ( "context" "sync" ) var _ = sync.Mutex{} var _ = context.Background // // 改装店(Smokey Drift / ApplyCar)的购买链与外观回声 —— // 2026-09-26 自上游 donor repair-shop.zip 移植(唯一参考源,没有混其它捐赠端)。 // 第一步(进场闭环)已经在 GameRouter.Teleport.cs / GameRouter.Vehicle.cs 落地; // 这一步补的是「进店之后能干什么」:切换展示车、买件、买套件、清车位。 // ⛑⛑ 三条 donor 用实机日志证明、不看代码绝对猜不出来的结论: // // 纯数值件(引擎/刹车数值/传动/车身数值)不能进刷车包。它们在 // VehiclePartConfig 里没有 PartPath、只有 PartPerfID,混进 Parts 会让真车装配把槽位冲空 // ⇒ 全黑透明不可驾驶(81005006 + engine 9995002 复现)。但它们是唯一的数值通道 // (客户端本地不应用),所以 owned-fleet 列表里必须带、刷车包默认不带,用 // gameplay.repairShop.sendStatParts 做 A/B。 // Parts 里必须有车轮。只发买过的件 ⇒ 客户端 NPDVehicleSwitch 报 // 「未配置车轮数据」,车退化成 SwitchToNonPhysicalDriveModular(Burst data not init,透明不可开)。 // 所以服务端要按套件把 Suit[] + WheelModelA[] + WheelModelB[] + SuitPaint 全展开成底座, // 再用买过的件按 PartTag 顶替。 // 钱和库存一律不模拟。客户端在发请求前已经拿我们推下去的商品表自己校验过了, // 服务端只要 ack ok;扣钱是另一条链(本次不做)。 // // // ---------------------------------------------------------------------------------- // 外观展开 // ---------------------------------------------------------------------------------- // // 一辆车此刻的完整外观: = 套件底座 id, = // 展开后的零件表。口径与客户端 ExpandForClient 一致(按 PartTag 顶替,没有就追加, // ConfigId=0 表示移除)。 // 顺序:先按套件展开底座(Suit[] + WheelModelA[] + WheelModelB[] + SuitPaint), // 再用买过的件按 PartTag 顶替。套件本身(type 18)由 SuitId 承载,不进 Parts。 // func GameRouter_VehicleTuning_BuildAppearance(ctx context.Context, world any, configId uint32, parts []any, suit uint32, includeStatParts bool) any { // Router implementation for GameRouter_VehicleTuning_BuildAppearance var zero any; return zero } // // 购买后重推 SyncAllUnlockedVehicles(64871623):改装面板靠这份列表判断 // 「哪些类目已解锁」,不重推的话第二次购买就点不动了(donor 2026-09-23 21:19)。 // 与 PublishVehicleOwnership 的区别:那个有「只推一次」的闸门,这个是每次都推。 // owned-fleet 里带纯数值件(见类注释第 1 条)。 // func GameRouter_VehicleTuning_PushTunedFleetAsync(ctx context.Context, session *GameSession, reason string) any { // Router implementation for GameRouter_VehicleTuning_PushTunedFleetAsync var zero any; return zero } // ---------------------------------------------------------------------------------- // 购买链 // ---------------------------------------------------------------------------------- // 63699253 — 改装面板开面板时要的完整商品表(返回体,不是 notify)。 func GameRouter_VehicleTuning_AskNpcShopCommodityInfo(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_VehicleTuning_AskNpcShopCommodityInfo var zero any; return zero } // // 63497070 — 店里换展示车。返回新车实体的 uid(0 = 失败),客户端随后 possession 它。 // 落点 = 该店的 Interior(地下工位),不是「玩家旁边」:donor 踩过「spawn 到 // 玩家右手边 5.5m」导致车卡在墙里。返回之后补一发 SyncTeleportVehicle 把车钉在工位上 // ——客户端自己的 parking-waypoint 查询在没有 spoon graph 时返回 0,会把它摆在玩家看的地方。 // func GameRouter_VehicleTuning_AskVehicleShopSpawnVehicle(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_VehicleTuning_AskVehicleShopSpawnVehicle var zero any; return zero } // 63613647 — 客户端把停在那的预览车收掉了。只记日志(车是客户端本地预览)。 func GameRouter_VehicleTuning_AskVehicleShopDestroyParking(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_VehicleTuning_AskVehicleShopDestroyParking var zero any; return zero } // 63970007 — 改装面板单件购买。不扣钱不查库存,直接 ok。 func GameRouter_VehicleTuning_AskBuyCommodity(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_VehicleTuning_AskBuyCommodity var zero any; return zero } // // 63701165 — 改装购买(NewCarStoreMgr 的「应用」)。{shopid, vehicleid, // commodityiddict{商品id:数量}, removedparttypes[]}。 // 这是唯一真正改变服务端状态的一步:把买到的件按车记进 WorldEntryState, // BuildAppearance 会在后续刷车 / owned-fleet 里把它们回声出去。 // func GameRouter_VehicleTuning_AskBuyModifyParts(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_VehicleTuning_AskBuyModifyParts var zero any; return zero } // // 63614427 — 4S / 商店整单购买。Smokey Drift 不走这条(它用 AskBuyModifyParts), // 但客户端会发,不注册就会挂在 await 上,所以照样 ack ok 并记日志。 // func GameRouter_VehicleTuning_AskBuyCommodities(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_VehicleTuning_AskBuyCommodities var zero any; return zero }