实现账号绑定、持久登录与跨设备对局恢复
CI / test (ubuntu-latest, node 22) (push) Failing after 33m10s
CI / test (ubuntu-latest, node 24) (push) Failing after 34m41s
CI / tooling (lint, imports, typecheck) (push) Failing after 36m11s
CI / test (windows-latest, node 22) (push) Canceled after 0s
CI / test (windows-latest, node 24) (push) Canceled after 0s
CI / test (ubuntu-latest, node 22) (push) Failing after 33m10s
CI / test (ubuntu-latest, node 24) (push) Failing after 34m41s
CI / tooling (lint, imports, typecheck) (push) Failing after 36m11s
CI / test (windows-latest, node 22) (push) Canceled after 0s
CI / test (windows-latest, node 24) (push) Canceled after 0s
This commit is contained in:
1 parent
488db49f11
commit
7c25c89992
62 files changed
+6855
-1005
No files matched your search
+12
-9
@@ -8,13 +8,13 @@
|
||||
// otherwise → Lobby
|
||||
// Deep link `?room=CODE`: remembered at boot, auto-joined once the player has entered and the
|
||||
// session is online (after a short grace period in case the server restores a room on resume).
|
||||
// Reloading a tab that already passed the title re-enters automatically (sessionStorage flag) and
|
||||
// Reloading a tab that already passed the title re-enters after checking its current role binding and
|
||||
// resumes the server session with the saved token; stale room/match state is dropped if the
|
||||
// server does not re-push it within RESTORE_GRACE_MS after `welcome`. Boot waits for
|
||||
// `identity.init()` (which token this tab may use without stealing another live tab's session)
|
||||
// before the first connect; a page restored from the back/forward cache reloads.
|
||||
// A `welcome` with a NEW playerId (the server restarted / the session expired) while a room or match was on screen:
|
||||
// toast 「服务器会话已重置,上一局模拟已结束」 (store.js sessionResetNotice) and return to the lobby with nothing stale.
|
||||
// drop stale match state (store.js sessionResetNotice); an unauthenticated transport returns to the title for checked entry.
|
||||
//
|
||||
// Shared modules are imported relatively ('../../shared/…' resolves to /shared/… in the browser).
|
||||
// Multi-device support (ui/device.js + css/devices.css): feature classes on <html>, no page zoom, safe areas, rotation
|
||||
@@ -41,6 +41,7 @@ import { store, useStore, emptyMatch, selectRoute, sessionResetNotice, isSpectat
|
||||
import { data } from './data.js';
|
||||
import { GAME_FILES } from './ui/gameComponents.js';
|
||||
import { TitleScreen, sanitizeName } from './screens/title.js';
|
||||
import { acceptAccountWelcome, restoreBoundEntry } from './skland/gate.js';
|
||||
import { LobbyScreen, rememberRoom, parseRoomParam } from './screens/lobby.js';
|
||||
import { RoomScreen } from './screens/room.js';
|
||||
import { GameScreen } from './screens/game.js';
|
||||
@@ -145,6 +146,7 @@ function backToLobby() {
|
||||
}
|
||||
|
||||
function onWelcome(msg) {
|
||||
const accountAccepted = acceptAccountWelcome(msg);
|
||||
identity.saveToken(msg.token);
|
||||
identity.rememberMatch(null);
|
||||
const prev = store.get();
|
||||
@@ -156,8 +158,8 @@ function onWelcome(msg) {
|
||||
|
||||
if (prevId != null && prevId !== msg.playerId) {
|
||||
// A brand-new server session (the server restarted — crashed / killed, so no room.closed arrived — or this session
|
||||
// expired on it): whatever we showed before is gone — back to the lobby cleanly and say why.
|
||||
const notice = sessionResetNotice(prev, msg.playerId);
|
||||
// expired on it): clear the old snapshot. A login-only transport stays at the title until checked account entry.
|
||||
const notice = accountAccepted ? sessionResetNotice(prev, msg.playerId) : null;
|
||||
backToLobby();
|
||||
if (notice) toast(t(notice), 'warn', { ttl: 7000 });
|
||||
} else if (prev.room || prev.match.public) {
|
||||
@@ -303,7 +305,7 @@ function App() {
|
||||
return html`<div class="app-root">
|
||||
<div class="app-bg" aria-hidden="true"></div>
|
||||
${error ? html`<${ScreenCrashed} error=${error} reset=${resetError} />` : html`<${Screen} key=${route} />`}
|
||||
<${ConnectionBanner} />
|
||||
<${ConnectionBanner} continueSession=${() => restoreBoundEntry(sanitizeName)} />
|
||||
<${ToastHost} />
|
||||
<${UiHosts} />
|
||||
<${GuideHost} />
|
||||
@@ -360,7 +362,7 @@ async function boot() {
|
||||
const entered = identity.wasEntered() && !!savedName;
|
||||
store.set((s) => ({
|
||||
me: { ...s.me, name: savedName },
|
||||
session: { entered },
|
||||
session: { entered: false },
|
||||
ui: { ...s.ui, pendingJoin },
|
||||
}));
|
||||
|
||||
@@ -378,9 +380,10 @@ async function boot() {
|
||||
// Optional local-client art manifest (emotes, tutorial pages, official UI sprites; DESIGN §13).
|
||||
data.load('local').catch(() => {});
|
||||
|
||||
const connectWhenReady = identityReady.then(() => {
|
||||
if (entered) net.setName(savedName);
|
||||
else net.connect();
|
||||
const connectWhenReady = identityReady.then((token) => {
|
||||
if (entered && token) return restoreBoundEntry(sanitizeName, { automatic: true });
|
||||
identity.setEntered(false);
|
||||
net.connect();
|
||||
});
|
||||
// the language (and its UI translations) before the first render: no Chinese flash for an English player
|
||||
const langReady = initLang().catch((err) => console.warn('[app] language setup failed', err));
|
||||
|
||||
Reference in new issue
Block a user