diff --git a/client/index.html b/client/index.html
index c7c9747..49d1687 100644
--- a/client/index.html
+++ b/client/index.html
@@ -236,7 +236,24 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/js/01_SubGame/codes/SubGameHooks.js b/client/js/01_SubGame/codes/SubGameHooks.js
index ccdcb22..d0ed491 100644
--- a/client/js/01_SubGame/codes/SubGameHooks.js
+++ b/client/js/01_SubGame/codes/SubGameHooks.js
@@ -246,5 +246,50 @@ SubGameHooks.gamemydraw = function (gameid, spid, times, timelong, no2, no3, no4
// [D] gameui_stop_voice(uiIndex):停止桌面语音气泡。
// SubGameHooks.gameui_stop_voice = function (uiIndex) { /* ... */ };
+//—— 网络与对局(B 阶段接线)——
+//平台入口只解包转交,业务全在 net/ 的 handler 里。
+
+// 对局推送。_msg 是完整包 {app, route, rpc, data}
+// (平台 12_Logic.js 按 route 分流,只有子游戏路由 erqiwang 才到这里)
+SubGameHooks._ReceiveData = function (_msg) {
+ if (!_msg) { return; }
+ EQW_Dispatcher.dispatch({ rpc: _msg.rpc, data: _msg.data });
+};
+
+// 开局。可能差异化下发(sendtype:1 + seatlist[])
+SubGameHooks.StartWar = function (_msg) {
+ EQW_ResyncHandler.handleStartWar(_msg);
+};
+
+// 断线重连:拿到 get_deskinfo 的全量快照
+SubGameHooks.Reconnect = function (_deskinfo) {
+ EQW_ResyncHandler.handleDeskinfo(_deskinfo);
+};
+
+// 重连但未开战:没有牌局数据,无需重画对局
+SubGameHooks.ReconnectNoMakewar = function () {
+ console.log('[EQW] 重连(未开战)');
+};
+
+// 解散。_msg 即 Desk.deskfree,可能为 null(首局发牌前解散)
+SubGameHooks.Free = function (_msg) {
+ EQW_ResultHandler.handleFree(_msg);
+};
+
+// 房间信息(房号 / 总局数 / roomtype 位串)
+SubGameHooks.setRoomDes = function (roomcode, asetcount, roomtype) {
+ EQW_ResyncHandler.handleSetRoomDes(roomcode, asetcount, roomtype);
+};
+
+// 启动编排。B 阶段只记下自己的座位;UI 初始化留给 C 阶段
+SubGameHooks.appStart = function () {
+ if (typeof C_Player !== 'undefined' && typeof C_Player.seat === 'number') {
+ EQW_GameState.room.mySeat = C_Player.seat;
+ }
+ if (typeof Desk !== 'undefined' && Desk.roomtype) {
+ EQW_GameState.room.options = EQW_RoomOptions_Parse.parse(Desk.roomtype);
+ }
+};
+
window.SubGameHooks = SubGameHooks;
if (typeof module !== 'undefined' && module.exports) { module.exports = SubGameHooks; }
diff --git a/client/tests/test_hooks.js b/client/tests/test_hooks.js
new file mode 100644
index 0000000..6088219
--- /dev/null
+++ b/client/tests/test_hooks.js
@@ -0,0 +1,53 @@
+// SubGameHooks 接线:平台入口 → 新架构
+const { load } = require('./_load');
+const t = require('./_assert')();
+
+global.window = global; // 转发壳用 window.SubGameHooks 判定
+
+load('client/js/gameabc-framework/system/EventBus.js');
+load('client/js/gameabc-framework/system/SpriteEventController.js');
+load('client/js/01_SubGame/codes/state/Events.js');
+load('client/js/01_SubGame/codes/state/GameState.js');
+load('client/js/01_SubGame/codes/state/RoomOptions.js');
+['DealHandler','CallHandler','MainHandler','BuryHandler','PlayHandler',
+ 'QueryHandler','ReadyHandler','ResultHandler','ResyncHandler','FailHandler']
+ .forEach(h => load('client/js/01_SubGame/codes/net/handlers/' + h + '.js'));
+load('client/js/01_SubGame/codes/net/Dispatcher.js');
+load('client/js/01_SubGame/codes/SubGameHooks.js');
+
+const S = EQW_GameState;
+
+// ---- _ReceiveData:完整包 → 分发 ----
+S.reset(); S.room.mySeat = 0;
+SubGameHooks._ReceiveData({ app: 'youle', route: 'erqiwang', rpc: 'fapai',
+ data: { success: true, asetidx: 1, asetcount: 6,
+ cards: [1,2], seat: 0, countdown: 15 } });
+t.eq('_ReceiveData 分发到 handler', S.my.cards, [1,2]);
+
+// ---- setRoomDes ----
+SubGameHooks.setRoomDes(123, 12, '00010');
+t.eq('setRoomDes 解析选项', S.room.options.climb, 1);
+
+// ---- Free:null 不崩 ----
+let boom = false;
+try { SubGameHooks.Free(null); } catch (e) { boom = true; }
+t.eq('Free(null) 不崩', boom, false);
+
+// ---- Reconnect ----
+S.reset(); S.room.mySeat = 1;
+SubGameHooks.Reconnect({ count: 6, idx: 3, PlayerInfo: [0,0,0], step: 1, MyCards: [5,6] });
+t.eq('Reconnect 填状态', S.my.cards, [5,6]);
+t.eq('Reconnect 填局数', S.room.asetIdx, 3);
+
+// ---- 畸形入参一律不崩 ----
+boom = false;
+try {
+ SubGameHooks._ReceiveData(null);
+ SubGameHooks._ReceiveData({});
+ SubGameHooks.StartWar(null);
+ SubGameHooks.Reconnect(null);
+ SubGameHooks.setRoomDes();
+} catch (e) { boom = true; }
+t.eq('畸形入参不崩', boom, false);
+
+process.exit(t.done('hooks') ? 0 : 1);
diff --git a/client/tests/test_index_load_order.js b/client/tests/test_index_load_order.js
new file mode 100644
index 0000000..c347d9f
--- /dev/null
+++ b/client/tests/test_index_load_order.js
@@ -0,0 +1,109 @@
+// index.html 加载顺序自检(task-13 步骤6守卫):
+// 按 index.html 里