Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ npm run test:browser

维护模式回归直接测试发布页面,并对照退役前版本核对主页文字及布局。原认证、媒体生命周期回归仅在测试服务器的响应中关闭维护开关,以保留未启用代码的验证;线上没有查询参数或浏览器存储开关可恢复 AI 服务。

游戏专项覆盖异常存档的保留与恢复、受限存储、旧 Service Worker 更新,以及 320px/390px/短横屏弹窗的触摸滚动、关闭与焦点恢复。Frontier 回归检查快速暂停恢复仅有一条动画循环、后台停帧和实际触摸移动;游戏画质保持不变。大厅仍保留竖屏游玩限制,已打开的弹窗可在横屏操作。

公开仓库只包含前端和测试,不包含生产凭据、用户数据或 Worker 私有源码。后端在本机 `.local-secrets/ray-backend` 独立版本管理,包含迁移、恢复验证和受保护发布工具;该目录不能提交到本仓库或暴露给 HTTP 服务。

- [云存档协议与迁移](docs/game-save-cloud-api.md)
Expand Down
58 changes: 53 additions & 5 deletions arcade/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -458,21 +458,30 @@ p {
backdrop-filter: blur(8px);
}

html.arcade-modal-open,
body.arcade-modal-open {
overflow: hidden;
touch-action: pan-y;
}

.modal-backdrop {
position: fixed;
inset: 0;
z-index: 50;
display: grid;
place-items: center;
padding: calc(env(safe-area-inset-top) + 16px) 18px calc(env(safe-area-inset-bottom) + 16px);
padding: calc(env(safe-area-inset-top) + 16px) calc(env(safe-area-inset-right) + 18px) calc(env(safe-area-inset-bottom) + 16px) calc(env(safe-area-inset-left) + 18px);
background: rgba(1, 3, 17, .66);
backdrop-filter: blur(10px);
touch-action: none;
touch-action: pan-y;
overscroll-behavior: contain;
}

.modal-card {
width: min(100%, 420px);
max-height: min(78svh, 78dvh);
min-height: 0;
max-height: calc(100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom) - 32px);
max-height: calc(100dvh - env(safe-area-inset-top) - env(safe-area-inset-bottom) - 32px);
overflow: hidden;
display: flex;
flex-direction: column;
Expand All @@ -486,15 +495,39 @@ p {
}

.modal-head {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
padding: 20px 18px 0;
}

.modal-title {
flex: 1;
min-width: 0;
overflow-wrap: anywhere;
font-size: 22px;
letter-spacing: -.03em;
letter-spacing: 0;
}

.modal-close {
flex: 0 0 44px;
width: 44px;
min-height: 44px;
padding: 0;
border: 1px solid rgba(103, 232, 249, .24);
border-radius: 12px;
background: rgba(255, 255, 255, .06);
color: inherit;
font: inherit;
font-size: 26px;
cursor: pointer;
}

