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

146 lines
8.8 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
//
// 房产自带家具的「家具机关(furniture gadget)」—— 2026-10-01。
// <para>用户报的症状:「沙发椅子已经生成了的没法交互」——
// 家具模型都在(AskLoadHouseData 的 FloorBuildInfoDict 已上线),
// 但椅子坐不了、衣柜开不了、电视点不亮。</para>
// <para>根因(一句话):客户端把家具建出来之后会再问一次服务端要机关实例 id ——
// HouseGadgetManager.lua:CreateGadgetsForFurnitures →
// AskCreateHouseFurnitureGadgets(63581446),读应答里的
// mapping.placedID2GadgetInstanceID,非 0 才写进 placedId2GadgetIdMap
// 并 gFurnitureUtils:TrySetGadgetInstanceId(furnitureGo, id)。
// 而这条 RPC 在服务端一直是 DefaultReturnCatalog4229938.cs:624 的
// body=[0xFF,0x01] shape="struct(631:dict7-empty)" —— 恒回空映射
// ⇒ placedId2GadgetIdMap 恒空 ⇒ 所有机关失效。日志铁证(console-latest.log:319-321):
// <code>
// -> rpc AskCreateHouseFurnitureGadgets [0x03CA2D06] 274b
// ! [RPC4229938] unimplemented AskCreateHouseFurnitureGadgets -> typed-default struct(631:dict7-empty) 2b
// &lt;- ret ... e=0 2b
// </code></para>
// <para>★ 为什么说「家具树本身是对的,只缺这一步」:客户端请求的 id 个数
// 逐套精确等于该房模板树里「HouseFurnitureConfig.PathId != 0」的家具数 ——
// 绿屿 33/33、旧物仓 3/3、观澜 6/6、素光 1/1(unknown-methods.log 请求体逐字节解码后与模板树对齐)。
// 而且请求里的 id 正好是 PlacedInstanceId = houseId*100000 + nodeId 的 32 位截断形态
// (绿屿首件 0xC5388A93 - 0xC5381F20 = 14003 = 模板节点号)。</para>
// <para>★ 机关 prefab 从哪来:HouseFurnitureConfig.PathId
// 就是 GadgetCatalog.json 的 pathId(逐条核对过,见
// HouseFurnitureGadgetCatalog4229938 的注释表)。位置也不用反推 ——
// HouseFurnitureTemplateConfig 节点的 Position 就是世界绝对坐标
// (绿屿椅 (2290.629, 160.720, 1397.878)、素光衣柜 (1096.527, 13.040, 2375.721)),
// Rotation.Y 就是 yaw。</para>
// <para>本文件做的事:</para>
// <list type="bullet">
// <item>AskCreateHouseFurnitureGadgets:对请求里每个 placedId
// 反解 nodeId → FurnitureId → PathId,回 placedId → gadgetInstanceId 映射;
// spawnGadgetEntities=true(默认)时同时用 SyncGadgetAdd(68843189)
// 把机关实体摆到家具节点的世界坐标上(uniqueId = placedId,与返回值同源)。</item>
// <item>AskDestoryHouseFurnitureGadgets:摘掉记录 + 推
// SyncGadgetRemoveList(68199571),回空 body(客户端该 RPC 的应答 reader 是空函数)。</item>
// </list>
// <para>⛔ 两个开关都要留(gameplay.houseFurniture):
// gadgets 是总开关;spawnGadgetEntities 决定要不要真的 SyncGadgetAdd。
// 后者是实测分流用的 —— 有两种可能,客户端行为决定哪一种:
// <list type="number">
// <item>H1(默认,spawn=true):机关实体得由服务端 SyncGadgetAdd 造出来,
// 客户端 OnGadgetSlotCompLoaded 的 gGadgetManager:GetEntitySearchByInstanceId(gadgetId) 才找得到。</item>
// <item>H2(spawn=false):机关 GameObject 本来就在家具 prefab 里
// (RegisterFurnitureLuaSlotReplace 用的就是 houseFurniture.gadgetGameObject),
// 服务端只需发一个唯一的非 0 id,客户端自己 TrySetGadgetInstanceId 就能把它点亮。
// OnGadgetSlotCompLoaded 里那个 10 帧重试计时器更像 H2(id 要等 TrySetGadgetInstanceId 之后才可寻址)。</item>
// </list>
// 上机判据:H1 若把椅子/灯渲染成两遍就是猜错了,把 spawnGadgetEntities 改 false 即可(热重载,不用重编译)。</para>
// <para>回滚:gameplay.houseFurniture.gadgets=false(回到恒空映射兜底),
// 或把两条 mid 从 Enabled4229938MethodIds 删掉。两者都与改动前逐字节一致。</para>
//
//
// 63581446 — HouseGadgetManager.CreateGadgetsForFurnitures 的请求:
// 「这些 placedId 的家具,给我它们的机关实例 id」。
// <para>进房子时每层各发一次(素光小筑两层会发两次,同一节点出现两遍)。
// 客户端离开房子时 DestroyGadgetsForFurnitures 会发 AskDestoryHouseFurnitureGadgets,
// 但场景切换也会清空客户端的表(OnBeforeSwitchScene → ClearAll),
// 所以这里每次请求都重推 SyncGadgetAdd(同一个 instanceId,幂等刷新),
// 否则「出去再进来」家具就再也点不动了。</para>
//
func GameRouter_HouseFurnitureGadget_OnCreateHouseFurnitureGadgets(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_HouseFurnitureGadget_OnCreateHouseFurnitureGadgets
var zero any; return zero
}
//
// 63759452 — HouseGadgetManager.DestroyGadgetsForFurnitures(官方拼写就是 Destory)。
// 离开房子 / 切场景时客户端会发;应答是 void(客户端 reader 是空函数)。
//
func GameRouter_HouseFurnitureGadget_OnDestoryHouseFurnitureGadgets(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_HouseFurnitureGadget_OnDestoryHouseFurnitureGadgets
var zero any; return zero
}
//
// ★★ 2026-10-01:把家具机关实体在每次网格 AOI 推送之后补回来。
// <para>为什么必须补:SyncGadgetGridAOIDecrease 的 addGridInfo 在客户端是
// 「当前 AOI 快照」语义 —— 本仓 GameRouter.GadgetGrid.cs 的注释早就记过这一点
// (「an AOIMove that only carried the newly entered cells made every previously streamed prop
// disappear」)。世界摆件(摊位 / 派对家具 / 健身房器材)被撤掉后,客户端能从自己的
// blob里再铺出来,所以它们没事;而家具机关是纯服务端造的、blob 里没有
// ⇒ 一撤就永久消失。</para>
// <para>实测铁证(E:\anatacbt\log\Player.log,2026-10-01 10:23–10:24):
// <code>
// 10:23:26.554 S2C NOTIFY SyncGadgetAddList (3731b) ← 33 件建出来
// 10:23:34.103 [DRMK-HG] entityHits=33/33 ← 全在
// 10:23:37.496 S2C NOTIFY SyncGadgetGridAOIDecrease (1363b) ← 第一次网格快照
// 10:23:56.345 [DRMK-HG] entityHits=0/33 ← 全没了
// </code>
// 该窗口内唯一的机关类 S2C 就是 SyncGadgetGridAOIDecrease(每 5 秒一次,
// cells=81)。</para>
// <para>补发走同一条通道(SyncGadgetAddList)且 uniqueId = placedId 不变 ——
// 与 OnCreateHouseFurnitureGadgets 里那条「每次请求都重推」是同一个幂等刷新手法,
// 不会多出第二份家具。只补玩家附近(&lt;300 m)那一套房的,跑到别的区就不做无用功。</para>
// <para>关掉:gameplay.houseFurniture.respawnAfterGridPush=false(热重载,不用重编译)。</para>
//
func GameRouter_HouseFurnitureGadget_TickHouseFurnitureGadgetsAsync(ctx context.Context, session *GameSession, state any, px float32, pz float32) any {
// Router implementation for GameRouter_HouseFurnitureGadget_TickHouseFurnitureGadgetsAsync
var zero any; return zero
}
// 只补玩家附近那一套房的机关;玩家跑到别的区就不做无用功。
func GameRouter_HouseFurnitureGadget_IsNearPlayer(ctx context.Context, x float32, z float32, px float32, pz float32) bool {
// Router implementation for GameRouter_HouseFurnitureGadget_IsNearPlayer
return true
}
//
// 把客户端发来的 placedId 反解成模板节点号。
// <para>⛔ 线上是 32 位截断形态:客户端发的是
// (houseId * 100000 + nodeId) &amp; 0xFFFFFFFF,高 4 字节恒为 0
// (实测绿屿首件 93 8A 38 C5 00 00 00 00 = 0xC5388A93,
// 而 0xC5388A93 - (11200003*100000 mod 2^32) = 14003)。
// 因为 houseId ≥ 11200003 ⇒ houseId*100000 ≈ 1.12e12 &gt; 2^32,截断必然发生。</para>
// <para>同时兼容「万一某条链路发的是完整 64 位值」:placedId &gt; 0xFFFFFFFF
// 时直接减 houseId*100000。两条路都算一遍、只认落在
// 1..99999(模板节点号上限实测 14150)的那个。</para>
//
func GameRouter_HouseFurnitureGadget_ResolveNodeIdFromPlacedId(ctx context.Context, placedId uint64, houseId uint32, baseOffset uint64) uint32 {
// Router implementation for GameRouter_HouseFurnitureGadget_ResolveNodeIdFromPlacedId
return 0
}
//
// 家具节点的机关落点。模板 Position 本来就是世界绝对坐标,直接搬;
// 少数家具带 GadgetOffset(实测 6 个 float:pos xyz + rot xyz,全表只有 2 条有值,
// 且都不在四套房的模板树里),有就叠加上去,避免以后换模板踩坑。
//
func GameRouter_HouseFurnitureGadget_ApplyGadgetOffset(ctx context.Context, node any, furnitureId uint32) any {
// Router implementation for GameRouter_HouseFurnitureGadget_ApplyGadgetOffset
var zero any; return zero
}