diff --git a/.gitignore b/.gitignore index 4334987..e215ae4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ research/ .playwright-mcp/ cache/ +star-raid/data/ diff --git a/AGENTS.md b/AGENTS.md index 1ccb2b3..7b1f7da 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,21 +4,24 @@ ## 项目概述 -- 纯 HTML5 Canvas + 原生 JavaScript,**无构建、无依赖、无框架** +- 客户端:纯 HTML5 Canvas + 原生 JavaScript,**无构建、无依赖、无框架** +- 服务端:**零 npm 依赖** Node 原生(`node:http` + `node:sqlite`,要求 Node≥22.5),SQLite 存储 - 240×320 像素画布,竖版弹幕射击(主武器/副武器/僚机/Boss 战/Boss Rush 模式) -- 游戏本体在 `star-raid/` 子目录 +- 账号系统:注册/登陆(用户名+邮箱+密码)、大厅排行榜 Top100、图鉴收集点亮同步 +- 游戏本体在 `star-raid/` 子目录,后端在 `star-raid/server/`,部署见 `star-raid/DEPLOY.md` ## 常用命令 ```bash cd star-raid -# 语法检查(每次改完 JS 必须跑) -for f in js/*.js; do node --check "$f" || echo "FAIL $f"; done +# 语法检查(每次改完 JS 必须跑;server/ 也要查) +for f in js/*.js server/*.js; do node --check "$f" || echo "FAIL $f"; done -# 本地运行(用户试玩用) -python3 -m http.server 8643 # 然后访问 http://localhost:8643 -# 注意:8642 端口有浏览器缓存污染,固定用 8643 +# 启动完整版(游戏+后端,Node≥22.5) +node server/server.js # 或 npm start;访问 http://localhost:8643 +# 注意:8643 现由 node server 占用;旧 python http.server 试玩方式已废弃 +# 数据库:star-raid/data/starraid.db(git 忽略,删除重启自动重建空库) # Git(仓库根 = 工作区根目录,游戏在子目录) git add -A && git commit -m "提交信息" @@ -26,9 +29,9 @@ git add -A && git commit -m "提交信息" ## 验证规则(最高优先级,违反会被用户骂) -1. **禁止运行游戏测试视觉效果**——包括 playwright 截图、浏览器数据断言。视觉和手感全部由用户亲自验收。Agent 只做:`node --check` 语法检查 + git 提交。 -2. 交付时必须诚实标注验证状态:"仅语法检查通过,未运行验证"。 -3. 完成了操作 ≠ 任务完成,未经验证不得说"修复了/完成了"。 +1. **游戏视觉/手感禁止 Agent 验收**——用户亲自验收。但**账号/大厅/排行榜/结算上报等功能流允许 playwright 测试**(用户 2026-08-06 明确授权):注册/登陆/数据断言可跑,只是不许用截图评判游戏画面效果。 +2. 交付时必须诚实标注验证状态。 +3. 完成了操作 ≠ 任务完成,未经验证不得说“修复了/完成了”。 ## 项目结构 @@ -36,18 +39,29 @@ git add -A && git commit -m "提交信息" star-raid/ ├── index.html # 入口 + 全部覆盖层 UI 结构 ├── css/style.css # 全部样式(像素风 UI 变量配色) +├── server/ # 后端(零依赖 node:http+node:sqlite) +│ ├── server.js # 入口:静态白名单+/api路由+安全头 +│ ├── db.js # SQLite schema(users/sessions/user_stats/codex_unlocks/runs) +│ ├── auth.js # scrypt密码哈希+服务端session +│ ├── api.js # API处理器;CODEX_KEYS 白名单(新增装备必须同步!) +│ └── util.js # body解析/限流/输入校验 +├── data/ # SQLite 数据库(git 忽略) ├── js/ │ ├── config.js # BAL 平衡参数、常量 │ ├── data.js # WEAPONS/SUBS/DRONES 装备数据表 │ ├── core.js # 游戏状态 G、发射逻辑、explodeFx 爆炸特效库 │ ├── combat.js # 子弹运动/碰撞/光束判定/zones 区域效果 │ ├── enemies.js # ENEMIES 杂兵表、BOSSES 表、bossMain/bossMinor 攻击库 -│ ├── render.js # 世界渲染(drawBoss 分支调度、血条、子弹、zones) +│ ├── render.js # 世界渲染(battleUI 门控只在战斗界面绘制游戏对象) │ ├── boss_art.js # Boss 美术库(drawProgenitor/drawAssault/drawPiano/drawUsurper/drawSaucer) │ ├── sprites.js # 像素字符画 bake 系统、SPR 精灵表、BHALO 弹光晕 │ ├── audio.js # SFX 音效(WebAudio 合成) -│ └── ui.js # 菜单/图鉴/rush 选择/老虎机/结算 -├── demo/ # 动作设计 demo(如 tentacle-sweep.html 触手扫击) +│ ├── net.js # NET:API 客户端(fetch 封装/图鉴同步/submitRun) +│ ├── auth.js # 认证流程(登陆/注册/登出 + boot 引导) +│ ├── home.js # 大厅:排行榜 + 图鉴展柜背景(drawHomeShowcase) +│ └── ui.js # 菜单/图鉴/rush 选择/老虎机/结算(submitRun 上报) +├── demo/ # 动作设计 demo(不在静态白名单,公网不可见) +├── DEPLOY.md # 部署指南(公网/HTTPS/安全边界) └── research/ # Boss 参考图与调研(不进 git) ``` @@ -76,3 +90,12 @@ star-raid/ - 大改动先讨论方案再动手;视觉效果由用户验收后再迭代 - 每完成一个逻辑单元立即 git 提交(提交信息写清改了什么+为什么) - 项目记忆(`.astrion/memory/`)沉淀重要约定,发现新约定主动更新 + +## 后端约定(2026-08-06 后端化) + +- 客户端流程:boot → `/api/me` → 未登录 o-auth(登陆/注册)→ 已登录 o-home(大厅:用户面板+排行榜)→ o-title(基地菜单) +- 结算上报:`submitRun()`(ui.js)在 endRun 与 rush 胜利(enemies.js killBoss)两处调用;`G.submitted` 防重 +- 图鉴:服务端 `codex_unlocks` 为准,登陆时与本地 SAVE.codex 并集合并;未收集=剪影+???不可演示 +- 背景规则:render() 只在 battleUI()(play/demoSim/pause/cards/over/slot)绘制游戏对象;大厅 UI="home" 时 drawHomeShowcase()(30s 重抽);其余=星空 +- 新增装备时必须同步 `server/api.js` 的 CODEX_KEYS 白名单(现 24+17+13+8=62) +- 星币/机库/机体仍存 localStorage 不同步(v1 边界,见 DEPLOY.md) diff --git a/star-raid/DEPLOY.md b/star-raid/DEPLOY.md new file mode 100644 index 0000000..343e7c7 --- /dev/null +++ b/star-raid/DEPLOY.md @@ -0,0 +1,61 @@ +# Star Raid 部署指南 + +## 运行要求 + +- **Node.js ≥ 22.5**(使用内置 `node:sqlite`,零 npm 依赖,无需 `npm install`) +- SQLite 数据库文件自动创建于 `star-raid/data/starraid.db`(已在 .gitignore) + +## 启动 + +```bash +cd star-raid +npm start # = node server/server.js +# 或指定环境变量 +PORT=8643 HOST=0.0.0.0 node server/server.js +``` + +| 环境变量 | 默认 | 说明 | +|---|---|---| +| `PORT` | `8643` | 监听端口 | +| `HOST` | `0.0.0.0` | 监听地址 | +| `COOKIE_SECURE` | 空 | **HTTPS 部署时必须设为 `1`**(Cookie 加 Secure) | +| `DATA_DIR` | `star-raid/data` | 数据库目录 | + +## 公网部署(nginx + HTTPS 推荐) + +```nginx +server { + listen 443 ssl; + server_name your-domain.com; + # ssl_certificate / certbot 申请 + location / { + proxy_pass http://127.0.0.1:8643; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; # 限流按真实 IP + } +} +``` + +启动时 `COOKIE_SECURE=1 PORT=8643 node server/server.js`,用 systemd/supervisor 守护进程。 + +## 已实现的安全措施 + +- 密码:scrypt + 每用户随机盐 + 时序安全比较;登陆失败统一报错(不泄露账号是否存在),不存在账号也跑一次哈希防时序探测 +- 会话:32 字节随机 token 存服务端 SQLite,HttpOnly + SameSite=Lax,7 天滑动续期,可吊销 +- SQL 注入:全部 prepared statement;图鉴 key 服务端白名单(`server/api.js` 的 `CODEX_KEYS`,**新增装备时必须同步**) +- 输入:用户名/邮箱/密码白名单校验;请求体 ≤16KB;POST 强制 `application/json`(配合 SameSite 防 CSRF) +- 限流:注册 10 次/5 分钟/IP,登陆 20 次/5 分钟/IP,成绩上报 20 次/5 分钟/账号 +- 静态资源白名单:只放行 `/`、`/css/`、`/js/`(`server/`、`data/`、`demo/` 不可访问),防目录穿越 +- 安全头:CSP / X-Frame-Options / X-Content-Type-Options / Referrer-Policy + +## v1 边界(明确不做) + +- 邮箱仅作唯一标识,**无验证邮件、无找回密码**(需 SMTP 服务,留 v2) +- **反作弊仅为基础校验**(数值范围/限流):纯客户端游戏无法杜绝作弊,有志者改内存照样能伪造高分。如需严肃竞技,要加服务端回放校验(大工程) +- 星币/机库强化/机体解锁仍存浏览器 localStorage,**不随账号同步**(跨设备冲突解决复杂,留 v2) +- 排行榜 = 全服单榜,无分页/赛季 + +## 数据维护 + +- 备份:拷贝 `data/starraid.db`(WAL 模式,建议先 `PRAGMA wal_checkpoint` 或直接停服拷贝) +- 重置排行榜:停服后删除 `data/` 下文件,重启自动重建空库 diff --git a/star-raid/css/style.css b/star-raid/css/style.css index 111984e..33ae25b 100644 --- a/star-raid/css/style.css +++ b/star-raid/css/style.css @@ -116,3 +116,27 @@ h1.logo{font-size:clamp(20px,5.4vw,44px);color:#fff;letter-spacing:4px;line-heig .cx-item.lock{opacity:.35;cursor:default} .cx-item.lock:hover{transform:none;border-color:var(--line)} .cx-sec{font-size:clamp(8px,1.5vw,10px);color:var(--cyan);margin:10px 0 4px;letter-spacing:3px} + +/* ---------- 登陆/注册 ---------- */ +.auth-f{display:flex;flex-direction:column;gap:8px;margin-top:12px} +.auth-f.hide{display:none} +.auth-f input{font:inherit;font-size:clamp(8px,1.6vw,11px);background:var(--panel2);border:3px solid var(--line); + color:var(--ink);padding:10px 12px;letter-spacing:1px;width:100%;outline:none} +.auth-f input:focus{border-color:var(--cyan)} +.auth-f input::placeholder{color:var(--dim)} +.auth-f .btn{width:100%;min-width:0;margin-top:2px} +/* ---------- 飞行员大厅 ---------- */ +#o-home{background:rgba(4,7,16,.42);justify-content:flex-start;gap:8px;overflow-y:auto} +#o-home .lb{width:min(94%,430px);background:var(--panel);border:3px solid var(--line);padding:10px 10px 8px;margin-top:auto;margin-bottom:2%} +.lb-tabs{display:flex;gap:6px;justify-content:center;margin:6px 0 8px} +.lb-tabs button{font:inherit;font-size:clamp(7px,1.3vw,9px);background:var(--panel2);color:var(--dim); + border:2px solid var(--line);padding:6px 10px;cursor:pointer;letter-spacing:1px} +.lb-tabs button.on{border-color:var(--cyan);color:var(--cyan)} +.lb-row{display:grid;grid-template-columns:30px 1fr 48px 58px 70px;gap:4px;align-items:center; + font-size:clamp(7px,1.3vw,9px);padding:4px 4px;letter-spacing:1px;text-align:left} +.lb-row span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +.lb-row span:first-child{color:var(--dim)} +.lb-row.lb-head{color:var(--dim);border-bottom:2px solid var(--line)} +.lb-row.me{background:rgba(65,230,255,.12);outline:1px solid rgba(65,230,255,.35)} +.lb-list{max-height:34%;min-height:60px;overflow-y:auto} +.lb-empty{color:var(--dim);font-size:clamp(7px,1.3vw,9px);padding:14px 0;letter-spacing:2px} diff --git a/star-raid/index.html b/star-raid/index.html index 0bcf207..3ad634d 100644 --- a/star-raid/index.html +++ b/star-raid/index.html @@ -3,6 +3,7 @@
+