.modal-body {
min-height: 0;
overscroll-behavior: contain;
overflow-wrap: anywhere;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
touch-action: pan-y;
Expand All @@ -510,12 +543,23 @@ p {

.modal-actions {
display: flex;
flex-shrink: 0;
flex-wrap: wrap;
gap: 10px;
padding: 0 18px 18px;
}

.modal-actions button {
flex: 1;
flex: 1 1 88px;
min-height: 44px;
min-width: 0;
white-space: normal;
overflow-wrap: anywhere;
}

@media (max-height: 420px) {
.modal-head { padding-top: 8px; }
.modal-actions { padding-bottom: 8px; }
}

.toast {
Expand Down Expand Up @@ -1021,6 +1065,10 @@ p {
}
}

body.arcade-modal-open .orientation-lock {
display: none;
}

@media (max-width: 374px) {
.game-topbar {
grid-template-columns: auto 1fr auto auto;
Expand Down
6 changes: 3 additions & 3 deletions arcade/frontier/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ <h2 data-result-title>突围结束</h2>
</main>

<script src="../js/save-schema.js?v=save-v3"></script>
<script src="../js/save-core.js?v=save-v2"></script>
<script src="../js/save-core.js?v=mobile-3"></script>
<script src="../js/storage.js?v=save-v2"></script>
<script src="../js/cloud-save.js?v=save-v2"></script>
<script src="../js/ui.js?v=rayhop-4"></script>
<script type="module" src="js/frontier.js?v=save-v3"></script>
<script src="../js/ui.js?v=mobile-3"></script>
<script type="module" src="js/frontier.js?v=mobile-3"></script>
</body>
</html>
42 changes: 31 additions & 11 deletions arcade/frontier/js/frontier.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ import * as THREE from "../vendor/three.module.js";
this.state = "menu";
this.last = 0;
this.raf = 0;
this.idleRaf = 0;
this.time = 0;
this.cloudSave = null;
this.activePointer = null;
Expand Down Expand Up @@ -155,9 +156,13 @@ import * as THREE from "../vendor/three.module.js";
this.resize();
window.addEventListener("resize", () => this.resize());
document.addEventListener("visibilitychange", () => {
if (document.hidden && this.state === "playing") this.pause();
if (document.hidden) {
this.pause();
this.stopAnimation();
} else if (this.state === "menu") this.drawIdle();
});
window.addEventListener("pagehide", () => this.pause());
window.addEventListener("pagehide", () => { this.pause(); this.stopAnimation(); });
window.addEventListener("pageshow", () => { if (this.state === "menu") this.drawIdle(); });
this.loading.classList.add("is-hidden");
this.drawIdle();
}
Expand Down Expand Up @@ -644,6 +649,7 @@ import * as THREE from "../vendor/three.module.js";

start() {
if (!this.scene) return;
this.stopAnimation();
this.result.hidden = true;
this.result.classList.remove("is-open");
this.menu.classList.remove("is-open");
Expand All @@ -659,32 +665,46 @@ import * as THREE from "../vendor/three.module.js";
pause() {
if (this.state !== "playing") return;
this.state = "paused";
this.stopAnimation();
this.releaseStick();
UI && UI.toast && UI.toast("Ray Frontier 已暂停");
}

resume() {
if (this.state !== "paused") return;
if (this.state !== "paused" || document.hidden) return;
this.stopAnimation();
this.state = "playing";
this.last = performance.now();
this.loop(this.last);
this.raf = requestAnimationFrame((time) => this.loop(time));
}

stopAnimation() {
cancelAnimationFrame(this.raf);
cancelAnimationFrame(this.idleRaf);
this.raf = this.idleRaf = 0;
}

drawIdle() {
cancelAnimationFrame(this.idleRaf);
this.idleRaf = 0;
if (this.state !== "menu" || document.hidden) return;
this.nodes.player.rotation.y += .01;
this.camera.position.lerp(new THREE.Vector3(0, 19.4, 22.4), .05);
this.camera.lookAt(0, 1.05, -5.8);
this.renderer.render(this.scene, this.camera);
if (this.state === "menu") requestAnimationFrame(() => this.drawIdle());
this.idleRaf = requestAnimationFrame(() => this.drawIdle());
}

loop(now) {
cancelAnimationFrame(this.raf);
this.raf = 0;
if (document.hidden) { this.pause(); return; }
if (this.state !== "playing") return;
const dt = Math.min(.033, Math.max(.001, (now - this.last) / 1000));
this.last = now;
this.update(dt);
this.render();
this.raf = requestAnimationFrame((t) => this.loop(t));
if (this.state === "playing" && !document.hidden) this.raf = requestAnimationFrame((t) => this.loop(t));
}

resize() {
Expand Down Expand Up @@ -1114,7 +1134,7 @@ import * as THREE from "../vendor/three.module.js";
p.xpNext = Math.floor(p.xpNext * 1.26 + 12);
p.hp = Math.min(p.maxHp, p.hp + 18);
this.state = "levelup";
cancelAnimationFrame(this.raf);
this.stopAnimation();
this.showUpgrades();
pulse("success");
}
Expand All @@ -1133,11 +1153,11 @@ import * as THREE from "../vendor/three.module.js";
button.className = "upgrade-card";
button.innerHTML = `<strong>${up.name}</strong><span>${up.text}</span>`;
button.addEventListener("click", () => {
if (this.state !== "levelup") return;
up.apply(this);
this.upgrades.hidden = true;
this.state = "playing";
this.last = performance.now();
this.loop(this.last);
this.state = "paused";
this.resume();
UI && UI.toast && UI.toast(up.name);
});
this.upgradeGrid.appendChild(button);
Expand Down Expand Up @@ -1310,7 +1330,7 @@ import * as THREE from "../vendor/three.module.js";
gameOver() {
if (this.state === "gameover") return;
this.state = "gameover";
cancelAnimationFrame(this.raf);
this.stopAnimation();
this.releaseStick();
const p = this.player;
Storage && Storage.updateBest && Storage.updateBest("frontier", {
Expand Down
8 changes: 4 additions & 4 deletions arcade/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<link rel="manifest" href="manifest.webmanifest" />
<link rel="icon" href="assets/icon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="assets/icon.svg" />
<link rel="stylesheet" href="css/style.css?v=cloudsave-2" />
<link rel="stylesheet" href="css/style.css?v=mobile-3" />
<link rel="stylesheet" href="css/save-ui.css?v=save-v2" />
</head>
<body>
Expand All @@ -26,17 +26,17 @@ <h2>请竖屏游玩</h2>
</div>

<script src="js/save-schema.js?v=save-v3"></script>
<script src="js/save-core.js?v=save-v2"></script>
<script src="js/save-core.js?v=mobile-3"></script>
<script src="js/storage.js?v=save-v2"></script>
<script src="js/cloud-save.js?v=save-v2"></script>
<script src="js/ui.js?v=rayhop-4"></script>
<script src="js/ui.js?v=mobile-3"></script>
<script src="games/cat-jump.js?v=fixed-2"></script>
<script src="games/ray-hop.js?v=rayhop-3"></script>
<script src="games/merge-2048.js?v=save-v3"></script>
<script src="games/dungeon.js"></script>
<script src="games/neon-balls.js?v=fixed-2"></script>
<script src="games/reaction.js"></script>
<script src="games/daily-card.js?v=save-v2"></script>
<script src="js/app.js?v=save-v2"></script>
<script src="js/app.js?v=mobile-3"></script>
</body>
</html>
10 changes: 4 additions & 6 deletions arcade/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,10 @@
UI.showModal({
title: "暂停中",
html: `<p>${UI.escapeHtml(message || "游戏已暂停。")}</p>`,
onDismiss: close => {
close();
if (currentGame) currentGame.resume();
},
actions: [
{ label: "继续", kind: "primary", onClick: (close) => { close(); pauseModalOpen = false; currentGame && currentGame.resume(); } },
{ label: "重开", kind: "secondary", onClick: (close) => { close(); pauseModalOpen = false; currentGame && currentGame.restart(); } },
Expand All @@ -441,12 +445,6 @@
window.addEventListener("blur", () => currentGame && currentGame.pause());

document.addEventListener("gesturestart", (ev) => ev.preventDefault(), { passive: false });
let lastTouchEnd = 0;
document.addEventListener("touchend", (ev) => {
const now = Date.now();
if (now - lastTouchEnd <= 320) ev.preventDefault();
lastTouchEnd = now;
}, { passive: false });

initCloudSave();
renderLobby();
Expand Down
8 changes: 5 additions & 3 deletions arcade/js/save-core.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,14 @@
const work = async () => {
const result = await transaction(key, fresh(this.initial), edit);
if (epoch === this.epoch) {
if (['pulled', 'resolved', 'imported'].includes(reason) && !equal(this.memory, result.record.payload)) {
const recovering = ['resolved', 'imported'].includes(reason);
const replacesMemory = version === this.optimisticVersion && (!this.failed || recovering);
if (replacesMemory && (reason === 'pulled' || recovering) && !equal(this.memory, result.record.payload)) {
window.dispatchEvent(new CustomEvent('ray-save-owner-changing', { detail: { appId: this.appId, reason } }));
}
this.record = result.record;
if (['resolved', 'imported'].includes(reason)) { this.failed = false; this.volatile.delete(key); }
if (!this.failed && version === this.optimisticVersion) this.memory = copy(result.record.payload);
if (recovering) { this.failed = false; this.volatile.delete(key); }
if (replacesMemory) this.memory = copy(result.record.payload);
this.persisted = !this.failed;
this.notify(reason);
}
Expand Down
Loading
Loading