feat: integrate login flow, lobby UI and bitmap font resources

This commit is contained in:
2026-09-06 13:46:57 +08:00
parent e1bcfd91ec
commit 842c68ef55
141 changed files with 11279 additions and 1717 deletions
@@ -1,5 +1,11 @@
import { APP } from '../core/constants.ts';
/** Legacy 12_Logic dispatches login replies from all three platform routes by rpc.
* This inbound contract is independent of the outbound agent route below. */
export function isLoginResponse(message:{route:string;rpc:string}):boolean{
return message.rpc==='player_login'&&(message.route==='agent'||message.route==='room'||message.route==='platform');
}
export type FirstSliceRpc =
| 'player_login'
| 'self_join_room'
@@ -11,7 +17,9 @@ export type FirstSliceRpc =
| 'other_online'
| 'connect_roomserver'
| 'connect_agentserver'
| 'kick_server';
| 'kick_server'
| 'kick_offline'
| 'show_message';
export type FirstSliceRoute = 'platform' | 'agent' | 'room';
@@ -28,6 +36,8 @@ export const FIRST_SLICE_RPC_ROUTE = {
connect_roomserver: 'room',
connect_agentserver: 'agent',
kick_server: 'agent',
kick_offline: 'agent',
show_message: 'agent',
} as const satisfies Record<FirstSliceRpc, 'platform' | 'agent' | 'room'>;
export function routeForFirstSlice<Rpc extends FirstSliceRpc>(
@@ -3,6 +3,7 @@ import type { GameSessionHost } from '../sdk/runtime/game-session-host.ts';
import type { ConnectionControlPort } from '../platform/connection-intent.ts';
import type { RuntimeSession } from '../platform/runtime-session.ts';
import type { PlatformStore } from '../platform/stores/platform-store.ts';
import { requireRecord, requireString } from './contracts/validation.ts';
import {
parseConnectAgentserverPayload,
parseConnectRoomserverPayload,
@@ -16,6 +17,7 @@ import {
} from './contracts/index.ts';
import {
buildFirstSliceEnvelope,
isLoginResponse,
FIRST_SLICE_RPC_ROUTE,
routeForFirstSlice,
type FirstSliceRpc,
@@ -26,6 +28,7 @@ export interface PlatformHandlersOptions {
readonly gameSession: GameSessionHost;
readonly session: RuntimeSession;
readonly connection: ConnectionControlPort;
readonly showMessage?: (message:string,time:number)=>void;
}
export type PlatformDispatchResult = 'handled-platform';
@@ -47,7 +50,7 @@ export class PlatformHandlers {
}
const rpc = message.rpc as FirstSliceRpc;
const expectedRoute = routeForFirstSlice(rpc);
if (message.route !== expectedRoute) {
if (message.route !== expectedRoute && !isLoginResponse(message)) {
throw new Error(
`Platform rpc route mismatch: rpc=${rpc} expected=${expectedRoute} received=${message.route}`,
);
@@ -118,6 +121,20 @@ export class PlatformHandlers {
);
break;
}
case 'show_message': {
const data=requireRecord(message.data,'$.data');
const text=requireString(data.msg,'$.data.msg');
// Desk.show_message converts the original time with Number; only >0 auto-closes.
const time=Number(data.time);
if(!this.options.showMessage)throw Error('show_message requires a message UI');
this.options.showMessage(text,time);
break;
}
case 'kick_offline': {
const data=requireRecord(message.data,'$.data');
this.options.session.handleKick(data,()=>this.options.connection.stopForKick(data));
break;
}
case 'kick_server': {
const parsed = parseKickServerPayload(message.data);
this.options.session.handleKick(
@@ -1,4 +1,5 @@
import type { InboundMessage } from '../core/types/envelope.ts';
import { isLoginResponse } from './first-slice-routes.ts';
import type { GameSessionHost } from '../sdk/runtime/game-session-host.ts';
import type { RuntimeSession } from '../platform/runtime-session.ts';
import type { PlatformDispatchResult, PlatformHandlers } from './platform-handlers.ts';
@@ -77,6 +78,6 @@ function isPlatformRoute(route: string): route is 'platform' | 'agent' | 'room'
}
function isLoginGateControl(message: InboundMessage): boolean {
return message.route === 'agent'
&& (message.rpc === 'player_login' || message.rpc === 'kick_server');
return isLoginResponse(message)||(message.route === 'agent'
&& (message.rpc === 'kick_server' || message.rpc === 'kick_offline'));
}