feat(session): 生成下行动作映射表并接入会话路由
This commit is contained in:
1 parent
780373180d
commit
87539a9645
6 files changed
+1201
No files matched your search
@@ -463,6 +463,19 @@
|
||||
`tools/RunSessionTest.ps1`:8000 个 s2c 文件回放,**1338 条记录全部回编码一致**。
|
||||
- 下一步:动作 ↔ 协议方法映射表(`ProtocolMetadata` 里的名字仍是 `UserPropertiesCCxxxx` 这类混淆名,
|
||||
需要从 module_18334 的 command / DI 装配里抽语义)、浏览器 WebSocket 传输与大厅渲染。
|
||||
- 18334 与下行映射(本轮进展):
|
||||
- 18334 实际只做 DI 装配与 UI 动作派发(509 处 `uar(new …)`),**协议下行 → 动作**的转换不在里面;
|
||||
转换代码与动作类同处 module_10032(动作直接持有 CC,例如 `SetUserPropertiesAction(user: UserPropertiesCC)`)。
|
||||
- 新增 `tools/gen_downlink_actions.js`:`method_table.json` → `model_binding.json` → `codec_fields.json`
|
||||
的 `ModelName`,再与 `Inventory.kt` 的「单字段动作」对齐,生成
|
||||
`names/downlink_actions.json` 与 `App/State/DownlinkActions.kt`。
|
||||
覆盖:681 个 methodId 中 **477 个能解析出协议模型名**,其中 **13 个 methodId 自动匹配到动作**
|
||||
(`UserPropertiesCC`×9 → `SetUserPropertiesAction`、`ReferralEventUserModelCC`×3、`GaragePreviewModelCC`×1)。
|
||||
- `App/Session/SessionRouter.kt`:解码记录 → `DownlinkActions`(或 Pt 容器的 `DownlinkModelActions`)→ Store。
|
||||
- 已知缺口:抓包里的 Pt 容器 item id(如 `2114179554921095892`)在 `model_binding.json` 里查不到,
|
||||
说明现有 modelId→codec 绑定只覆盖 363 个模型;需要补「modelId → 模块/类」的运行时注册表,
|
||||
才能把大厅容器的模型路由进状态层。当前 `RunSessionTest.ps1` 只断言「会话解码 + 回编码」,
|
||||
路由计数(已派发/未覆盖)作为诊断输出。
|
||||
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
||||
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
||||
`launchParams` 是纯透传(`(pass-through)`,无 reducer),已把字段收口为 `Map<String, String>`。
|
||||
|
||||
@@ -0,0 +1,912 @@
|
||||
{
|
||||
"Note": "下行 methodId → 协议模型 → 客户端动作。模型名来自 codec_fields.json 的 ModelName;动作来自 Inventory.kt 里“只有一个字段且字段类型等于该模型”的 data class。未匹配的模型在 Models 里保留候选,留给后续手工对照(多为 CC → 多个基本字段的转换动作)。",
|
||||
"Source": [
|
||||
"method_table.json",
|
||||
"model_binding.json",
|
||||
"codec_fields.json",
|
||||
"Inventory.kt"
|
||||
],
|
||||
"Methods": 681,
|
||||
"Resolved": 477,
|
||||
"Matched": 13,
|
||||
"MatchedModels": [
|
||||
{
|
||||
"Model": "GaragePreviewModelCC",
|
||||
"Action": "InitGarageResources",
|
||||
"Field": "params"
|
||||
},
|
||||
{
|
||||
"Model": "ReferralEventUserModelCC",
|
||||
"Action": "SetReferralEventData",
|
||||
"Field": "eventData"
|
||||
},
|
||||
{
|
||||
"Model": "UserPropertiesCC",
|
||||
"Action": "SetUserPropertiesAction",
|
||||
"Field": "user"
|
||||
}
|
||||
],
|
||||
"Models": [
|
||||
{
|
||||
"Model": "AndroidPayCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "AntiAddictionRealNameValidatorModelCC",
|
||||
"Methods": 6,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "AppleAppStoreCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "AresUltimateProjectileCC",
|
||||
"Methods": 4,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ArmsRaceCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ArtilleryElevatingBarrelCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BattleBotControlCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BattleChatCC",
|
||||
"Methods": 5,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BattleCreateCC",
|
||||
"Methods": 6,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BattleDMInfoCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BattleGearScoreCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BattleGoldBonusCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BattleInfoCC",
|
||||
"Methods": 7,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BattleInviteCC",
|
||||
"Methods": 8,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BattleMineCC",
|
||||
"Methods": 6,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BattlePassPurchaseNotifierCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BattleStartDelayCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BattleTutorialCC",
|
||||
"Methods": 4,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BattleUltimateRadarCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BattlefieldBonusesCC",
|
||||
"Methods": 6,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BattlefieldCC",
|
||||
"Methods": 4,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BonkWeaponCC",
|
||||
"Methods": 4,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BossStateCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "BotNavigationCC",
|
||||
"Methods": 4,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "CaptchaCC",
|
||||
"Methods": 4,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ChallengeClientCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ChatCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ClanChatCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ClanCreateCC",
|
||||
"Methods": 8,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ClanFriendsCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ClanIncomingCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ClanMembersCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ClanNotifierData",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ClanOutgoingCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ClanPanelNotificationCC",
|
||||
"Methods": 4,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ClanPermissionsCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ClanUserCC",
|
||||
"Methods": 15,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "CoinInfoCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "CommonFacilityCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "CompositePartnerCC",
|
||||
"Methods": 11,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ContainerCC",
|
||||
"Methods": 16,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ContextTutorialModelCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ContractUserModelCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "CrisisEffectCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "CrusaderUltimateCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "DamageIndicatorCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "DebtInfoCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "DeferredOverdriveCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "DemomanSfxCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "DictatorUltimateCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "DominationCC",
|
||||
"Methods": 8,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "DonationAlertCC",
|
||||
"Methods": 5,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "DroneIndicatorCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "EffectDescriptionCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ExternalEntranceCC",
|
||||
"Methods": 8,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "FacebookPaymentCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "FlameThrowerCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "FreezeCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "FriendsCC",
|
||||
"Methods": 8,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "GaragePreviewModelCC",
|
||||
"Methods": 1,
|
||||
"Actions": [
|
||||
"InitGarageResources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Model": "GaussCC",
|
||||
"Methods": 5,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "GlobeLightningCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "GoldBonusCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "GrenadeCC",
|
||||
"Methods": 8,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "HopperUltimateCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "HornetUltimateCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "HoverChassisCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "HunterUltimateCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "InventoryItemCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "InviteEntranceCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "IsisCC",
|
||||
"Methods": 5,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "JuggernautCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "JuggernautUltimateCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "KeysInfoCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "KillStreakCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "LaserPointerCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "LicenseClanUserCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "MachineGunCC",
|
||||
"Methods": 6,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "MainQuestUserModelCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "MammothUltimateCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "MatchmakingGroupCC",
|
||||
"Methods": 5,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "MatchmakingLayoutCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "MeteorStormCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ModerationToolInteropCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "MountedResistancesCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "NewsShowingCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "NotificationsByCountryModelCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "NuclearRechargeCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "PaidRenameCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "PaladinAuraVisualCC",
|
||||
"Methods": 4,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "PayGardenPaymentCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "PayPalPayModelCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "PeriodicRewardCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "PointBasedBattleCC",
|
||||
"Methods": 7,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "PremiumNotifierCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "RailgunCC",
|
||||
"Methods": 5,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "RankNotifierData",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ReferralEventUserModelCC",
|
||||
"Methods": 3,
|
||||
"Actions": [
|
||||
"SetReferralEventData"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Model": "RegistrationModelCC",
|
||||
"Methods": 7,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "RemoteTankStateUpdatingCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "RicochetCC",
|
||||
"Methods": 4,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "RotatingTurretCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "RuStorePayCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "RubyInfoCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ScorpioCC",
|
||||
"Methods": 4,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ServerHaltCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ServerHaltEntranceCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ShaftCC",
|
||||
"Methods": 4,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ShotCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ShotgunShotCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ShowcaseViewCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "SmokyCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "SpectatorsModelCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "SpeedCharacteristicsCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "StarsInfoCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "StatisticsDMCC",
|
||||
"Methods": 4,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "StatisticsModelCC",
|
||||
"Methods": 8,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "StatisticsTeamCC",
|
||||
"Methods": 6,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "StatusEffectCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "StreamWeaponCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "SuicideCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "SuppliesTutorialModelCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TankCC",
|
||||
"Methods": 6,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TankCritCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TankEffectSFXCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TankHasGrenadeCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TankPointersCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TankResistancesCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TankSpawnerCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TankStunCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TankSuperchargeCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TeamBattleInfoCC",
|
||||
"Methods": 5,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TeamJuggernautCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TerminatorCC",
|
||||
"Methods": 5,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TeslaBazookaCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TeslaCC",
|
||||
"Methods": 5,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "ThunderCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TitanUltimateGeneratorCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TrackedChassisCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TsunamiCC",
|
||||
"Methods": 3,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TwinsCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TwitchAccountModelCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "TwoStepVerificationModelCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "UidCheckCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "UidNotifierData",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "UltimateCC",
|
||||
"Methods": 5,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "UpgradeGarageItemCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "UserAccountRemovalModelCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "UserAchievementCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "UserActionsLoggerCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "UserContainerCC",
|
||||
"Methods": 8,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "UserEmailCC",
|
||||
"Methods": 10,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "UserPropertiesCC",
|
||||
"Methods": 9,
|
||||
"Actions": [
|
||||
"SetUserPropertiesAction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Model": "UserReportsModelCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "VikingUltimateCC",
|
||||
"Methods": 2,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "WaspUltimateBombCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "WeaponCommonCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "WeaponWeakeningCC",
|
||||
"Methods": 1,
|
||||
"Actions": []
|
||||
},
|
||||
{
|
||||
"Model": "at",
|
||||
"Methods": 4,
|
||||
"Actions": []
|
||||
}
|
||||
],
|
||||
"Actions": [
|
||||
{
|
||||
"MethodId": "8836555743203700004",
|
||||
"Model": "GaragePreviewModelCC",
|
||||
"Action": "InitGarageResources",
|
||||
"Field": "params"
|
||||
},
|
||||
{
|
||||
"MethodId": "2940606982196178690",
|
||||
"Model": "ReferralEventUserModelCC",
|
||||
"Action": "SetReferralEventData",
|
||||
"Field": "eventData"
|
||||
},
|
||||
{
|
||||
"MethodId": "2940608322101861340",
|
||||
"Model": "ReferralEventUserModelCC",
|
||||
"Action": "SetReferralEventData",
|
||||
"Field": "eventData"
|
||||
},
|
||||
{
|
||||
"MethodId": "1880027114297758713",
|
||||
"Model": "ReferralEventUserModelCC",
|
||||
"Action": "SetReferralEventData",
|
||||
"Field": "eventData"
|
||||
},
|
||||
{
|
||||
"MethodId": "276076212290602640",
|
||||
"Model": "UserPropertiesCC",
|
||||
"Action": "SetUserPropertiesAction",
|
||||
"Field": "user"
|
||||
},
|
||||
{
|
||||
"MethodId": "7864195052961084235",
|
||||
"Model": "UserPropertiesCC",
|
||||
"Action": "SetUserPropertiesAction",
|
||||
"Field": "user"
|
||||
},
|
||||
{
|
||||
"MethodId": "3982372259754314184",
|
||||
"Model": "UserPropertiesCC",
|
||||
"Action": "SetUserPropertiesAction",
|
||||
"Field": "user"
|
||||
},
|
||||
{
|
||||
"MethodId": "6492680006777979140",
|
||||
"Model": "UserPropertiesCC",
|
||||
"Action": "SetUserPropertiesAction",
|
||||
"Field": "user"
|
||||
},
|
||||
{
|
||||
"MethodId": "5628691596950015452",
|
||||
"Model": "UserPropertiesCC",
|
||||
"Action": "SetUserPropertiesAction",
|
||||
"Field": "user"
|
||||
},
|
||||
{
|
||||
"MethodId": "7864034006236084309",
|
||||
"Model": "UserPropertiesCC",
|
||||
"Action": "SetUserPropertiesAction",
|
||||
"Field": "user"
|
||||
},
|
||||
{
|
||||
"MethodId": "3977381235093458189",
|
||||
"Model": "UserPropertiesCC",
|
||||
"Action": "SetUserPropertiesAction",
|
||||
"Field": "user"
|
||||
},
|
||||
{
|
||||
"MethodId": "848734776772436751",
|
||||
"Model": "UserPropertiesCC",
|
||||
"Action": "SetUserPropertiesAction",
|
||||
"Field": "user"
|
||||
},
|
||||
{
|
||||
"MethodId": "8468360636859619961",
|
||||
"Model": "UserPropertiesCC",
|
||||
"Action": "SetUserPropertiesAction",
|
||||
"Field": "user"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
// gen_downlink_actions.js —— 生成「下行 methodId → 协议模型 → 客户端动作」表
|
||||
//
|
||||
// 数据源(都不猜,全部来自既有产物):
|
||||
// 1. names/method_table.json methodId → modelId / codec
|
||||
// 2. names/model_binding.json modelId → module::codec
|
||||
// 3. names/codec_fields.json module::codec → ModelName(协议模型名,例如 UserPropertiesCC)
|
||||
// 4. TankiOffline/.../Inventory.kt 动作类字段表(`data class Xxx(...)`)
|
||||
//
|
||||
// 输出:
|
||||
// names/downlink_actions.json { Note, Resolved, Matched, Models: [...], Actions: [...] }
|
||||
// TankiOffline/.../App/State/DownlinkActions.kt
|
||||
// val DownlinkActions: Map<Long, (Any) -> Any?>
|
||||
//
|
||||
// 用法:cd 3DTank/tools && node gen_downlink_actions.js
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const P = path.resolve(__dirname, '..');
|
||||
const Names = path.join(P, 'names');
|
||||
const KotlinRoot = path.resolve(P, '..', 'TankiOffline', 'app', 'src', 'jsMain', 'kotlin', 'App', 'State');
|
||||
|
||||
const MethodTable = JSON.parse(fs.readFileSync(path.join(Names, 'method_table.json'), 'utf8')).Rows;
|
||||
const CodecFields = JSON.parse(fs.readFileSync(path.join(Names, 'codec_fields.json'), 'utf8')).Codecs;
|
||||
const ModelBinding = JSON.parse(fs.readFileSync(path.join(Names, 'model_binding.json'), 'utf8')).Entries;
|
||||
const Inventory = fs.readFileSync(path.join(KotlinRoot, 'Inventory.kt'), 'utf8');
|
||||
|
||||
// ---------------------------------------------------------------- 模型名解析
|
||||
const ByModuleCodec = new Map();
|
||||
const ByCodec = new Map();
|
||||
for (const C of Object.values(CodecFields)) {
|
||||
if (!C.ModelName) continue;
|
||||
ByModuleCodec.set(C.Module + '::' + C.Codec, C.ModelName);
|
||||
if (!ByCodec.has(C.Codec)) ByCodec.set(C.Codec, C.ModelName);
|
||||
}
|
||||
const ModelIdToKey = new Map(ModelBinding.map((E) => [E.ModelId, E.Module + '::' + E.Codec]));
|
||||
|
||||
const Rows = [];
|
||||
for (const R of MethodTable) {
|
||||
const Key = ModelIdToKey.get(R.ModelId);
|
||||
const Model = (Key && ByModuleCodec.get(Key)) || ByCodec.get(R.Codec) || null;
|
||||
Rows.push({ MethodId: R.MethodId, ModelId: R.ModelId, Codec: R.Codec, Model });
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- 动作字段表
|
||||
/** 解析 Inventory.kt 的 data class:name → [{ field, type }] */
|
||||
function ParseActions(Text) {
|
||||
const Out = new Map();
|
||||
const Re = /data class (\w+)\(\n([\s\S]*?)\n\)/g;
|
||||
let M;
|
||||
while ((M = Re.exec(Text))) {
|
||||
const Fields = [];
|
||||
const FRe = /val (\w+): ([A-Za-z0-9_.]+)(<[^>]*>)?\??,/g;
|
||||
let F;
|
||||
while ((F = FRe.exec(M[2]))) Fields.push({ Field: F[1], Type: F[2], Generic: F[3] || '' });
|
||||
Out.set(M[1], Fields);
|
||||
}
|
||||
return Out;
|
||||
}
|
||||
|
||||
const Actions = ParseActions(Inventory);
|
||||
const ModelNames = new Set(Rows.map((R) => R.Model).filter(Boolean));
|
||||
|
||||
// 模型名 → 候选动作(字段类型恰为该模型,且动作只有这一个字段)
|
||||
const ActionByModel = new Map();
|
||||
for (const [Name, Fields] of Actions) {
|
||||
if (Fields.length !== 1) continue;
|
||||
const T = Fields[0].Type;
|
||||
if (!ModelNames.has(T)) continue;
|
||||
if (!ActionByModel.has(T)) ActionByModel.set(T, []);
|
||||
ActionByModel.get(T).push({ Action: Name, Field: Fields[0].Field });
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- 汇总
|
||||
const Matched = [];
|
||||
const MatchedModels = [];
|
||||
const Models = [];
|
||||
for (const Name of [...ModelNames].sort()) {
|
||||
const Cand = ActionByModel.get(Name) || [];
|
||||
const Methods = Rows.filter((R) => R.Model === Name).map((R) => R.MethodId);
|
||||
Models.push({ Model: Name, Methods: Methods.length, Actions: Cand.map((C) => C.Action) });
|
||||
if (Cand.length === 1) {
|
||||
for (const Id of Methods) {
|
||||
Matched.push({ MethodId: Id, Model: Name, Action: Cand[0].Action, Field: Cand[0].Field });
|
||||
}
|
||||
MatchedModels.push({ Model: Name, Action: Cand[0].Action, Field: Cand[0].Field });
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(Names, 'downlink_actions.json'),
|
||||
JSON.stringify(
|
||||
{
|
||||
Note:
|
||||
'下行 methodId → 协议模型 → 客户端动作。模型名来自 codec_fields.json 的 ModelName;' +
|
||||
'动作来自 Inventory.kt 里“只有一个字段且字段类型等于该模型”的 data class。' +
|
||||
'未匹配的模型在 Models 里保留候选,留给后续手工对照(多为 CC → 多个基本字段的转换动作)。',
|
||||
Source: ['method_table.json', 'model_binding.json', 'codec_fields.json', 'Inventory.kt'],
|
||||
Methods: Rows.length,
|
||||
Resolved: Rows.filter((R) => R.Model).length,
|
||||
Matched: Matched.length,
|
||||
MatchedModels,
|
||||
Models,
|
||||
Actions: Matched,
|
||||
},
|
||||
null,
|
||||
1,
|
||||
),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------- Kotlin 表
|
||||
const Lines = [];
|
||||
Lines.push('// DownlinkActions.kt —— 下行 methodId → 客户端动作(生成物,不要手改)');
|
||||
Lines.push('//');
|
||||
Lines.push('// 由 3DTank/tools/gen_downlink_actions.js 生成:');
|
||||
Lines.push('// methodId(method_table.json)→ modelId → module::codec(model_binding.json)');
|
||||
Lines.push('// → 协议模型名(codec_fields.json 的 ModelName)');
|
||||
Lines.push('// → Inventory.kt 里唯一一个「只带该模型字段」的动作。');
|
||||
Lines.push('//');
|
||||
Lines.push('// 覆盖范围:仅自动可判定的组合;其余协议模型需要 CC → 多字段的转换动作,');
|
||||
Lines.push('// 见 names/downlink_actions.json 的 Models 清单(后续按协议逐个补转换器)。');
|
||||
Lines.push('package App.State');
|
||||
Lines.push('');
|
||||
Lines.push('/** 每条记录解码后,用 (Any) -> Any? 构造对应动作;未覆盖的 methodId 不在表里。 */');
|
||||
Lines.push('val DownlinkActions: Map<Long, (Any) -> Any?> = mapOf(');
|
||||
for (const M of Matched) {
|
||||
Lines.push(
|
||||
' ' + M.MethodId + 'L to { Value -> ' + M.Action + '(Value as ' + M.Model + ') },',
|
||||
);
|
||||
}
|
||||
Lines.push(')');
|
||||
Lines.push('');
|
||||
Lines.push('/** Pt/Lt 容器里的 modelId → 动作(同一批协议模型的容器形态)。 */');
|
||||
Lines.push('val DownlinkModelActions: Map<Long, (Any) -> Any?> = mapOf(');
|
||||
for (const M of MatchedModels) {
|
||||
const Ids = ModelBinding.filter((E) => {
|
||||
const Key = E.Module + '::' + E.Codec;
|
||||
return ByModuleCodec.get(Key) === M.Model;
|
||||
}).map((E) => E.ModelId);
|
||||
for (const Id of Ids) {
|
||||
Lines.push(' ' + Id + 'L to { Value -> ' + M.Action + '(Value as ' + M.Model + ') },');
|
||||
}
|
||||
}
|
||||
Lines.push(')');
|
||||
Lines.push('');
|
||||
fs.writeFileSync(path.join(KotlinRoot, 'DownlinkActions.kt'), Lines.join('\n'), 'utf8');
|
||||
|
||||
console.log(
|
||||
`methodId ${Rows.length} 个,解析出协议模型 ${Rows.filter((R) => R.Model).length} 个;` +
|
||||
`自动匹配动作 ${Matched.length} 条,涉及模型 ${new Set(Matched.map((M) => M.Model)).size} 个。`,
|
||||
);
|
||||
@@ -71,6 +71,7 @@ object SessionCheck {
|
||||
println("抓包目录:$Root(${Files.size} 个 s2c 文件)")
|
||||
|
||||
val Session = SpaceSession()
|
||||
val Router = SessionRouter()
|
||||
var Frames = 0
|
||||
var Records = 0
|
||||
var Ok = 0
|
||||
@@ -95,9 +96,15 @@ object SessionCheck {
|
||||
Methods[R.MethodId] = (Methods[R.MethodId] ?: 0) + 1
|
||||
if (R.Payload.contentEquals(R.Reencoded)) Ok++ else Bad++
|
||||
}
|
||||
Router.RouteAll(List)
|
||||
}
|
||||
|
||||
println("文件 $Frames|记录 $Records|回编码一致 $Ok|不一致 $Bad|记录头截断 $HeaderErrors|其它错误 $Errors|别名表=${Session.AliasCount}")
|
||||
println("路由:已派发 ${Router.Applied}|未覆盖 ${Router.Unmapped}|失败 ${Router.Failed}|用户=${Router.Store.State.User.Uid}|屏幕=${Router.Store.State.Screen.Current}")
|
||||
val TopUnmapped = Router.UnmappedIds.entries.sortedByDescending { it.value }.take(6)
|
||||
if (TopUnmapped.isNotEmpty()) {
|
||||
println("未覆盖 id:" + TopUnmapped.joinToString(",") { "${it.key}×${it.value}" })
|
||||
}
|
||||
val Top = Methods.entries.sortedByDescending { it.value }.take(5)
|
||||
if (Top.isNotEmpty()) {
|
||||
println("高频 methodId:" + Top.joinToString(",") { "${it.key}×${it.value}" })
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// SessionRouter.kt —— 解码记录 → 客户端动作 → Store
|
||||
//
|
||||
// 映射表是生成物 `App.State.DownlinkActions`(methodId → (CC) -> Action);
|
||||
// 未覆盖的 methodId 计入 Unmapped,便于按覆盖清单逐步补转换器。
|
||||
package App.Session
|
||||
|
||||
import App.State.DownlinkActions
|
||||
import App.State.DownlinkModelActions
|
||||
import App.State.ReduceRoot
|
||||
import App.State.RootState
|
||||
import Net.Protocol.DecodedRecord
|
||||
import Net.Protocol.PtPayload
|
||||
import State.ReduxStore
|
||||
|
||||
class SessionRouter(InitialState: RootState = RootState()) {
|
||||
|
||||
val Store: ReduxStore<RootState> = ReduxStore(InitialState, ::ReduceRoot)
|
||||
|
||||
var Applied: Int = 0
|
||||
private set
|
||||
var Unmapped: Int = 0
|
||||
private set
|
||||
var Failed: Int = 0
|
||||
private set
|
||||
|
||||
/** 未覆盖的 id(methodId 或 Pt item modelId)计数,自检时打印头部。 */
|
||||
val UnmappedIds: MutableMap<Long, Int> = LinkedHashMap()
|
||||
|
||||
/** 已应用动作的类名(调试 / 自检用)。 */
|
||||
val AppliedNames: MutableList<String> = ArrayList()
|
||||
|
||||
/** 路由一条记录;返回实际派发的动作(未覆盖 / 失败时为 null)。 */
|
||||
fun Route(Record: DecodedRecord): Any? {
|
||||
// Pt 容器:里面的 Item.Id 是 modelId,按模型表路由
|
||||
val Value = Record.Value
|
||||
if (Value is PtPayload) {
|
||||
var Last: Any? = null
|
||||
for (Item in Value.Items) {
|
||||
val MakeModel = DownlinkModelActions[Item.Id]
|
||||
if (MakeModel == null) {
|
||||
Unmapped++
|
||||
UnmappedIds[Item.Id] = (UnmappedIds[Item.Id] ?: 0) + 1
|
||||
continue
|
||||
}
|
||||
try {
|
||||
val Action = MakeModel(Item.Value)
|
||||
if (Action != null) {
|
||||
Store.Dispatch(Action)
|
||||
Applied++
|
||||
AppliedNames.add(Action::class.simpleName ?: "?")
|
||||
Last = Action
|
||||
}
|
||||
} catch (E: Throwable) {
|
||||
Failed++
|
||||
}
|
||||
}
|
||||
return Last
|
||||
}
|
||||
val Make = DownlinkActions[Record.MethodId]
|
||||
if (Make == null) {
|
||||
Unmapped++
|
||||
UnmappedIds[Record.MethodId] = (UnmappedIds[Record.MethodId] ?: 0) + 1
|
||||
return null
|
||||
}
|
||||
return try {
|
||||
val Action = Make(Record.Value)
|
||||
if (Action != null) {
|
||||
Store.Dispatch(Action)
|
||||
Applied++
|
||||
AppliedNames.add(Action::class.simpleName ?: "?")
|
||||
}
|
||||
Action
|
||||
} catch (E: Throwable) {
|
||||
Failed++
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun RouteAll(Records: List<DecodedRecord>) {
|
||||
for (R in Records) Route(R)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// DownlinkActions.kt —— 下行 methodId → 客户端动作(生成物,不要手改)
|
||||
//
|
||||
// 由 3DTank/tools/gen_downlink_actions.js 生成:
|
||||
// methodId(method_table.json)→ modelId → module::codec(model_binding.json)
|
||||
// → 协议模型名(codec_fields.json 的 ModelName)
|
||||
// → Inventory.kt 里唯一一个「只带该模型字段」的动作。
|
||||
//
|
||||
// 覆盖范围:仅自动可判定的组合;其余协议模型需要 CC → 多字段的转换动作,
|
||||
// 见 names/downlink_actions.json 的 Models 清单(后续按协议逐个补转换器)。
|
||||
package App.State
|
||||
|
||||
/** 每条记录解码后,用 (Any) -> Any? 构造对应动作;未覆盖的 methodId 不在表里。 */
|
||||
val DownlinkActions: Map<Long, (Any) -> Any?> = mapOf(
|
||||
8836555743203700004L to { Value -> InitGarageResources(Value as GaragePreviewModelCC) },
|
||||
2940606982196178690L to { Value -> SetReferralEventData(Value as ReferralEventUserModelCC) },
|
||||
2940608322101861340L to { Value -> SetReferralEventData(Value as ReferralEventUserModelCC) },
|
||||
1880027114297758713L to { Value -> SetReferralEventData(Value as ReferralEventUserModelCC) },
|
||||
276076212290602640L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
7864195052961084235L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
3982372259754314184L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
6492680006777979140L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
5628691596950015452L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
7864034006236084309L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
3977381235093458189L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
848734776772436751L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
8468360636859619961L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
)
|
||||
|
||||
/** Pt/Lt 容器里的 modelId → 动作(同一批协议模型的容器形态)。 */
|
||||
val DownlinkModelActions: Map<Long, (Any) -> Any?> = mapOf(
|
||||
3730185794267514852L to { Value -> InitGarageResources(Value as GaragePreviewModelCC) },
|
||||
5167916989664044333L to { Value -> SetReferralEventData(Value as ReferralEventUserModelCC) },
|
||||
1893408944113965505L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
)
|
||||
Reference in new issue
Block a user