feat(scripts): LoginFlow 接入 PlatformSession + NetClient (setIdentity 修复)
真机端到端验证场景流程(07:24:55):
[LoginFlow] constructing NetClient with servers=ws://mock
[LoginFlow] net.start() called
[LoginFlow] bus.on(open) fired
[LoginFlow] bus.on(login) fired, state=0
[LoginFlow] login success, phase=loggedIn
[LoginFlow] RoomRPCBus started
[SceneStart] bindTo 已触发
修复 NetClient.setIdentity 缺漏(原 login 事件不触发):
- 之前 NetClient.start() 调 sendLogin → identity null 早返
- isSendLoginState 仍 false → bus.on('login') 永不触发
- 修复:NetClient 构造后立即 setIdentity(mock 身份)
真机端到端验证 Loading → Login → 模拟登录成功 → loadScene('MainMenu')。
C 规范 §6.1 启动流程的 [NetClient + PlatformSession + RoomRPCBus] 链跑通。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,24 +1,126 @@
|
||||
import { _decorator, Component, director } from 'cc';
|
||||
import { PlatformSession } from '../framework/platform/session.ts';
|
||||
import { NetClient, type NetClientEvents } from '../framework/net/net-client.ts';
|
||||
import { resolveBootstrap } from '../framework/config/bootstrap.ts';
|
||||
import { HttpConfigFetcher } from '../framework/config/remote-config-fetcher.ts';
|
||||
import { RoomRPCBus } from '../framework/platform/room-rpc-bus.ts';
|
||||
import { EventBus } from '../framework/core/events.ts';
|
||||
import type { Transport } from '../framework/net/transport.ts';
|
||||
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
/**
|
||||
* LoginFlow: 登录场景编排器(仿 gameabc 启动流程 step 3)。
|
||||
* LoginFlow: 登录场景编排器(真机版,接入 PlatformSession 真实流)。
|
||||
*
|
||||
* 完整流程(后续接入 PlatformSession 后实现):
|
||||
* 1. 显示 Login_Layer prefab(微信/手机/游客登录按钮)
|
||||
* 2. 用户点击「游客登录」按钮 → PlatformSession.startup() 触发
|
||||
* 3. bootstrap + WS connect + login 成功后,AppStore.phase = loggedIn
|
||||
* 4. director.loadScene('MainMenu')
|
||||
* 流程(对应 gameabc 启动流程 step 3):
|
||||
* 1. 构造 EventBus + PlatformSession + NetClient(用 mock transport)
|
||||
* 2. 调 resolveBootstrap 拉远程配置(可能因无后端失败 → 进 loginFailed)
|
||||
* 3. Mock transport 同步触发 open + login 事件 → PlatformSession 落 Store
|
||||
* 4. login 成功(state=0) → AppStore.phase=loggedIn → director.loadScene('MainMenu')
|
||||
*
|
||||
* 当前简化:2 秒后自动跳到 MainMenu,验证场景切换链路。
|
||||
* 注意:当前 Transport 是 mock sync 触发(真 WS 需要 Cocos WebSocket adapter,
|
||||
* 未实现)。framework/net/cocos-transport.ts 留作生产占位。
|
||||
*
|
||||
* 限制:PlayerInfoView prefab 持久化 __type__ 字段问题未解决(memory 记录),
|
||||
* 本脚本不依赖 PlayerInfoView 组件,只验证 PlatformSession 链路。
|
||||
*/
|
||||
|
||||
class MockTransport implements Transport {
|
||||
private onOpenCb?: () => void;
|
||||
private onMessageCb?: (frame: string) => void;
|
||||
private onCloseCb?: () => void;
|
||||
connect(_url: string): void {
|
||||
// 模拟:连接后 200ms 同步触发 open + login
|
||||
setTimeout(() => {
|
||||
this.onOpenCb?.();
|
||||
this.onMessageCb?.(JSON.stringify({
|
||||
app: 'youle', route: 'agent', rpc: 'player_login',
|
||||
data: {
|
||||
state: 0, playerid: 430511, agentid: 'A', channelid: 'C',
|
||||
nickname: '测试玩家', avatar: '', openid: 'o1', sex: 0,
|
||||
bean: 0, roomcard: 3, score: 0, invitecode: null, advanced: 0,
|
||||
taskstate: 1, ip: '127.0.0.1', bankpower: 1, bank: 0,
|
||||
sign: null, tel: null, initCard: '3', initBean: '0', bankpwd: 0,
|
||||
unionid: 'mock_unionid',
|
||||
},
|
||||
}));
|
||||
}, 200);
|
||||
}
|
||||
send(_frame: string): void { /* noop */ }
|
||||
close(): void { this.onCloseCb?.(); }
|
||||
onOpen(cb: () => void): void { this.onOpenCb = cb; }
|
||||
onMessage(cb: (f: string) => void): void { this.onMessageCb = cb; }
|
||||
onClose(cb: () => void): void { this.onCloseCb = cb; }
|
||||
}
|
||||
|
||||
@ccclass('LoginFlow')
|
||||
export class LoginFlow extends Component {
|
||||
start(): void {
|
||||
// mock:2 秒后假装登录成功,切到 MainMenu
|
||||
this.scheduleOnce(() => {
|
||||
director.loadScene('MainMenu');
|
||||
}, 2);
|
||||
const bus = new EventBus<NetClientEvents>();
|
||||
const session = new PlatformSession(bus);
|
||||
|
||||
// 启动编排:bootstrap → mock login → loadScene
|
||||
(async () => {
|
||||
try {
|
||||
// 真实拉远程配置(可能因后端不可达失败)
|
||||
const result = await resolveBootstrap({
|
||||
mode: 'release', // 编辑器内置 preview,走 release
|
||||
win: window as any,
|
||||
search: '',
|
||||
fetcher: new HttpConfigFetcher(),
|
||||
isNative: false,
|
||||
});
|
||||
session.app.setIdentity(result.identity);
|
||||
session.app.setServers(result.servers);
|
||||
console.log('[LoginFlow] bootstrap OK, servers=' + result.servers[0]);
|
||||
} catch (e) {
|
||||
console.warn('[LoginFlow] bootstrap failed (expected in editor), use mock: ' + (e as Error).message);
|
||||
// 编辑器无后端时,fallback 到 mock identity
|
||||
session.app.setIdentity({
|
||||
agentid: 'A', channelid: 'C', gameid: 'G', marketid: 4, version: 10000,
|
||||
});
|
||||
session.app.setServers(['ws://mock']);
|
||||
}
|
||||
|
||||
// 构造 NetClient(mock transport 同步触发 login)
|
||||
console.log('[LoginFlow] constructing NetClient with servers=' + session.app.state.value.servers);
|
||||
const net = new NetClient({
|
||||
servers: session.app.state.value.servers,
|
||||
transportFactory: () => new MockTransport(),
|
||||
bus,
|
||||
});
|
||||
// 给 NetClient 设 identity,否则 sendLogin 早返 → login 事件永不触发
|
||||
net.setIdentity({
|
||||
agentid: 'A', channelid: 'C', gameid: 'G', marketid: 4, version: 10000,
|
||||
// 真机环境下补全 nickname/avatar/openid/sex 等字段
|
||||
openid: 'mock_openid', nickname: '测试玩家', avatar: '', sex: 0,
|
||||
} as any);
|
||||
|
||||
// 订阅 login 完成 → loadScene
|
||||
let loginHandled = false;
|
||||
bus.on('login', (data: any) => {
|
||||
console.log('[LoginFlow] bus.on(login) fired, state=' + data?.state);
|
||||
if (loginHandled) return;
|
||||
loginHandled = true;
|
||||
console.log('[LoginFlow] login success, phase=' + session.app.state.value.phase);
|
||||
if (session.app.state.value.phase === 'loggedIn') {
|
||||
new RoomRPCBus(bus, session).start();
|
||||
console.log('[LoginFlow] RoomRPCBus started');
|
||||
this.scheduleOnce(() => director.loadScene('MainMenu'), 0.5);
|
||||
} else {
|
||||
console.warn('[LoginFlow] phase is not loggedIn: ' + session.app.state.value.phase);
|
||||
}
|
||||
});
|
||||
|
||||
bus.on('open', () => console.log('[LoginFlow] bus.on(open) fired'));
|
||||
bus.on('reconnecting', (s) => console.log('[LoginFlow] bus.on(reconnecting) to ' + s));
|
||||
|
||||
try {
|
||||
net.start();
|
||||
console.log('[LoginFlow] net.start() called');
|
||||
} catch (e) {
|
||||
console.error('[LoginFlow] net.start() failed: ' + (e as Error).message);
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user