diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/LoginFlow.ts b/cocoscreator_projects/YouleNexus/assets/scripts/LoginFlow.ts index 1c82029..3c931c1 100644 --- a/cocoscreator_projects/YouleNexus/assets/scripts/LoginFlow.ts +++ b/cocoscreator_projects/YouleNexus/assets/scripts/LoginFlow.ts @@ -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(); + 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); + } + })(); } } \ No newline at end of file