package routers import ( "context" "sync" ) var _ = sync.Mutex{} var _ = context.Background // // Scene devices — doors, vending machines, fridges, arcades, signs. // Why "the fridge does nothing": an Ask* device INVOKE is only a REQUEST. The client's // device-state manager waits for the server to echo the transition before it runs the // presentation (door animation, fridge open, vending purchase). The donor handoff // (HANDOFF-2026-09-18) walked exactly this path: round 13 added the echo, 13A removed it on a // test that never happened, and 15D restored it from the one session whose log proved it worked // (03:50 — the countdown door ran 9→6 cleanly with check=0 throughout; without the echo the same // door re-fired 23× with the state DECREASING and the checkIndex escalating). Our logs show the // very same signature today: AskSceneDeviceChangeState ×333 and AskSceneDeviceRecordValue ×364, // all of them still on the generic neutral fallback. // Wire (all CSharpOnly: raw fields, ZERO markers): // // C2S AskSceneDeviceChangeState 67226162 (22b) = u8 type | u64 id | i32 stateType | i32 state // | i32 checkIndex | u8 trailing // C2S AskSceneDeviceRecordValue 67703474 (21b) = u8 type | u64 id | i32 valueName // | string value (BIASED len + UTF-8) // | i32 checkIndex | trailing // reply (both) = SceneDeviceCheckIndexCallback { int CheckIndex; uint Error; } = 8 bytes // S2C SyncSceneDeviceStateChange 68165561 (21b) = u8 type | u64 id | i32 stateType | i32 state // | i32 checkIndex // S2C SyncSceneDeviceValueChange 68332775 = u8 type | u64 id | i32 valueName // | string value | i32 checkIndex // // Two traps baked into the code below: // 1. String bias is asymmetric. C2S device strings are BIASED (len+1 — a 9-char // "StartMind" arrives as 0x0A); S2C strings are RAW (ReadStringOrNull @0x189AAB3D0). // 2. Never push SyncSceneDeviceSignalSend(68477555). The client relays a server signal // echo back as its own request — 13A measured 14×/s. That one echo is a real loop. // // AskSceneDeviceChangeState(67226162) / AskSceneDeviceRecordValue(67703474). func GameRouter_SceneDevices_OnAskSceneDeviceChangeState(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_SceneDevices_OnAskSceneDeviceChangeState var zero any; return zero } // AskSceneDeviceRecordValue(67703474) — value records (vending count, sign text). func GameRouter_SceneDevices_OnAskSceneDeviceRecordValue(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_SceneDevices_OnAskSceneDeviceRecordValue var zero any; return zero } // CheckCanOccupySceneDevice(67063877) / ReleaseOccupySceneDevice(67244136): void. func GameRouter_SceneDevices_OnSceneDeviceOccupy(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_SceneDevices_OnSceneDeviceOccupy var zero any; return zero } // // AskCheckSpoonConditionByType(67709054) — "may I run this device script?". // ⛔⛔ **2026-09-28 实机订正(用户验证)—— 下面这段旧说法是错的,已改写,别再改回去:** // 旧文:"The generated neutral default is 0x00 = false, which is precisely what makes an // interaction appear dead: the client asks, we say no, and nothing plays. Donor round 13 // answers 0x01." —— donor 那条从未实测,只是推断;对**衣柜这一族实测结果完全相反**。 // 实测(塔菲公寓衣柜 uid 14013633775178039662): // · 答 0x01(true):F 提示一闪即没、换装打不开,`[SPOON] action` 只发 2 次就停 // (packets-20260928-204201.log 21:03:18.961 S2C hex=01)。 // · 答 0x00(false):F 常驻,22:46:44 `[SPOON] action` 触发,payload 与能换装的对照组 // v1.5 逐字节相同(console-20260928-224215.log)。 // 对照组 v1.5 没有本 handler,走 DefaultReturnCatalog4229938 兜底 0x00 ⇒ 衣柜一直可用。 // ⇒ **true 才是让交互挂死的那个值**:客户端拿到 true 就把交互交给服务端 spoon 脚本流程, // 而 AskDoSpoonServerActionByType 我们只回空 OK、给不出任何脚本输出 ⇒ 卡死、提示被撤。 // 答 false 时客户端走本地表现,脚本照跑。 // 开关:Drmk.Server.Configuration.SceneDeviceSettings.SpoonConditionTrue // (默认已改为 false;配置里 `world.content.sceneDevices.spoonConditionTrue` 显式 false)。 // 另见 `PrivateServerConfig.cs` 该字段的完整实测记录。 // func GameRouter_SceneDevices_OnAskCheckSpoonConditionByType(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_SceneDevices_OnAskCheckSpoonConditionByType var zero any; return zero } // AskGetSpoonServerOutputPortIndexByType(67346473): raw u32, always 0. func GameRouter_SceneDevices_OnAskGetSpoonServerOutputPortIndexByType(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_SceneDevices_OnAskGetSpoonServerOutputPortIndexByType var zero any; return zero } // AskDoSpoonServerActionByType(67863523): void. Logged — the payload carries the port. func GameRouter_SceneDevices_OnAskDoSpoonServerActionByType(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_SceneDevices_OnAskDoSpoonServerActionByType var zero any; return zero } // --- wire helpers ------------------------------------------------------------------------ // SceneDeviceCheckIndexCallback { int CheckIndex; uint Error; } — 8 bytes, Error = 0. func GameRouter_SceneDevices_SceneDeviceOkReply(ctx context.Context, checkIndex int32) []byte { // Router implementation for GameRouter_SceneDevices_SceneDeviceOkReply return nil } func GameRouter_SceneDevices_EchoOff(ctx context.Context, cfg any, id uint64) bool { // Router implementation for GameRouter_SceneDevices_EchoOff return true } // SyncSceneDeviceStateChange(68165561) — 21 raw bytes, no markers. func GameRouter_SceneDevices_PushDeviceStateAsync(ctx context.Context, conn any, typeVal byte, id uint64, stateType int32, state int32, checkIndex int32) any { // Router implementation for GameRouter_SceneDevices_PushDeviceStateAsync var zero any; return zero } // SyncSceneDeviceValueChange(68332775) — S2C strings are RAW-length, not biased. func GameRouter_SceneDevices_PushDeviceValueAsync(ctx context.Context, conn any, typeVal byte, id uint64, valueName int32, value []byte, checkIndex int32) any { // Router implementation for GameRouter_SceneDevices_PushDeviceValueAsync var zero any; return zero } // Raw 7-bit varint (no +1 bias) — the S2C string length encoding. func GameRouter_SceneDevices_WriteRaw7(ctx context.Context, sink []byte, value int32) any { // Router implementation for GameRouter_SceneDevices_WriteRaw7 var zero any; return zero } // -------------------------------------------------------------------------------------- // AskSceneDeviceSendSignal (67144566) — 2026-09-27,桌面清单 T1-44 P0。 // // 为什么排 P0:**它是 `unknown-methods.log` 里出现最多的 INVOKE**(全量 249 次,09-25 以后 74 次), // 而且解出来的 `signalName` 不是设备状态,是**玩法/任务/特效信号** —— 服务端此前一条都收不到: // // | signalName | 次数 | 含义(按名字推断) | // |---|---|---| // | `FinishMind` | 238 | 心智/意志玩法结束(集中在两个固定 gadget uid) | // | `StartMind` | 4 | 同上,开始 | // | `BowlingInviteTimelinePlay` | 2 | 保龄球邀请时间轴播放 | // | `TaskFinish60001155` | 2 | **任务 60001155 完成**(gadget 上触发) | // | `SmokeFX Off` | 2 | 烟雾特效关闭 | // | `carshop` | 1 | 车店 | // // ⛑ `TaskFinish60001155` 值得盯:服务端一直不知道任务什么时候完成(T2-9 任务面板那条线 // 缺的就是"真实达成"的事件钩子),这条是客户端唯一主动上报任务完成的通道之一。 // // 方向:**INVOKE**(`ClientToGameSceneDelegate.lua:899` 是 `self.Invoke(...)`)。 // 返回:`UXRPCTask`(void,`RPCDeserializeAuto.lua:14968` 的 reader 函数体为空)⇒ **空 OK 即正解**。 // // Wire(与 `AskSceneDeviceRecordValue` 同族,**raw,零 marker**): // `u8 type | u64 id | string signalName` // type = `SceneDeviceEntityType`(dump.cs:1749290 **只有两个值**:`Gadget = 0` / `Destructible = 1`; // 观测到的 249 条全部是 **0 = Gadget**) // signalName = **偏置** 7-bit varint 长度 + UTF-8(与本文件 RecordValue 的 C2S 字符串同规则, // ⛑ 不是 S2C 的 raw —— 见本文件头"陷阱 1:字符串偏置是不对称的") // // ⛑⛑ **绝对不要在这个 handler 里推 `SyncSceneDeviceSignalSend`(68477555)**:本文件头已记录, // 客户端会把服务端推的信号**当自己的请求再发回来**(donor 13A 实测 **14 次/秒**)⇒ 真回环。 // 这里只回空 OK + 打日志。将来真要驱动玩法,另开一条不会回环的路。 // // ⛑ 硬规则 94:id 必须进 `GameRouter.cs` 的 `Enabled4229938MethodIds`,否则编译过但不注册。 // -------------------------------------------------------------------------------------- func GameRouter_SceneDevices_OnAskSceneDeviceSendSignal(ctx context.Context, conn any, msg any) any { // Router implementation for GameRouter_SceneDevices_OnAskSceneDeviceSendSignal var zero any; return zero } func GameRouter_SceneDevices_TypeName(ctx context.Context, typeVal byte) string { // Router implementation for GameRouter_SceneDevices_TypeName return "" } // // C2S 设备字符串的**有偏** 7-bit varint 长度:线上是 `len + 1`,返回时已减回。 // 同时给出消耗的字节数(观测到的信号名都 < 127 字节,但保龄球那条 25 字符也走同一路径, // 不做多字节解析将来迟早踩到)。 // func GameRouter_SceneDevices_ReadBiasedLen7(ctx context.Context, span []byte) any { // Router implementation for GameRouter_SceneDevices_ReadBiasedLen7 var zero any; return zero }