1127 lines
66 KiB
C#
1127 lines
66 KiB
C#
using Ananta.SDK.Rpc;
|
||
using Ananta.Server.Protocol.Client4229938;
|
||
using Ananta.Server.Handlers.LoginGate;
|
||
|
||
namespace Ananta.Server.Handlers.Game;
|
||
|
||
/// <summary>
|
||
/// Build 4229938 private-server surface: world entry, direct character switching,
|
||
/// combat, traversal/buffs and movement. Everything else falls back to typed-neutral RPC replies.
|
||
/// </summary>
|
||
internal sealed partial class GameRouter
|
||
{
|
||
internal const string WorldStateKey = "client4229938-world";
|
||
private readonly GateSessionHub? _gateSessions;
|
||
|
||
internal GameRouter(GateSessionHub? gateSessions = null) => _gateSessions = gateSessions;
|
||
|
||
/// <summary>
|
||
/// 客户端挂起(切后台 / 失焦,<c>RequestPlayerStartHangup</c>)期间必须停止一切后台推送。
|
||
///
|
||
/// 客户端在 Hangup 时不再消费网络消息;服务端若继续全速写,TCP 缓冲会被灌满,
|
||
/// 客户端回到前台时判定「与服务器的网络连接已断开」⇒ RPC session 归零
|
||
/// (Player.log: <c>session=0|pid=0|0->0</c> + <c>RPCNotify invalid socket</c>)⇒
|
||
/// 进 Reconnect Module ⇒ 12 秒重连超时。
|
||
/// 实测 2026-09-22 23:58:19 / 23:58:32 / 00:05:19 三次断线都是这个形态,
|
||
/// 且每次都紧跟在 <c>[Davinci] type=TimeScaleAnomaly</c> 之后 0.5 ms。
|
||
///
|
||
/// ⛑️ <c>WorldEntryState.IsHangup</c> 在加这道门之前全项目**只有写入、零处读取**
|
||
/// (挂起只是记了个日志就不管了),这是断线重连的直接原因。
|
||
/// 所有后台推送循环在 tick 开头调用它:挂起期间只睡不推,恢复后自动继续。
|
||
/// </summary>
|
||
internal static bool IsBackgroundPushPaused(Ananta.SDK.Network.TcpSession? session)
|
||
{
|
||
if (session is null)
|
||
return false;
|
||
var state = GetStateIfExists(session);
|
||
if (state is null)
|
||
return false;
|
||
lock (state.SyncRoot)
|
||
return state.IsHangup;
|
||
}
|
||
|
||
private static readonly HashSet<uint> Enabled4229938MethodIds = new()
|
||
{
|
||
// Login / world-entry barriers.
|
||
MethodId.LoginGame,
|
||
MethodId.RequestGameSceneData,
|
||
MethodId.GetServerTimeGame,
|
||
MethodId.GetSpriteToken,
|
||
MethodId.CheckPSOPermissions,
|
||
MethodId.AskSetGamePause,
|
||
MethodId.AskLoadSceneCompleted,
|
||
MethodId.AskLoadGameResCompleted,
|
||
MethodId.AskLoadingFinished,
|
||
MethodId.AskLoadedInSameScene,
|
||
|
||
// Roster + direct character switching.
|
||
MethodId.AskAllSpiritPanelData,
|
||
MethodId.AskSwitchSpirit,
|
||
// 客户端 GM 控制台的「就地切人」(69758035)。INVOKE,参数 = uint32 的 FightSpirit id。
|
||
// ⛑ 硬规则 94:不进这张表 = 编译通过但 handler 永不注册,客户端会 await 到超时。
|
||
// 之前 3 次 INVOKE 全部落到 unimplemented 空 ack,就是这个原因。
|
||
MethodId.GmSwitchSpiritHere,
|
||
// 切人演出 phase 2 回执(2026-09-24,见 GameRouter.Switching.cs)。INVOKE,
|
||
// ⛑ 硬规则 94:不进这张表 = 编译通过但 handler 永不注册,客户端会 await 到超时。
|
||
MethodId.ReportPreSwitchSpiritFinish,
|
||
|
||
// Tab wheel summoning (嘉姆/帮派Boss, dial to the second wheel). Unlike the Z wheel this
|
||
// one really asks the server, so it is how characters outside the client's CanJoin list
|
||
// can be brought into the world. See GameRouter.GangBoss.cs.
|
||
MethodId.AskSummonGangMember,
|
||
// The same wheel's "disband all". Summoned characters have no other despawn path, so
|
||
// leaving this unrouted means they accumulate permanently.
|
||
MethodId.AskDestroyAllGangMember,
|
||
|
||
// Fashion / wardrobe: client sends wear choices from the appearance panel;
|
||
// the server echoes SyncSetSpiritFashions so the model keeps the skin after closing.
|
||
MethodId.AskSetSpiritFashionsWithSource,
|
||
|
||
// Wardrobe bookkeeping (mark-seen / favourite / read suit list). Ack-only INVOKEs: with no
|
||
// route the client awaits a response that never comes and the wardrobe button hangs.
|
||
MethodId.AskReadFashions,
|
||
MethodId.AskFavoriteFashions,
|
||
MethodId.AskReadFashionSuits,
|
||
|
||
// Wardrobe dye (染色). Ack-only INVOKEs — the client commits the dye locally on err=0 and
|
||
// only needs the server to remember it (see GameRouter.FashionColoring.cs). Unrouted, the
|
||
// dye page waits on an INVOKE that is never answered and the apply button hangs.
|
||
MethodId.AskUnlockFashionColoringSlot,
|
||
MethodId.AskSetFashionColoringSchemeInfos,
|
||
MethodId.AskApplyFashionColoringSchemeInfos,
|
||
MethodId.AskResetFashionColoringSchemeInf,
|
||
|
||
// Weapon skins (武器皮肤). Ownership is seeded at login from WeaponSkinConfig; these two
|
||
// remember which skin each character actually wears. Ack-only INVOKEs — unrouted, the skin
|
||
// picker waits on a reply that never comes. See GameRouter.WeaponSkins.cs.
|
||
MethodId.AskSetWeaponSkins,
|
||
MethodId.AskSyncWeaponSkinToSpirits,
|
||
|
||
// Profession phone apps (清洁工 Washer / 赛车手 Racer). Entry and switch gate were already
|
||
// open; only the content RPCs were unanswered, so each panel rendered an empty shell.
|
||
// See GameRouter.JobApps.cs.
|
||
MethodId.AskGetWasherMissionInfo,
|
||
MethodId.AskAcceptWasherMission,
|
||
MethodId.AskFinishWasherMission,
|
||
MethodId.AskRacingCompetitionGroupInfo,
|
||
|
||
// Phone menu skin parts (手机壁纸/装饰/挂件). The apply button only asks; the worn suit
|
||
// lives in the per-character login-payload field SpiritInfo.MobileSkinInfo, so the server
|
||
// has to store the choice and echo it back as SyncSpiritMobileSkinPartInfo.
|
||
MethodId.AskSetMobileSkinPart,
|
||
MethodId.AskResetMobileSkinPart,
|
||
|
||
// 手机拨号线(T1-9)。通讯录与解锁进度在登录帧下发,这两条是拨号/通话后客户端 await 的回执:
|
||
// 不注册 = 走中性兜底,客户端回调拿到非预期分支 ⇒ 通话记录不落本地、联系人选项一律播 FailDialogId。
|
||
// 见 GameRouter.Phone.cs。⛑ 回滚:把这两行删掉即可(handler 不注册 = 回到兜底)。
|
||
MethodId.AskPhoneAddCallRecord,
|
||
MethodId.AskPhoneContactOptionAction,
|
||
|
||
// 对话线(T1-4 / T2-15)。`AskStartDialog` 客户端 await 的是 **uint 返回值**,兜底回的是
|
||
// 0 字节空包 ⇒ 实测拨 Shannon Dairy 卡 20 秒;另两条是 void,回空 OK 即可。见 GameRouter.Dialog.cs。
|
||
// ⛑ 2026-09-25 补记:Dialog.cs 里的 AskStartDialog 当时只写了 handler、**漏了这三行白名单**
|
||
// ⇒ 按硬规则 94 它根本不会注册。本次一并补齐。
|
||
MethodId.AskStartDialog,
|
||
MethodId.AskDoDialogAction,
|
||
MethodId.AskForceFinishDialog,
|
||
|
||
// 牛奶车「车尾·回血」交互(2026-10-01 实机抓包:`AskDrinkMilk [0x04072CA2] body=8b`
|
||
// = gadget uniqueId 0x5000000000000001,与 debugPlace 的 Seq 逐位吻合)。
|
||
// dump.cs:1730509 `UXRPCTask AskDrinkMilk(ulong instanceId)` ⇒ 返回 void,回空 OK 即可;
|
||
// 回血本身由服务端推 SyncUnitAttrs + SyncUnitHp(见 GameRouter.MilkCar.cs)。
|
||
// ⛑ 回滚:删掉这一行即可(handler 不注册 = 回到兜底,点回血只有演出不回血)。
|
||
MethodId.AskDrinkMilk,
|
||
|
||
// 杂项轻量回执(T1-1 NPC 语音 / T1-2 城市图鉴 / T1-18 锁定目标)。见 GameRouter.MiscAcks.cs。
|
||
// ⛑ AskSetClientLockTarget 实际是 NOTIFY,接它只为观察;注册 Both 以防将来改 INVOKE。
|
||
MethodId.AskAgentStartNpcSound,
|
||
MethodId.AskReadCityPedia,
|
||
MethodId.AskSetClientLockTarget,
|
||
|
||
// 第二批(2026-09-25 19:5x):全部取自 `unknown-methods.log` 里**仍在兜底**的 INVOKE,
|
||
// 按最后出现时间筛过(不是照清单抄 —— 清单状态列已滞后)。返回全是 void ⇒ 空 OK。
|
||
// 见 GameRouter.MiscAcks.cs 文件末尾的注释(含逐条的 dump 行号与最后出现时间)。
|
||
MethodId.AskDoNpcAction,
|
||
MethodId.SyncWorldBattlePlayers,
|
||
MethodId.AskReleaseLuaSlotEntityEvent,
|
||
MethodId.AskAddPaokuLimit,
|
||
MethodId.AskRemovePaokuLimit,
|
||
MethodId.AskEnterMoveGround,
|
||
MethodId.AskLeaveMoveGround,
|
||
|
||
// 第三批(2026-09-27):前两批把「返回 void ⇒ 空 OK」的收完了,这五条**要真返回值**,
|
||
// 空 OK 会让客户端在反序列化处拿到 null/0 字节而走错分支。返回体布局全部以
|
||
// `RPCDeserializeAuto.lua` 的返回值 reader 为准(不是照 dump.cs 猜):
|
||
// AskTimePanelInfo → struct 无标记 + 空 7Bit 列表(`FF 01`)
|
||
// GetUploadObjectUrl → complex `FF` + string/u32/string(本服无上传端点,回空值)
|
||
// AskItemDestructibleCreate → 裸 u64(号走 IdCounterStore,硬规则 104)
|
||
// AskStartClimb / AskEndClimb → 日志里是 **NOTIFY**,注册 Both 只为留证据 + 防改 INVOKE。
|
||
// 见 GameRouter.MiscAcks.cs 第三批注释。⛑ 回滚:删这五行即可(handler 不注册 = 回兜底)。
|
||
MethodId.AskTimePanelInfo,
|
||
MethodId.GetUploadObjectUrl,
|
||
MethodId.AskItemDestructibleCreate,
|
||
MethodId.AskStartClimb,
|
||
MethodId.AskEndClimb,
|
||
|
||
// 场景设备「发信号」:`AskSceneDeviceSendSignal`(67144566) —— `unknown-methods.log` 里
|
||
// **出现最多的 INVOKE**(249 次)。返回 void ⇒ 空 OK;⛑⛑ handler 里**绝不推**
|
||
// `SyncSceneDeviceSignalSend`(68477555)(客户端会把它当自己的请求发回来 ⇒ 真回环)。
|
||
// 见 GameRouter.SceneDevices.cs 文件末尾。
|
||
MethodId.AskSceneDeviceSendSignal,
|
||
|
||
// 第四批(2026-09-27 23:3x):T1-44 **A 组 16 条 void 回执**,一次清完。
|
||
// 逐条核过方向:`INVOKE` 1~156 次、`NOTIFY` **全 0** ⇒ 全部按 Invoke 注册。
|
||
// 返回全是 void ⇒ 空 OK。其中受击/击飞四条做了字段解析(唯一能读到 `stiffId` / `stiffTime` /
|
||
// `speed` 真值的通道)。见 GameRouter.MiscAcks.cs 第四批注释。⛑ 回滚:删这 16 行。
|
||
MethodId.ReportUnitHitFly,
|
||
MethodId.ReportUnitHitFlyEnd,
|
||
MethodId.ReportUnitFallGround,
|
||
MethodId.ReportUnitFallGroundEnd,
|
||
// 第五批(2026-09-29 20:4x):投技/钳制两条,**纯抓包用**(只打日志 + 空回执,不改行为)。
|
||
// 目的是确认「客户端到底发不发、什么时候发、enemyid 是谁」——目前这两条只有
|
||
// DefaultReturnCatalog 的空兜底,连一行日志都没有,无法判断投技演出的时序。
|
||
// 契约:各一个 u64 enemyid(ClientToGameSceneDelegate.lua:326 / :4775)。
|
||
// ⛑ 回滚:删这两行 + GameRouter.MiscAcks.cs 里对应的两个 handler。
|
||
MethodId.AskControlPowerHoldEnemyUp,
|
||
MethodId.AskControlPowerHoldEnemyFallGrou,
|
||
MethodId.AskCancelInteractionActionRedPoi,
|
||
MethodId.ReportPlayerStartCommonInteract,
|
||
MethodId.AskPlayerOnMetro,
|
||
MethodId.AskRemoveMapPin,
|
||
MethodId.GmActiveBadgeEffect,
|
||
MethodId.GMActivateNpcProfile,
|
||
MethodId.EndWorldLifeGame,
|
||
MethodId.AskRestaurantBuyFoods,
|
||
MethodId.RecordAgentBehavior,
|
||
MethodId.ChangeVehicleDoorState,
|
||
MethodId.AskStartGuideByCondition,
|
||
MethodId.AskSetSpiritFashionVariantPreference,
|
||
|
||
// 第五批(2026-09-27 23:4x):T1-44 **B 组 5 条要真返回值的**。空 OK 会让客户端拿到 0 字节
|
||
// 后在反序列化处走错分支 ⇒ 按 lua 的返回值 reader 造真返回体。
|
||
// `AskPutMapPinFar`→`PinData`(struct 无标记,Id 走 IdCounterStore,PinPos 回传请求的 x/z)
|
||
// `GetCharacterRandomDialog`→裸 u32 / `StartWorldLifeGame`→裸 u64(发号)
|
||
// `AskBasketballOperatorAction`→裸 u64(0)
|
||
// `AskWushuTournamentSeasonData`→complex(全 0 + 空 Rounds 列表)
|
||
// ⛑ 只保证结构与 await 正确,不保证语义(无对话表 / 无赛季数据 / 图钉不持久化)。
|
||
// 见 GameRouter.MiscAcks.cs 第五批注释。⛑ 回滚:删这 5 行。
|
||
MethodId.AskPutMapPinFar,
|
||
MethodId.GetCharacterRandomDialog,
|
||
MethodId.StartWorldLifeGame,
|
||
MethodId.AskBasketballOperatorAction,
|
||
MethodId.AskWushuTournamentSeasonData,
|
||
|
||
// 第七批(2026-10-03):挑战记录 **T0-2** 的运行时那一半。
|
||
// 客户端 2026-10-02 11:58 实机跑过一轮完整挑战流程(AskNewChallengeRecordList →
|
||
// StartNewChallenge → SetNewChallengeData → FinishNewChallenge),当时四条全落兜底。
|
||
// ⛑ 只接「记录读写」三条:`StartNewChallenge` / `SetNewChallengeData` 之外的 void 类与兜底字节
|
||
// 完全等价;`FinishNewChallenge` 要新造 4 个嵌套契约(Reader[607/308/1428/500])才能接,
|
||
// 而兜底那 88 字节本来就是对的 ⇒ 不动它。详见 GameRouter.ChallengeRecords.cs 文件头。
|
||
// ⛑ 回滚:删这 3 行即可(handler 不注册 = 回到中性兜底)。
|
||
MethodId.SetNewChallengeData,
|
||
MethodId.AskNewChallengeRecord,
|
||
MethodId.AskNewChallengeRecordList,
|
||
|
||
// 第六批(2026-09-29):`unknown-methods.log` **近三天(09-27 / 09-28)**仍在兜底、
|
||
// 且源码里确实没有 handler 的 **全部 13 条 INVOKE**。
|
||
// ⛑ 取数方式:日志是 09-15 起的累计,top30 里绝大多数早已注册 ⇒ 不能直接照抄频次榜,
|
||
// 必须先按 ts 过滤再与源码做差集(硬规则 214)。这 13 条 NOTIFY 计数全为 0 ⇒ 全按 Invoke。
|
||
// 返回:11 条 void(空 OK)+ `AskCloseConnectionToGame` 回 `0u`
|
||
// + `AskMultiverseStatus` 回 complex 空壳(见 MiscMethods4229938.cs)。
|
||
// ⛑ 回滚:删这 13 行即可(handler 不注册 = 回到中性兜底)。
|
||
MethodId.GMChangeNpcFavor,
|
||
MethodId.GmDropReward,
|
||
MethodId.FastReenter,
|
||
MethodId.AskVehicleSendGamePlaySignal,
|
||
MethodId.AskChangeSceneItemQuality,
|
||
MethodId.AskPuppetGetup,
|
||
MethodId.AskChangeRoleUseSystemName,
|
||
MethodId.AskMetroHit,
|
||
MethodId.AskMetroHitEnd,
|
||
MethodId.AskSaveSettings,
|
||
MethodId.AskCloseConnectionToGame,
|
||
MethodId.AskClearSwitchInFashionCache,
|
||
MethodId.AskMultiverseStatus,
|
||
|
||
// 第七批(2026-09-29):客户端 **GM 控制台** 剩下的 9 条 INVOKE(各只出现过 1 次)。
|
||
// 频次低但**方向是 INVOKE** ⇒ 漏登记 = 控制台按钮 await 到超时(硬规则 94)。
|
||
// 返回全部 void ⇒ 空 OK。⛑ 不做真实现(缺系统解锁表,硬规则 230)。
|
||
// 加上第六批,近三天日志里的 **INVOKE 已 100% 有回执**。⛑ 回滚:删这 9 行。
|
||
MethodId.GmWorldRefreshEnemyGroup,
|
||
MethodId.GmRecoverAllSkillCooldown,
|
||
MethodId.GmFinishAllGuides,
|
||
MethodId.GmUnlockSystem,
|
||
MethodId.GmUnlockPlanningBoardMultiPlayer,
|
||
MethodId.GmRefreshAllDestructibles,
|
||
MethodId.GmCompleteAgentProfileTarget,
|
||
MethodId.GmChangeSystem,
|
||
MethodId.GmAddFan,
|
||
|
||
// 第八批(2026-09-29):**全时段**仍未注册的 INVOKE 里返回 void 的 47 个。
|
||
// ⛑ 上一轮只统计「近三天」,结论「INVOKE 全部有回执」不严谨 —— 全时段还有 56 个。
|
||
// 本批一次接住其中 void 的 47 个(含 22 个 GM/Force 控制台命令:按了会 await 卡住)。
|
||
// 剩下 9 个要真返回值的见 GameRouter.MiscAcks.cs 第八批末尾「本批未做」清单。
|
||
// ⛑ 回滚:删这 47 行即可。
|
||
MethodId.AskTeleportOnCrossBoundary,
|
||
MethodId.AskCreateHouseParkingVehicles,
|
||
MethodId.AskDestroyHouseParkingVehicles,
|
||
MethodId.AskUseItems,
|
||
MethodId.AskPoliceTakeCaseReward,
|
||
MethodId.AskActivateNpcProfile,
|
||
MethodId.AskSwitchSpiritByTaskRole,
|
||
MethodId.ForceAcceptTask,
|
||
MethodId.ForceSubmitTask,
|
||
MethodId.RecordSceneDeviceBehavior,
|
||
MethodId.AskPlayerEnterWater,
|
||
MethodId.AskPlayerLeaveWater,
|
||
MethodId.AskStartTaxiNavigate,
|
||
MethodId.AskEndTaxiNavigate,
|
||
MethodId.AskEndTaxiTeleport,
|
||
// 2026-09-30 出租车真实现(GameRouter.Taxi.cs):AskStart/AskEnd 原本是占位 AckVoid,
|
||
// 现在由 Taxi.cs 接管;下面两条是本次新增注册(缺了 handler 根本不会被调用,规则 94)。
|
||
MethodId.AskTaxiAccelerate,
|
||
MethodId.AskAetherAISetVehicleStatus,
|
||
MethodId.AskCreateCustomLink,
|
||
MethodId.AskCancelInviterPlayerInteractio,
|
||
MethodId.AskUpdatePlayerScenarioInfo,
|
||
MethodId.AskChangePlayerScenarioSlot,
|
||
MethodId.AskChangeNameByItem,
|
||
MethodId.AskSetSpiritWearFashionHiddenPartsWithSource,
|
||
MethodId.AskTakeNpcProfileProgressRewardWithWeb,
|
||
MethodId.AskReportArcadeGameResult,
|
||
MethodId.AskPoliceTrailTeleport,
|
||
MethodId.AskPoliceFakeFileTakeReward,
|
||
MethodId.AskTradeListItem,
|
||
MethodId.RequestRevive,
|
||
MethodId.GmDaVinciCode,
|
||
MethodId.GmAddVehicle,
|
||
MethodId.GmAddHealItems,
|
||
MethodId.GmAddAllSpirits,
|
||
MethodId.GmUnlockBadge,
|
||
MethodId.GmUnlockAllQuest,
|
||
MethodId.GmSetMapEntrance,
|
||
MethodId.GmSubmitEvent,
|
||
MethodId.GmEnableThreatDebug,
|
||
MethodId.GmStartEnemyDetectDebug,
|
||
MethodId.GmStartEnemyDetectStateDebug,
|
||
MethodId.GmEndEnemyStrategyDebug,
|
||
MethodId.GmStartSyncAIAction,
|
||
MethodId.GmEndSyncAIAction,
|
||
MethodId.GmRegenerateWildEnemy,
|
||
MethodId.GmKillEnemy,
|
||
MethodId.GmEnemyAiStopFlagByCamp,
|
||
MethodId.GmAllSwitchToBattle,
|
||
MethodId.GmTestDamage,
|
||
MethodId.GmDeathPartyForcePlayerWin,
|
||
|
||
// 第九批(2026-09-29 16:2x 实机后补):历史日志里没有、本次会话第一次被点到的 3 条 GM INVOKE。
|
||
// ⛑ 「收口」是相对当前日志的 —— 每次实机完都要重跑 `_wm_pending_byid.py`(第八批的教训)。
|
||
// `GmAddWeapon` 是 0.5~1 秒连发重试,最该接的一条。返回全部 void。⛑ 回滚:删这 3 行。
|
||
MethodId.GmAddWeapon,
|
||
MethodId.GmAddOtherBuff,
|
||
MethodId.GmRemoveBuff,
|
||
|
||
// 第十批(2026-09-29 20:4x):相对上一轮差集的**唯一新增 INVOKE**。
|
||
// RET 是 List7Bit<uint>(非空 void),handler 手拼 `FF 01`,见 GameRouter.MiscAcks.cs。
|
||
// ⛑ 回滚:删这 1 行 + MiscAcks.cs 里第十批那条 handler。
|
||
MethodId.AskSetSpiritFunctionSuitSchemeIn,
|
||
|
||
// Combat / weapon sandbox.
|
||
MethodId.AskSwitchWeapon,
|
||
MethodId.AskSwitchFightStyle,
|
||
MethodId.AskSetWeaponFightStyle,
|
||
MethodId.AskLoadWeaponToSlot,
|
||
MethodId.AskDepositSpiritWeapon,
|
||
MethodId.AskExchangeWeaponSlot,
|
||
MethodId.AskClientUseCommonSkill,
|
||
MethodId.AskUseSkill,
|
||
MethodId.AskSkillUseWeaponDurability,
|
||
MethodId.AskWeaponEquipBullets,
|
||
MethodId.ReportSkillEnd,
|
||
MethodId.AskMultipleSkillHit2,
|
||
MethodId.AskInterruptSkillExecute,
|
||
MethodId.AskInterruptSkillExecuteStiff,
|
||
MethodId.AskSkillExecuteEnd,
|
||
MethodId.AskSkillExecute,
|
||
MethodId.AskSkillDestructibleCreate,
|
||
// Dropped-weapon pick-up. Paired with AskSkillDestructibleCreate above — see
|
||
// GameRouter.WeaponDrop.cs: the drop hands out a destructible id and this one consumes it.
|
||
MethodId.AskPickUpWeapon,
|
||
|
||
// Scene prop activation report — see GameRouter.Gadget.cs. The client flips the prop
|
||
// locally and tells us; we echo it back so the state survives a relog.
|
||
MethodId.AskActiveDynamicGo,
|
||
|
||
// Doors as gadgets (T2-11) — see GameRouter.Door.cs. The client reports a crossing and
|
||
// waits for the INVOKE answer; unrouted it never entered the door flow at all.
|
||
MethodId.AskPlayerEnterOrLeaveDoor,
|
||
|
||
// Lifts — see GameRouter.Elevator.cs. The call button is only a request; the cab moves
|
||
// when we echo SyncMobilePlatformInfo back, so these must be answered, not swallowed.
|
||
MethodId.AskMoveMobilePlatform,
|
||
MethodId.AskGetOnMobilePlatform,
|
||
MethodId.AskGetOffMobilePlatform,
|
||
|
||
// Scene devices (doors / vending / fridge / arcade) — see GameRouter.SceneDevices.cs.
|
||
// The generic fallback already answers these with the generated 8-byte callback, but the
|
||
// client also waits for the echoed state/value transition before it plays the interaction,
|
||
// and the generated neutral answer to the Spoon condition check is "false".
|
||
MethodId.AskSceneDeviceChangeState,
|
||
MethodId.AskSceneDeviceRecordValue,
|
||
MethodId.CheckCanOccupySceneDevice,
|
||
MethodId.ReleaseOccupySceneDevice,
|
||
MethodId.AskCheckSpoonConditionByType,
|
||
MethodId.AskGetSpoonServerOutputPortIndexByType,
|
||
MethodId.AskDoSpoonServerActionByType,
|
||
|
||
// Client-spawned (local) enemy combat. These are the reports the client's own combat
|
||
// sim emits about the units it owns; GameRouter.LocalEnemyCombat.cs answers the fall
|
||
// handshake and logs the rest, so a locally generated enemy's real behaviour is
|
||
// visible in the console instead of being inferred.
|
||
MethodId.AskPlayHurtEffect,
|
||
MethodId.AskEnemyStartFall,
|
||
MethodId.AskEnemyEndFall,
|
||
MethodId.AskEnemyFallGround,
|
||
MethodId.AskEnemyUseClientSkill,
|
||
|
||
// ⛑⛑ 硬规则 94:白名单是「允许注册的 [Handler]」集合,只在 C2S 方向生效;
|
||
// 但 GameRouter 是 partial,EnemyStiff.cs / LocalEnemyCombat.cs 里新写的 handler 一样走
|
||
// RegisterSelected —— 不进这张表,代码编译通过、日志却永远不会出现(Invoke 还会让客户端 await 卡死)。
|
||
// 新增任何 [Handler] 后都必须同步补这里。
|
||
MethodId.AskAgentEnterStiff,
|
||
MethodId.AskAgentExitStiff,
|
||
MethodId.ReportUnitStandUp,
|
||
MethodId.ReportEnemyPrepareFinish,
|
||
MethodId.ReportAgentDiePerfEnd,
|
||
MethodId.ReportEnemyBattleMoveEnd,
|
||
|
||
// Client-decided traversal/web buffs.
|
||
MethodId.AskAddClientBuff,
|
||
MethodId.AskRemoveClientBuff,
|
||
MethodId.AskEnterFeiSuoCrouch,
|
||
MethodId.AskFeiSuoSuccess,
|
||
MethodId.AskLeaveFeiSuoCrouch,
|
||
// 蹲伏暗杀:客户端上报「谁在我背后」,我们回 SyncEnemyCanAssassinate 授权。
|
||
// 不登记 ⇒ 客户端每 0.4s 发一次但服务端完全收不到 ⇒ HUD 不出暗杀提示(规则 94)。
|
||
MethodId.ReportCanAssassinateEnemies,
|
||
|
||
// Live transform tracking.
|
||
MethodId.AskReportLogicAgentSyncData,
|
||
MethodId.AskUnitMoveActionSimple,
|
||
MethodId.AskUnitMoveActionSimpleWithGround,
|
||
MethodId.AskUnitMoveActionWithGround,
|
||
MethodId.AskUnitMoveAction,
|
||
|
||
// Time/sync basics.
|
||
MethodId.GetServerTimeGame,
|
||
|
||
// Task panel: the client offers "settle/abandon the running task" on ESC whenever it
|
||
// believes a task is active, and confirming it invokes this. Without an answer the
|
||
// client's await never completes and the panel appears frozen.
|
||
MethodId.AskDeleteTask,
|
||
|
||
// Scene ("raid") switching.
|
||
MethodId.AskPublicSwitchToPublicScene,
|
||
MethodId.AskEnterRaidByMapEntrance,
|
||
// 大地图「交通直达」同图传送(2026-09-29 补):MapSubSystem_Entrance.lua:566 的
|
||
// AskPlayerChangePositionByScenePortal 是 Invoke,不进白名单 handler 一次都不会被
|
||
// 调用(硬规则 94)⇒ 客户端 await 永久挂起 ⇒ 地铁站点了没反应。
|
||
MethodId.AskPlayerChangePositionByScenePortal,
|
||
|
||
// Teleport (inbound requests; pushes need no registration).
|
||
MethodId.AskTeleport,
|
||
MethodId.ReportPreTeleportFinish,
|
||
MethodId.ReportPostTeleportFinish,
|
||
MethodId.Teleport,
|
||
// 车辆改装店(ApplyCar / RepairShop)进场事务的两个 domain RPC:
|
||
// 不进这张表 handler 一次都不会被调用(硬规则 94)⇒ 客户端停在 loading 黑屏,
|
||
// 而且它们都是 INVOKE,漏登记会让客户端 await 永久挂起。见 GameRouter.Teleport.cs。
|
||
MethodId.AskTeleportToParkingWaypoint,
|
||
MethodId.AskTeleportToLeavingWaypoint,
|
||
|
||
// Vehicles: summon + owned fleet + client-driven drive loop + S011 boarding story.
|
||
// Drive-loop notifies/invokes are accepted (no-op / empty-OK) so the client-side
|
||
// drive loop never stalls — proven set from the sibling 08.05 build.
|
||
MethodId.GmAddVehicle,
|
||
MethodId.AskVehicleShopSpawnVehicle,
|
||
// 改装店购买链(2026-09-26 自 repair-shop.zip 移植)。全是 INVOKE:不进白名单
|
||
// 客户端就永远 await 不到回包(面板点了没反应 / 卡住),所以必须注册。
|
||
MethodId.AskNpcShopCommodityInfo,
|
||
MethodId.AskVehicleShopDestroyParking,
|
||
MethodId.AskBuyCommodities,
|
||
MethodId.AskBuyCommodity,
|
||
MethodId.AskBuyModifyParts,
|
||
MethodId.VehicleDriveStateChange,
|
||
MethodId.AskGetUnlockedVehicles,
|
||
MethodId.AskSummonVehicle,
|
||
// T1-16 载具骇入 (ported from donor_7k).
|
||
MethodId.AskHack,
|
||
MethodId.AskHackVehicle,
|
||
// EonBug app "pick a handle" dialog (63975191, INVOKE). Left unanswered it re-arms every
|
||
// couple of seconds and the app can never be closed — see GameRouter.HackerApp.cs.
|
||
MethodId.AskChangeHackerName,
|
||
|
||
// EonBug forum actions (读新帖 63150736 / 接差事 63036377). Both return void; unrouted they
|
||
// are INVOKEs the client awaits forever, so tapping a post never clears.
|
||
MethodId.AskReadHackerNewPost,
|
||
MethodId.AskAcceptHackerPostTask,
|
||
MethodId.AskUpdateVehicleAITaskStatus,
|
||
|
||
// Cat Express / 蓝猫物流 (UberSim) — courier board. All nine are INVOKE: an unregistered
|
||
// one makes the client await a reply that never comes (hard rule 94), which is exactly what
|
||
// AskGetTruckSatisfactionAverage was doing — 16 unhandled 0-byte calls in unknown-methods.log.
|
||
MethodId.AskGetTruckJobOrders,
|
||
MethodId.AskRefreshTruckOrder,
|
||
MethodId.AskAcceptTruckJobOrder,
|
||
MethodId.AskObsoleteTruckJobOrder,
|
||
MethodId.AskAutoAcceptTruckJobOrder,
|
||
MethodId.AskGetFinishedOrderWraps,
|
||
MethodId.AskGetTruckSatisfactionAverage,
|
||
MethodId.AskSetTruckJobDefaultVehicleId,
|
||
MethodId.AskStartTruckOrderGuide,
|
||
MethodId.AskPreSettleTruckOrder,
|
||
MethodId.AskSettleTruckOrder,
|
||
MethodId.AskFinishJob,
|
||
MethodId.AskAddTruckOrderSpecialPointRewards,
|
||
// 取货点 / 送货点坐标(自定义单路径 DeliveryTaskManager.lua:1870 会等它的回包)。
|
||
MethodId.AskQueryTruckPosInfo,
|
||
|
||
// Urban-job (职业) handshakes. AskStartJob is the one that actually gates the courier app:
|
||
// the "领取驾照" start button awaits it and only opens the order board from its callback, so
|
||
// an unregistered id strands the player on the start screen instead of just losing a feature.
|
||
MethodId.AskStartJob,
|
||
MethodId.AskQuitJob,
|
||
MethodId.AskAcceptTask,
|
||
|
||
// T2-31 Seymour 大停电 (Blackout) — Gadgets_Hacker_Tetris 控制器回路。
|
||
// AskStart/AskFinish 是 Invoke:漏登记会让客户端 await 永久挂起,不只是"没功能"。
|
||
// 这几个 MethodId 之前都没在本仓出现(_handler_dup_check 基线干净),不存在重复注册。
|
||
MethodId.AskStartHackerTetris,
|
||
MethodId.AskFinishHackerTetris,
|
||
MethodId.ReportHackerTetrisCreation,
|
||
MethodId.AskTaskGadgetsListLoadComplete,
|
||
MethodId.AskTaskPlateLoadComplete,
|
||
// T2-32 路人骇入(骇路人手机看信息)。AskHackingNpc / AskHackingNpcPress 是 INVOKE
|
||
// (gReliableRpcManager:RegisterRPC 等回执),漏登记 ⇒ 长按骇入时客户端 await 永久挂起;
|
||
// ReportBeHacked 是 NOTIFY,登记它只为不再落进 unknown-methods.log。
|
||
// 触发前提是服务端推过 SyncUnitHackableState(68941159) —— 见 GameRouter.HackNpc.cs。
|
||
MethodId.AskHackingNpc,
|
||
MethodId.AskHackingNpcPress,
|
||
MethodId.ReportBeHacked,
|
||
// T2-32 黑客召唤物(蜘蛛机器人 / 送货无人机)的操控交接。
|
||
// 两个都是 gReliableRpcManager:RegisterRPC 等回执的 INVOKE:长按 F 回收、N 键切换操控。
|
||
// 不登记的话客户端 await 永久挂起,操控面板的按钮就是按了没反应。
|
||
MethodId.AskStopControlAgent,
|
||
MethodId.AskControlAgent,
|
||
// 巡卫官(里希 15021021)执勤终端的三条派遣(巡卫犬 2 / 直升机搜查 3 / 战术支援 4)。
|
||
// INVOKE:客户端 gReliableRpcManager 等回执。此前不在表内 ⇒ 走 DefaultUnknownInvoke 空 OK,
|
||
// 按钮"点得动、没效果"。⛑ 硬规则 94:不进这张表 handler 一次都不会被调用。
|
||
// 见 GameRouter.PoliceDispatch.cs。回滚 = 删掉这一行(回到中性兜底)。
|
||
MethodId.AskPoliceDispatch,
|
||
// 直升机搜查(同一条派遣线的第二组):AskHelicopterNpc 是 INVOKE 且客户端四个调用点都在
|
||
// RegisterRPC 等回调,漏登记 = 面板点了卡住;ScanChange 是 NOTIFY,登记只为不再落进
|
||
// unknown-methods.log;Stop 是 INVOKE(无参)。见 GameRouter.PoliceDispatch.cs。
|
||
MethodId.AskHelicopterNpc,
|
||
MethodId.AskHelicopterScanChange,
|
||
MethodId.AskPoliceStopHelicopterDispatch,
|
||
// 正向逮捕 NPC(2026-09-25):AskGetArrestTimes 返回 int、AskIsArrested / AskPoliceEffectiveExam
|
||
// 无返回值,三条都是 INVOKE ⇒ 漏登记就是面板点了卡住。AskPoliceExamInteract 由 C# 侧发
|
||
// (lua 零引用、RpcSurface 里也没有),形状未知,接住只为回执 + 取证 hex。
|
||
// AskInteractCmd 是 NOTIFY,登记只为不再落进 unknown-methods.log 并抓前 5 条字节。
|
||
// 见 GameRouter.PoliceArrest.cs。回滚 = 删这五行(回到中性兜底)。
|
||
MethodId.AskGetArrestTimes,
|
||
MethodId.AskIsArrested,
|
||
MethodId.AskPoliceEffectiveExam,
|
||
MethodId.AskPoliceExamInteract,
|
||
// 开罚单(2026-09-25):PoliceExamineManager.lua:1163 由 C# 侧发 AskPoliceFine(pid, fineIdList),
|
||
// INVOKE ⇒ 不登记就是"点了开罚单没结果"(客户端 await 卡死 + 服务端从不回掉落帧)。
|
||
// 登记后由 GameRouter.PoliceFine.cs 回 ShowReceiveRewardDetail(64145515) Reason=PoliceFine
|
||
// ⇒ 客户端弹罚款结果。回滚 = 删这一行 + 删 GameRouter.PoliceFine.cs。
|
||
MethodId.AskPoliceFineNpc,
|
||
// 掉落物拾取(路人打落的手机 / NPC 携带物)。两个都是 INVOKE、返回体空 reader:
|
||
// 客户端掉手机是本地行为(GM DebugEnableDropPhoneWhenDeath),但拾取时要服务端回执,
|
||
// 不登记 ⇒ 走 DefaultUnknownInvoke 兜底、拾取动作卡在 await(硬规则 94)。
|
||
// 见 GameRouter.NpcPhoneDrop.cs。回滚 = 删这两行(回到中性兜底)。
|
||
MethodId.AskPickUpSceneItemTransferToItem,
|
||
MethodId.AskEnemyItemPickUp,
|
||
// Plate(盘)演出管线的四条客户端回执(2026-09-25 试点,见 GameRouter.Plate.cs)。
|
||
// 它们只在服务端推过 SyncPlateAddList(68554224) 之后才会发,unknown-methods.log 里
|
||
// 一次都没出现过 ⇒ 不登记就永远看不到,也就没有「线格式对不对」的判据。
|
||
// AskPlateLoadComplete / AskPlateResourceReady 是★通关判据。全按 INVOKE 回空 OK,
|
||
// 免得客户端 await 挂死。回滚 = 删这四行。
|
||
MethodId.AskPlateLoadComplete,
|
||
MethodId.AskPlateResourceReady,
|
||
MethodId.AskPlateAgentCreateConfirmed,
|
||
MethodId.AskNotifyPlateGameStallEvent,
|
||
// ★★ 客户端主动问「好感 NPC 都在哪儿」(2026-09-25):`AskQueryAllFavorNpcAgentPos`
|
||
// 在 unknown-methods.log 里出现过 54 次,我们一直回空字典 ⇒ 客户端眼里一个好感 NPC 都不在场。
|
||
// 真数据 = NpcActivityCatalog 的活动坐标 + RaidId(与常驻 NPC 落点同一份数据)。
|
||
// 见 GameRouter.NpcLife.cs。回滚 = 删这两行。
|
||
MethodId.AskQueryAllFavorNpcAgentPos,
|
||
MethodId.AskQueryFavorNpcAgentPos,
|
||
MethodId.AskPlayerStartEnterOrExitVehicle,
|
||
MethodId.AskPlayerFinishEnterOrExitVehicle,
|
||
MethodId.AskVehicleMove,
|
||
MethodId.AskVehicleStartMove,
|
||
MethodId.AskVehicleStopMove,
|
||
MethodId.AskVehicleHorn,
|
||
MethodId.AskEnterVehicleIndoor,
|
||
MethodId.AskExitVehicleIndoor,
|
||
MethodId.ReportDrivingVehicle,
|
||
MethodId.AskKillVehicle,
|
||
MethodId.AskChangeGoVehicleDriveState,
|
||
MethodId.AskVehicleDeadEnd,
|
||
MethodId.AskVehicleNitro,
|
||
MethodId.AskVehicleStuck,
|
||
MethodId.AskVehicleHit,
|
||
MethodId.AskVehicleHitEnd,
|
||
MethodId.AskClaimVehicleSeat,
|
||
|
||
// 载具路线规划 / 自驾(2026-09-24,GameRouter.VehicleNav.cs)。前三条客户端正在每秒问一次,
|
||
// 之前全落到 typed-default = 空路线;
|
||
// 2026-09-27 补:Start/Stop/ChangeTarget 现在会真的下发 S2C 68349264 推/关自动驾驶,
|
||
// 并把漏注册的 CancelTarget(67836393) 补上 —— 它是 INVOKE,缺回执会让 lua await 卡死(规则 84)。
|
||
MethodId.AskVehicleNavigationPathPoints,
|
||
MethodId.AskVehicleNavigationPathPointsFromPos,
|
||
MethodId.AskVehicleNavigationPathLength,
|
||
MethodId.AskVehicleStartAutonomousDriving,
|
||
MethodId.AskVehicleStopAutonomousDriving,
|
||
MethodId.AskVehicleChangeAutonomousDrivingTarget,
|
||
MethodId.AskVehicleCancelAutonomousDrivingTarget,
|
||
MethodId.SyncStoryCoreClientInfo,
|
||
MethodId.AskInteractCmd,
|
||
MethodId.AskInvitePlayerInteractionAction,
|
||
MethodId.AskStartPlayerInteractionAction,
|
||
MethodId.AskReplyInvitePlayerInteractionAction,
|
||
|
||
// GM console: C2S invokes (spawn vehicle, add enemy, teleport). The client
|
||
// materializes GM entities locally; the server returns real ids and logs them.
|
||
MethodId.GmSpawnVehicle,
|
||
MethodId.GmAddEnemyWithPosition,
|
||
MethodId.GmAddEnemy,
|
||
MethodId.GmAddEnemyByPlayer,
|
||
MethodId.GmAddNpc,
|
||
MethodId.GmTeleportXYZ,
|
||
|
||
// Aether city simulation (pedestrians / static NPCs / traffic). The client runs
|
||
// the whole simulation locally; these are the C2S calls it makes once Aether is
|
||
// initialized. They must be answered or the awaited tasks stall the spawner.
|
||
MethodId.AskMassHideAreas,
|
||
MethodId.AskAetherChangeQuality,
|
||
MethodId.AskAetherAIBorrowVehicle,
|
||
MethodId.AskChangePedToVehicleNpc,
|
||
MethodId.AskReportAetherLeaflet,
|
||
MethodId.AskAetherAICreateRaidVehicleAndClaimSeatByAether,
|
||
MethodId.AskAetherAIRaidVehicleRegressAether,
|
||
MethodId.AskRaidVehicleConvertToAether,
|
||
|
||
// Formerly-unhandled batch (explicit compatibility handlers).
|
||
// AskTradeGetMarketList moved down to the trade pair - it now has a real handler.
|
||
MethodId.AskTradeGetHistoryPage,
|
||
MethodId.AskPlayerRankingSummary,
|
||
MethodId.AskPopularityPhoneFirstOpened,
|
||
MethodId.AskPopularityUIOpened,
|
||
MethodId.SyncOpenInspireHub,
|
||
MethodId.AskQueryInspireHubAllGamePlayRecommendData,
|
||
MethodId.AskQueryInspireHubAllGamePlayRankData,
|
||
MethodId.AskLinkInfos,
|
||
MethodId.GetLastMode,
|
||
MethodId.AskSwitchLinkMode,
|
||
MethodId.OnParkourStateChange,
|
||
MethodId.AskPlayerCameraMove,
|
||
MethodId.AskUpdatePlayerCameraRotation,
|
||
MethodId.AskUpdatePlayerCameraFOV,
|
||
MethodId.AskUpdatePlayerCameraAspectRatio,
|
||
MethodId.AskVehicleNitroValue,
|
||
MethodId.AskModifyVehicleTopSpeed,
|
||
MethodId.AskVehicleContactDamage,
|
||
MethodId.AskVehicleInteractConfig,
|
||
MethodId.AskUpdateVehicleDestructibleParts,
|
||
MethodId.AskGetVehicleRadioContent,
|
||
// 车载电台切台通知(67959772)—— 见 GameRouter.VehicleRadio.cs。
|
||
MethodId.AskSwitchVehicleRadio,
|
||
// 大地图入口点击传送(63284182)—— 见 GameRouter.MapEntrance.cs。
|
||
// ⛑ 不进这张表 = handler 永不注册(硬规则 94)⇒ 点入口仍然没反应。
|
||
MethodId.AskTouchMapEntrance,
|
||
MethodId.SyncChangeSafeArea,
|
||
MethodId.SyncChangeBuilding,
|
||
MethodId.SyncChangeIndoor,
|
||
MethodId.GetMailHeadList,
|
||
MethodId.AskAkxSessionList,
|
||
MethodId.AskGetAllMetroInfos,
|
||
MethodId.AskMetroGadgetIds,
|
||
MethodId.AskMomentsPostSimpleInfos,
|
||
// 趴卜 (Bubble) interaction half: likes / mark-read / comments / taps. All seven are INVOKE
|
||
// and used to fall through to the generic typed default, so nothing the player did in the
|
||
// app was ever recorded. Return shapes come from DefaultReturnCatalog4229938.
|
||
MethodId.AskMomentsPostInfos,
|
||
MethodId.AskMomentsUnreadMessage,
|
||
MethodId.AskMomentsHaveUnreadMessage,
|
||
MethodId.AskMomentsMarkRead,
|
||
MethodId.AskMomentsLikePost,
|
||
MethodId.AskMomentsSendCommentWithId,
|
||
MethodId.AskMomentsTapPostWithCount,
|
||
MethodId.AskPanelOpenOrClose,
|
||
MethodId.AskQueryPlayerUnlockNameEffect,
|
||
MethodId.GetPersonalInfo,
|
||
|
||
// Skill/combat accepts (logged empty replies; authoritative damage later).
|
||
MethodId.AskInterruptSkillExecute2,
|
||
MethodId.AskSkillExecute3,
|
||
MethodId.AskSkillExecuteEnd2,
|
||
MethodId.AskSkillAddState,
|
||
MethodId.AskSkillOpenShield,
|
||
MethodId.AskSkillCloseShield,
|
||
MethodId.AskSkillTimeCurve,
|
||
MethodId.ReportSkillAnimationEnd,
|
||
MethodId.AskSkillSpawnItem,
|
||
MethodId.AskSwitchSpiritComplete,
|
||
MethodId.AskSpoonClientAttack,
|
||
MethodId.AskVehicleSkillDamage,
|
||
MethodId.AskSkillDestructibleCreateGadget,
|
||
MethodId.AskSkillDestructibleCreateVehicle,
|
||
|
||
// Time + weather.
|
||
MethodId.AskPassingTime,
|
||
MethodId.ChangePersonalTimeSetting,
|
||
MethodId.AddPersonalTimeSetting,
|
||
MethodId.GmSetWeather,
|
||
MethodId.GmSetWeatherParam,
|
||
MethodId.GmSetTime,
|
||
MethodId.GmFixRaidTime,
|
||
MethodId.GmPassingTime,
|
||
|
||
// Destructibles / red dots / discards / hangup / curves.
|
||
MethodId.AskNotifyDestructibleHits,
|
||
MethodId.AskOperateDestructibleObject,
|
||
MethodId.AskMoveDestructibleObjects,
|
||
MethodId.AskBreakDestructibleObjects,
|
||
MethodId.AskReadWeaponRedDots,
|
||
MethodId.AskDiscardWeaponByInstanceId,
|
||
MethodId.AskDiscardWeapon,
|
||
MethodId.RequestPlayerStartHangup,
|
||
MethodId.RequestPlayerStopHangup,
|
||
MethodId.AskBreakSkillTimeCurve,
|
||
|
||
// Economy: gacha draws, mall purchases and the in-game GM console (ALT+F1) wallet grants.
|
||
// Without these ids the handlers exist but are never registered, and every client request
|
||
// silently falls into logs/unknown-methods.log.
|
||
MethodId.AskDrawGacha,
|
||
MethodId.AskMallBuyCommodity,
|
||
MethodId.AskTradeGetMarketList,
|
||
MethodId.AskTradeBuyItem,
|
||
MethodId.AddMoney,
|
||
MethodId.AddItem,
|
||
|
||
// Talent trees (天赋, T1-6). All five are void-returning INVOKEs the client awaits; without a
|
||
// route they fall through to the typed-neutral fallback and every activation is silently
|
||
// dropped, so the panel shows the node as locked no matter how often the player clicks.
|
||
MethodId.AskActiveGameplayTalentLayer,
|
||
MethodId.AskResetGameplayTalent,
|
||
MethodId.AskActiveSpiritJobTalentLayer,
|
||
MethodId.AskResetSpiritJobTalent,
|
||
MethodId.AskConvertCommonSpiritTalentExp,
|
||
|
||
// NPC shops (商店). AskNpcShop is the "open the shelf" INVOKE the client fires when the
|
||
// player presses F on a clerk; without a route it lands in unknown-methods.log and the
|
||
// panel opens empty because no SyncFullCommodityInfos ever arrives.
|
||
MethodId.AskNpcShop,
|
||
|
||
// 商店的库存类交易(2026-10-02 自 7k-v0.6 移植)。⛑ 这四条不是「实现了买卖」,
|
||
// 是**刻意回绝**:捐赠端原注释 "must not fall back to an empty success reply, which
|
||
// would falsely claim a purchase/sale occurred"。它们也不在 DefaultReturnCatalog 里
|
||
// ⇒ 不注册就会走 ReturnEmptyOkAsync(error=0)= 客户端以为交易成功。详见
|
||
// GameRouter.NpcShopTrades.cs。AskCloseNpcShop 只是让它不再灌 unknown-methods.log。
|
||
MethodId.AskBuyCommodityToBag,
|
||
MethodId.AskBarterBuyCommodity,
|
||
MethodId.AskSellCommodityToShop,
|
||
MethodId.AskGeneralBuyBackWeaponToShop,
|
||
MethodId.AskCloseNpcShop,
|
||
|
||
// 环境 NPC 剧情 / 刺激事件收尾(2026-10-02 自 7k-v0.6 移植)。三条都是 Notify,
|
||
// 只维护 WorldEntryState.StaticNpcPreparedPlotEvents 这条账本;trigger 成功后发
|
||
// SyncPlotCurrentEvent(EventId=0) 把反应关掉,否则 NPC 会卡在已演完的那条反应上。
|
||
// 详见 GameRouter.Plot.cs。
|
||
MethodId.AskPreparePlotEvent,
|
||
MethodId.AskTriggerPlotEvent,
|
||
MethodId.AskEndPreparePlotEvent,
|
||
|
||
// T2 单发杂项(2026-10-02 自 7k-v0.6 移植)。两条纯记账 Notify + 三条没有等价
|
||
// runtime 的 Invoke(明确 error=1,不假装成功)。详见 GameRouter.T2Misc.cs。
|
||
MethodId.AskHackerBetray,
|
||
MethodId.SyncTrackGPS,
|
||
MethodId.AskDestroyGangMember,
|
||
MethodId.AskAgentChangeWeaponByTemplateId,
|
||
MethodId.AskResetTruckOrderGoods,
|
||
|
||
// 老版衣柜入口(无 source 参数),补齐 short source=8 后走同一套穿戴逻辑。
|
||
// 见 GameRouter.Fashion.cs。
|
||
MethodId.AskSetSpiritFashions,
|
||
|
||
// 行人路网:客户端为它手下的单位问路(详见 GameRouter.PedPath.cs)。
|
||
// 不接这条 ⇒ 走 DefaultReturnCatalog 兜底 = 永远回空路径 ⇒ 那个单位站着不动。
|
||
MethodId.AskClientNpcFindPath,
|
||
MethodId.AskAetherNpcUpdatePathForObstacle,
|
||
|
||
// GM 控制台职业命令族(GmJob*,2026-09-24 自捐赠端 DremkaPS 移植)。
|
||
// 不接这六条 ⇒ 客户端 ALT+F1 里每一次职业操作都掉进 unknown-methods.log,
|
||
// 且 Invoke 类会让控制台那次调用一直 await 到超时。详见 GameRouter.GmJobs.cs。
|
||
MethodId.GmJobTake,
|
||
MethodId.GmJobStart,
|
||
MethodId.GmJobQuit,
|
||
MethodId.GmJobPromote,
|
||
MethodId.GmSetJobLevel,
|
||
MethodId.GmJobEnd,
|
||
|
||
// GM 传送面板的原生副本切换(2026-09-24 自捐赠端 DremkaPS 移植,见 GameRouter.SceneSwitch.cs)。
|
||
MethodId.EnterRaid,
|
||
|
||
// 移动命令的回执(2026-09-24 自捐赠端 DremkaPS 移植,见 GameRouter.AgentCommandAck.cs)。
|
||
// 服务端发的 SyncLogicAgentCommand 是 fire-and-forget,不接这几条就永远分不清
|
||
// 「客户端收了但没跑」和「客户端根本拒收」。
|
||
MethodId.AskReportLogicAgentCommandSuccess2,
|
||
MethodId.AskReportLogicAgentCommandFail,
|
||
MethodId.AskReportLogicAgentCommandData2,
|
||
MethodId.AskAgentPoiActionDone,
|
||
|
||
// 迷雾 / 探索 POI / Aether 氛围层(2026-09-24 自捐赠端 DremkaPS 移植,见 GameRouter.GmFog.cs)。
|
||
// 这四条是抓包筛出来的:66 个候选里只有 6 个真被客户端发过,这里取其中与迷雾/氛围有关的
|
||
// 四条。⛑ GmGetAmbianceEnabled 是 0 字节 INVOKE,不接会让控制台 await 到超时。
|
||
MethodId.SyncEnterFogMapPoiId,
|
||
MethodId.GmUnlockAllFogMap,
|
||
MethodId.GmSetAmbianceEnabled,
|
||
MethodId.GmGetAmbianceEnabled,
|
||
|
||
// 搜打撤 / ExtractionShooter(2026-09-24 自捐赠端 DremkaPS 移植,见 GameRouter.Extraction.cs)。
|
||
// ⛑ [Handler] 写了不等于注册:不在这张表里的 methodId 会被 RegisterSelected 直接 continue
|
||
// ⇒ 请求不报错、只回 `typed-default void 0b`,且 unknown-methods.log 里反而没有。
|
||
MethodId.AskExtractionShooterSearchContainer,
|
||
MethodId.AskExtractionShooterEndSearchContainer,
|
||
MethodId.AskExtractionShooterShiftItem,
|
||
MethodId.AskExtractionShooterRemoveItem,
|
||
MethodId.AskExtractionShooterLockItem,
|
||
MethodId.AskExtractionShooterSortBag,
|
||
MethodId.AskExtractionShooterSortBags,
|
||
MethodId.AskExtractionShooterSplitItem,
|
||
MethodId.AskExtractionShooterBagExpansion,
|
||
MethodId.AskExtractionShooterTransferAllToStash,
|
||
MethodId.AskExtractionShooterRaidSplitItem,
|
||
MethodId.AskExtractionShooterShiftContainerItem,
|
||
MethodId.AskExtractionShooterRemoveContainerItem,
|
||
MethodId.AskExtractionShooterShiftContainerItemToBag,
|
||
MethodId.AskExtractionShooterShiftBagItemToContainer,
|
||
MethodId.AskExtractionShooterShiftContainerItemToSlot,
|
||
MethodId.AskExtractionShooterShiftSlotItemToContainer,
|
||
MethodId.AskQuitExtractionShooter,
|
||
// 伪造匹配(2026-09-24,见 GameRouter.ExtractionMatch.cs):客户端点「开始」的那四条通用
|
||
// 开局请求。⛑ 这四条不是搜打撤专用 —— 别的玩法开局也走,所以 handler 内部还有一道
|
||
// fakeMatch 开关,默认关;开着的日子里任何一次点开始都会被劫到 preferredRaid。
|
||
MethodId.AskStartSingleMatch,
|
||
MethodId.StartBotMatch,
|
||
MethodId.StartMatchInTeam,
|
||
MethodId.StartGameInTeam,
|
||
|
||
// 娃娃机 / 小游戏机(ClawMachineJiGuan_game.gcp,2026-09-24,见 GameRouter.ClawMachine.cs)。
|
||
// 四条 Claw/购票 + 两条 SpoonServer 动作在 dump.cs 里都是 void 返回的 INVOKE:
|
||
// 不接 ⇒ 图里的 SpoonServerMultiOutput 节点一直等到 context dispose,按 F 无反应。
|
||
MethodId.AskClawBuyTicket,
|
||
MethodId.AskBuyMiniGameTicket,
|
||
MethodId.AskClawSettlement,
|
||
MethodId.AskClawDateFail,
|
||
MethodId.AskDoSpoonServerAction,
|
||
MethodId.AskDoSpoonServerActionByType,
|
||
|
||
// MiniGame_Bee(娃娃机)结算三件套(2026-09-24,见 GameRouter.ClawMachine.cs)。
|
||
// unknown-methods.log 铁证:09-22 15:04:10 客户端连发这三条且我方当时全没接 ⇒ 结算卡死。
|
||
MethodId.SetMiniGame_BeeScore,
|
||
MethodId.AskReportArcadeGameResultBee,
|
||
MethodId.AskMiniGame_BeeSettlement,
|
||
|
||
// 保龄球 / GameGround 玩法区(2026-09-24 自 AnantaBowlingLoader_v1.0.0.0 包内
|
||
// 「3_保龄球_移植进DRMK-PS_v1.7_A方案」移植,见 GameRouter.GameGround.cs /
|
||
// GameRouter.NpcInvite.cs)。★ 硬门是 SyncGameGroundZoneInfo(68004609):客户端只有收到
|
||
// BowlingZoneInfo 才会建局,而它只在 EnterBowlingZone(67832701) 被调用后才可能到达,
|
||
// 所以 EnterBowlingZone / GmEnterBowlingZone 不进表 = 走到机器前按 F 完全没反应。
|
||
// ⛑ 规则 94:不在这张表里 = handler 编译通过但永不注册。
|
||
// 打气球(龙栖村摊位,2026-09-29 自 donor v2.1 移植,见 GameRouter.Balloon.cs)。
|
||
// ★ 与保龄球同一个 GameGround 家族,硬门 = SyncGameGroundZoneInfo(68004609),
|
||
// 只是多态 tag 换成 3(Concrete[449][3] = Auto.Reader[680])。
|
||
// ⛑ 规则 94:不在这张表里 = handler 编译通过但永不注册 ⇒ 客户端拿到 51 字节全零壳
|
||
// (ZoneSessionId 为空)⇒ 会话建不起来、面板退不出来。
|
||
// ⛑ 注意 AskBreakDestructibleObject(67463592,单数) 与表里已有的
|
||
// AskBreakDestructibleObjects(67891964,复数) 是**两个不同的号**,别互相顶掉:
|
||
// 单数是「打爆上报(计分闭环)」,气球靠它算分。
|
||
MethodId.EnterBalloonZone,
|
||
MethodId.GmEnterBalloonZone,
|
||
MethodId.LeaveBalloon,
|
||
MethodId.AskBreakDestructibleObject,
|
||
|
||
// ★★★ R90(2026-09-29 自 donor v2.1 移植)—— 龙栖村摊位小游戏「能玩」的入口 RPC 全集,
|
||
// 见 GameRouter.MiniGameEntry.cs。全部 INVOKE 型,缺一条 = 客户端 await 不到回执
|
||
// ⇒ 模态界面永久等待 ⇒ 只能大退。
|
||
// 返回值:除**三处**需手拼载荷外都是 void ——
|
||
// EnterRingTossZone(裸 u64 pid)/ AskFinishChestnutGame(裸 byte)/ AskFarmerFry(空字典 FF 01)。
|
||
// ① 套圈(纯本地计分,只需一个非零 pid)
|
||
MethodId.EnterRingTossZone,
|
||
MethodId.RecordRingTossResult,
|
||
MethodId.LeaveRingToss,
|
||
// ② 组队对局入口 —— 龙栖村所有摊位的**公共**入口,优先级最高
|
||
MethodId.AskStartPartyMiniGameMatch,
|
||
MethodId.AskConfirmPartyMiniGameMatch,
|
||
MethodId.AskCancelPartyMiniGameMatch,
|
||
// ③ 刮刮乐(入口在 IL2CPP/Spoon,Lua 0 命中;形状取自 AutoGen)
|
||
MethodId.AskStartScratchGame,
|
||
MethodId.AskLoseScratchGame,
|
||
MethodId.AskFinishScratchGame,
|
||
// ④ 五子棋
|
||
MethodId.EnterGomokuZoneDoublePlayer,
|
||
MethodId.PlaceGomokuPiece,
|
||
MethodId.LeaveGomoku,
|
||
// ④b 五子棋**单机**(人机 / 残局)—— ★ RET 是**裸 u64** = 对手实体 uid。
|
||
// 不注册时兜底回 0 ⇒ 客户端 TryGetLogicAgent(0) 失败 ⇒ 对局起不来(点了不落子)。
|
||
// 实现见 GameRouter.GomokuSolo.cs(复刻骰子 SpawnDiceOpponentAsync)。
|
||
MethodId.EnterGomokuZoneDoubleAI,
|
||
MethodId.EnterGomokuZoneEndGame,
|
||
// ⑤ 炒茶 / 炒栗 —— ★★ 唯一一组**不是 void** 的返回:
|
||
// AskStartFryChestnutGame → void;AskFinishChestnutGame → 裸 byte;
|
||
// AskFarmerFry → Dict7Bit<u32,u32>(回空字典 FF 01),走 ClientToGameDelegate。
|
||
// ⛔ 后两条必须 ReturnRawAsync 手拼载荷,照抄 ReturnEmptyOkAsync 会走位。
|
||
MethodId.AskStartFryChestnutGame,
|
||
MethodId.AskFinishChestnutGame,
|
||
MethodId.AskFarmerFry,
|
||
|
||
// ★ 象棋 / ChineseChess(2026-10-03 移植自捐赠分支 DRMK-PS_09_HyNB-龙栖村小游戏 R182/R183,
|
||
// 实现见 GameRouter.ChineseChess.cs + ChineseChessBoard.cs)。
|
||
// 客户端「进得去棋盘、开局后黑屏 / 能选子走不动」的根因是**服务端零推送**:
|
||
// · ChineseChessPlayer.lua:224 在「等对手单位」处 coroutine.yield(无超时 token),
|
||
// 而 OpponentAgentInstanceId 的唯一来源是 SyncGameGroundZoneInfo.ParticipantInfos
|
||
// (专用推送 SyncGameGroundZonePlayerInfo 被 `Pid == myPid` 过滤,对手永远进不去);
|
||
// · 回合翻转的唯一来源是 SyncGameGroundZoneTurnChange,客户端本地 MoveChessTo 不翻回合;
|
||
// · AI 不在客户端(ChineseChessSearchChart.lua 零调用点)⇒ 应手只能服务端算。
|
||
// 返回契约已核:5 个入口 RET 全 void(兜底表里没有它们的 case);
|
||
// QueryChineseChessPlayerInfo 兜底 body 19B = complex(681: dict32+dict32+u32+u32),
|
||
// 与 ChineseChessPlayerInfo 的字段布局逐段吻合(见 DefaultReturnCatalog4229938.cs:…67492072)。
|
||
// ⛑ 回滚:删掉下面 6 行即可(handler 不注册 = 走 typed-default 兜底),不用删 ChineseChess*.cs。
|
||
MethodId.AskChineseChessEnterZoneDoubleAI, // 67307903 标准人机对局
|
||
MethodId.AskChineseChessEnterZoneEndGame, // 67616104 残局挑战
|
||
MethodId.AskChineseChessLeaveZone, // 67667163 离开
|
||
MethodId.QueryChineseChessPlayerInfo, // 67492072 开始面板要的残局/难度进度
|
||
MethodId.AskChineseChessMovePiece, // 67463405 落子 → 服务端算 AI 应手
|
||
MethodId.AskChineseChessSurrender, // 67452562 认输 → 推 GameOver
|
||
|
||
// ★ 麻将 / Majiang(2026-10-04 移植自捐赠分支 DRMK-PS_09_HyNB-v1.9-R64,
|
||
// 实现见 GameRouter.MahjongSolo.cs + MahjongRules4229938.cs(规则引擎零改动直拷))。
|
||
// 10-03 曾判「重活、建议单独排期」,10-04 重估推翻:该分支带 41KB 实机 wire 样本
|
||
// (.runtime/r62-mahjong-wire-samples.json),而样本里三个 NPC 座位就是
|
||
// Player101/102/103 ⇒ 单机形态(1 真人 + 3 NPC)是官方跑过的形态。
|
||
// 主线 MethodIds.json 与该分支字节级相同 ⇒ SyncMj* 常量本来就全齐。
|
||
// ⛑ 回滚:删掉下面 12 行即可(handler 不注册 = 走 typed-default 兜底)。
|
||
MethodId.AskStartMahjongGame, // 63184671 开局入口(Npc / Pve 房型)
|
||
MethodId.MahjongPlayerReady,
|
||
MethodId.ArrangeMjHolds,
|
||
MethodId.HuanPai,
|
||
MethodId.DingQue,
|
||
MethodId.ChuPai,
|
||
MethodId.Peng,
|
||
MethodId.Gang,
|
||
MethodId.Hu,
|
||
MethodId.Guo,
|
||
MethodId.TuoGuan,
|
||
MethodId.BackToMahjong,
|
||
|
||
MethodId.EnterBowlingZone,
|
||
MethodId.GmEnterBowlingZone,
|
||
MethodId.LeaveBowling,
|
||
MethodId.RecordBowlingScore,
|
||
MethodId.RecordAgentBowlingScore,
|
||
MethodId.RecordAllBowlingScoreAndDrop,
|
||
MethodId.AskBowlingRelease,
|
||
MethodId.AskAgentBowlingRelease,
|
||
MethodId.AskBowlingBuyTicket,
|
||
MethodId.BroadcastBowlingClientInfo,
|
||
MethodId.SetGameGroundPlayerReady,
|
||
MethodId.SetGameGroundPlayerPlayAgain,
|
||
MethodId.SetGameGroundPlayerSurrender,
|
||
MethodId.LeaveGameGroundZone,
|
||
MethodId.AskSimulationInviteNpc,
|
||
MethodId.AskGetNpcRandomWearFashions,
|
||
// 餐饮店(呼噜怪兽/猫咖、梦花园/女仆咖啡…)的「邀请伙伴」——**另一条**链,
|
||
// 走 ClientToGameSceneDelegate 发 AskRestaurantInviteNpc(RestaurantConfig.Id),且是 NOTIFY。
|
||
// 修之前服务端报 `unimplemented notify AskRestaurantInviteNpc -> accepted`(只受理、不推送)
|
||
// ⇒ 客户端永远等不到 SyncNpcChatLeaveGameplay ⇒ 按钮按了没反应。详见 GameRouter.NpcInvite.cs。
|
||
// ⛑ 硬规则 94:不进这张表 = handler 编译通过但永不注册(NOTIFY 也一样,只是不会 await 卡死)。
|
||
// 本机铁证:logs\unknown-methods.log 2026-09-22T11:01:53 发过 1 条 id=67057178 hex=C2832F05
|
||
// (= 87000002 女仆屋),一直只受理没实现。回滚 = 删这一行。
|
||
MethodId.AskRestaurantInviteNpc,
|
||
// 「主题派对」开局 / 群聊邀请 / 结束(2026-09-29 自 v2.1 捐赠端移植,
|
||
// 见 GameRouter.NpcInvite.cs;硬规则 94:不进白名单 = 编译过但永不注册)。
|
||
MethodId.StartSingleParty,
|
||
MethodId.InviteMultiNpcChat,
|
||
MethodId.PartyOver,
|
||
|
||
// 飞镖 / DartsGame(2026-09-24,见 GameRouter.Darts.cs)。21:48:49~21:51:20 真机一局打完
|
||
// 只发过这三条(规则 112:按日志数排序,出现过才接):
|
||
// StartDarts 63777955 / TriggerDartTiming 67926798 / RecordDarts 63225711
|
||
// dump.cs 三条都是 void ⇒ 空包 + err=0 即 OK。⚠ 只治 await,治不了 T-pose(动画在客户端
|
||
// Gameplay_Dart timeline 里,服务端插不上手)。
|
||
MethodId.StartDarts,
|
||
MethodId.TriggerDartTiming,
|
||
MethodId.RecordDarts,
|
||
|
||
// 眼界 (Tuite)(2026-09-24,见 GameRouter.Tuite.cs)。列表本身走登录帧的
|
||
// PlayerClientInfoMinor.PlayerTuiteInfo.TuiteList,这几条是「点开一条 + 点赞/收藏/关注/
|
||
// 评论」的交互半边。不进表 = handler 编译通过但永不注册,点进去就是空的详情页
|
||
// (六种列表函数全部只读 TuiteList,没有一条是拉列表的请求)。
|
||
MethodId.AskTuiteGetDetail,
|
||
MethodId.AskTuiteGetDetailByConfigId,
|
||
MethodId.AskInteractTuite,
|
||
MethodId.AskTuiteFollowRole,
|
||
MethodId.AskReportTuiteDetailOpened,
|
||
MethodId.AskTuiteGetCommentList,
|
||
MethodId.AskTuiteLikeComment,
|
||
MethodId.AskTuiteGetTimelineData,
|
||
MethodId.AskTuiteTakeReward,
|
||
|
||
// BBChat(2026-09-24,见 GameRouter.NpcChat.cs)。注意这不是玩家 IM —— 这个 app 里全是
|
||
// NPC 会话(红点来自 gRedDotUtils.CheckNpcChatHasRedDot())。会话列表走登录帧的
|
||
// InfoNpcCultivation.NpcChats / NpcGroupChats,这几条是「点开一条之后推进对话」的交互半边。
|
||
// 全部请求体只有一个 u32,客户端也没有为它们声明任何回包 reader ⇒ 只能回空 OK。
|
||
MethodId.InteractNpcChat,
|
||
MethodId.InteractNpcChatToEnd,
|
||
MethodId.ChatToNpc,
|
||
MethodId.InviteNpcChat,
|
||
MethodId.AskMarkNpcChatRead,
|
||
MethodId.RequestNpcChatList,
|
||
MethodId.AskCloseNpcChatWnd,
|
||
MethodId.AskClearSpiritGroupChat,
|
||
MethodId.AskClearNpcUncompletedInviteChat,
|
||
|
||
// 骰子 / DiceGame(2026-09-27 移植自 DRMK-PS_09_HyNB-v1.9,见 GameRouter.Dice.cs)。
|
||
// 酒吧「午夜回响」吧台玩法。★ 硬门是 AskEnterDiceZoneDoubleAI(67898934) 的返回值 —— 契约是
|
||
// UXRPCTask<ulong>,必须回一个**世界里真实存在的实体 uid**(造 NPC),回 0 或编造的 uid
|
||
// 客户端都卡在「AgentId为0 / 等待 Agent 实体同步」。另外三条是 void,空包 + err=0 即可。
|
||
// ⛑ 回滚:把这四行删掉即可(handler 不注册 = 走 typed-neutral 兜底),不用删 Dice.cs。
|
||
MethodId.AskEnterDiceZoneDoubleAI,
|
||
MethodId.AskDicePlayerAction,
|
||
MethodId.AskLeaveDiceZone,
|
||
MethodId.AskReportDiceBehavior,
|
||
|
||
// 钓鱼 / FishingGame(2026-09-27,见 GameRouter.Fishing.cs)。
|
||
// ★ 硬门是 AskFishingSpotFullSync(63275440) 的回包 —— 契约是
|
||
// UXRPCTask<FishingSpotFullSyncInfo>,且 **Fishes 必须非空**:
|
||
// FishingGame.lua:56 在 poolCount == 0 时直接 CleanGame()(反编译写成 ~= 0,语义是 == 0),
|
||
// 空池 = 刚开局就被关掉。AskFishingSuccess(63577896) 是 void,空包 + err=0 即可。
|
||
// ⛑ 回滚:把这两行删掉即可(handler 不注册 = 走 typed-neutral 兜底),不用删 Fishing.cs。
|
||
MethodId.AskFishingSpotFullSync,
|
||
MethodId.AskFishingSuccess,
|
||
|
||
// 房产 / House(2026-09-30,见 GameRouter.House.cs)。
|
||
// 只有一条需要注册:AskLoadHouseData(63109556) 的契约是
|
||
// UXRPCTask<HouseBuildingClientData>(复杂返回),兜底表那份 57B 里 OwnerPid 是 0,
|
||
// 我们要换成自己的 pid。**购买本身不需要新方法** —— 客户端复用的是
|
||
// AskNpcShopCommodityInfo / AskBuyCommodity(见 GameRouter.VehicleTuning.cs)。
|
||
// ⛑ 回滚:删掉这一行即可(回到兜底表的 OwnerPid=0 版本)。
|
||
MethodId.AskLoadHouseData,
|
||
|
||
// 车库 / 停车(2026-09-30,见 GameRouter.Garage.cs)。
|
||
// 这一族 9 条在兜底表里全是 shape="void"(空 body),之前一个 handler 都没注册 ⇒
|
||
// 客户端「调取载具 / 驶离车库」整条链路断掉。9 条全部注册:
|
||
// AskHouseParking / AskHouseMoveParkingSpace —— 记账 + 回推 SyncHouseParking
|
||
// AskCreateHouseParkingVehicles —— spawn 车 + 回推 SyncHouseParkingEntityBinding(命门)
|
||
// AskDestroyHouseParkingVehicles / AskTeleportToHouseGarage / AskTeleportFromHouseGarage
|
||
// AskHouseCancelParking / GmHouseParking / GmHouseCancelParking
|
||
// ⛑ 回滚:删这 9 行即可(handler 不注册 = 回到空 body 兜底,与改动前逐字节一致)。
|
||
MethodId.AskHouseParking,
|
||
MethodId.AskHouseMoveParkingSpace,
|
||
MethodId.AskHouseCancelParking,
|
||
MethodId.AskTeleportToHouseGarage,
|
||
MethodId.AskTeleportFromHouseGarage,
|
||
MethodId.AskCreateHouseParkingVehicles,
|
||
MethodId.AskDestroyHouseParkingVehicles,
|
||
MethodId.GmHouseParking,
|
||
MethodId.GmHouseCancelParking,
|
||
|
||
// 家具机关 / 可互动家具(2026-10-01,见 GameRouter.HouseFurnitureGadget.cs)。
|
||
// 家具**模型**早就有了(AskLoadHouseData 的 FloorBuildInfoDict),但客户端建完模型
|
||
// 还会再问一次服务端要「机关实例 id」,这两条在兜底表里是
|
||
// case 63581446u: body=[0xFF,0x01] shape="struct(631:dict7-empty)" ← 恒空映射
|
||
// case 63759452u: body=[] shape="void"
|
||
// ⇒ 椅子坐不了 / 衣柜开不了 / 电视点不亮。两条都注册。
|
||
// ⛑ 回滚:删这两行即可(回到恒空映射兜底,与改动前逐字节一致);
|
||
// 或保留注册但把 gameplay.houseFurniture.gadgets 设为 false(热重载)。
|
||
MethodId.AskCreateHouseFurnitureGadgets,
|
||
MethodId.AskDestoryHouseFurnitureGadgets,
|
||
// 2026-09-29 自捐赠端 v1.9 整体移植(GameRouter.Fishing.cs 现在有 12 个 handler):
|
||
// 不进这张表 = 编译通过但 handler 永不注册,GM 命令点了没反应、钓点永远刷不出鱼池。
|
||
// 这 12 个全是 C2S INVOKE handler;IGameToClient_SyncFishing* 是 S2C 通知目标、不在此表。
|
||
MethodId.GmAddFishingGear,
|
||
MethodId.GmRemoveFishingGear,
|
||
MethodId.GmInitFishingSpot,
|
||
MethodId.GmSpawnFishingSpotFish,
|
||
MethodId.GmResetAllFishingSpots,
|
||
MethodId.GmClearFishing,
|
||
MethodId.GmAddAllFishes,
|
||
MethodId.GmOnFishingSuccess,
|
||
MethodId.GmViewFishingSpot,
|
||
MethodId.GmViewFishingInfo,
|
||
// 2026-09-30 厕所 QTE / 马桶鸵鸟骑乘(GameRouter.ToiletQte.cs)。
|
||
// ⛑ 规则 94:不进这张表 = 编译通过但 handler 永不注册。
|
||
// AskRideAgent / AskStopRideAgent 是 C2S INVOKE(回包必须 0 字节);
|
||
// AskToiletOstrichFlee 是 C2S **Notify**(QTE 失败时客户端上报)——
|
||
// 它是 Notify 也一样要进这张表,否则只会落进 DefaultUnknownNotify,服务端永远收不到。
|
||
MethodId.AskRideAgent,
|
||
MethodId.AskStopRideAgent,
|
||
|
||
// ★ 赛车 / Racing(2026-10-03 移植自捐赠分支 DRMK-PS_09_HyNB-赛车)。
|
||
// 「王牌车神·城中之夜」(ChallengeConfig 1003 → RelatedTask 60000147, raid 23300888)。
|
||
// 客户端点「开始挑战」只在本地 SetCurrentTask,服务端必须 (a) 记住当前任务并
|
||
// (b) 由 ScheduleRacingAutoStart 点火 4 拍 zone 包 + 检查点门 + 结算。
|
||
MethodId.AskAcceptAndSetCurrentTask, // 63961538 接受并设当前任务 → 赛车自启点火
|
||
// 赛事车辆 / 检查点计数上报(GameRouter.RacingVehicle.cs):
|
||
// 不注册时落 typed-default 回 err=0 假 OK,服务端对赛道/车/进度一无所知。
|
||
MethodId.AskSetRacingCompetitionVehicle, // 63764162 赛事车辆(越过职业门后才收到)
|
||
MethodId.AskSetTaskCounterValue, // 63753212 检查点计数上报(= 完赛判据)
|
||
// ⛑ 不注册 StartNewChallenge(63092905) / SetNewChallengeData(63990449) /
|
||
// FinishNewChallenge(63261145):主线 GameRouter.ChallengeRecords.cs 已接后两条,
|
||
// 第一条兜底是 void 空包与 ack 字节等价;而 Finish 的回包主线兜底 129 字节才对
|
||
// (捐赠端写成空类 ChallengeResult 只回 1 字节,是错的 —— 硬规则 209)。
|
||
// ⛑ 回滚:删掉上面 3 行即可(handler 不注册 = 回到中性兜底),Racing*.cs 保留无害。
|
||
|
||
// ★ 音乐 / 演出线三条玩法(2026-10-04,见 GameRouter.LiveHouse.cs)。
|
||
// 接之前这些 id 全部落在 DefaultReturnCatalog 的空包兜底 ⇒ 面板能开、点得动,
|
||
// 但(a)曲目成绩重进清零(b)首次引导永远不播(c)舞会点了没反应(d)直播间全空。
|
||
// ⛑ 回滚:删掉下面这 11 行即可回到中性兜底,GameRouter.LiveHouse.cs 留着无害。
|
||
//
|
||
// —— 1. Livehouse 音游 ——
|
||
MethodId.AskQueryLiveHouseInfo, // 63848409 首次游玩标记(决定 intro timeline)
|
||
MethodId.AskLiveHouseMusicList, // 63885626 曲目进度列表
|
||
MethodId.SetLiveHouseMusicResult, // 63723141 成绩落盘
|
||
MethodId.LiveHouseMusicStart, // 63293678 开局 notify
|
||
MethodId.LiveHouseMusicInterrupt, // 63752208 中断 notify
|
||
MethodId.AskLiveHouseUseItem, // 63665380 金手指道具(放行,不重复扣)
|
||
//
|
||
// —— 2. PartyDance 舞池(三连 song → mode → start + 收摊)——
|
||
MethodId.AskSelectPartyDanceSong, // 63363245
|
||
MethodId.AskSelectPartyDanceMode, // 63715994
|
||
MethodId.AskStartPartyDance, // 63292540 ★ 推 SyncPartyDanceStart 的唯一点
|
||
MethodId.AskLeavePartyDance, // 63516846 推 SyncPartyDanceEnd
|
||
//
|
||
// —— 3. PartyLive 派对直播 ——
|
||
MethodId.PartyLiveSwitch, // 63948406 开/关直播
|
||
MethodId.SendPartyEvent, // 63765017 点赞/送礼事件
|
||
MethodId.GmPartyLiveAddEvent, // 65785804 GM 推一条弹幕
|
||
|
||
MethodId.AskToiletOstrichFlee,
|
||
};
|
||
|
||
internal RpcRouter Build()
|
||
{
|
||
var router = new RpcRouter("game");
|
||
MethodId.RegisterKnownNames(router);
|
||
_ = AttributedHandlerRegistry.RegisterSelected(router, this, Enabled4229938MethodIds);
|
||
router.OnUnknownInvoke(DefaultUnknownInvoke4229938);
|
||
router.OnUnknownNotify(DefaultUnknownNotify4229938);
|
||
return router;
|
||
}
|
||
}
|