feedback5 WT: CONTRIBUTING.md (Chinese, English summary) — setup, targeted / golden / opt-in browser tests, the fidelity rules (official data + PRTS, [ASSUMED], no copyrighted text at length), commit and pull-request conventions, adding a 自选 operator per kits/README.md; ARCHITECTURE and README link it; docs-paths covers it
This commit is contained in:
1 parent
11655e19d6
commit
adaa8695ef
4 files changed
+129
-10
No files matched your search
+115
@@ -0,0 +1,115 @@
|
||||
# 参与贡献
|
||||
|
||||
欢迎提 Issue 和 Pull Request:bug、和官方规则不一致的地方、新干员的战斗逻辑、翻译、文档都可以。动手之前先看一眼
|
||||
[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)(代码地图:哪个规则在哪个文件)。English summary at the end.
|
||||
|
||||
## 1. 准备环境
|
||||
|
||||
需要 Node.js 22 或 24(CI 在这两个版本上跑)。
|
||||
|
||||
```bash
|
||||
git clone https://github.com/sganggs/Stronghold-Protocol.git
|
||||
cd Stronghold-Protocol
|
||||
npm install # postinstall 把 pixi / preact / three 复制到 public/vendor
|
||||
npm run setup # 下载美术 / 音频(只跑测试可以不下载,缺素材的用例会自动跳过)
|
||||
npm run dev # 启动服务器(node --watch,改了服务器代码自动重启):http://localhost:3000
|
||||
```
|
||||
|
||||
`npm run doctor` 检查 Node 版本、素材、端口。官方数据表由 `tools/build-data.mjs` 下载到 `.cache/gamedata/`,改数据时才需要。
|
||||
|
||||
## 2. 运行测试
|
||||
|
||||
只跑和改动相关的测试,提交 PR 前再跑一次全部:
|
||||
|
||||
```bash
|
||||
node --test test/content/op_siege.test.js # 一个文件
|
||||
node --test --test-name-pattern="S3" test/content/op_siege.test.js # 文件里的部分用例
|
||||
node --test # 全部(需要浏览器 / 素材的用例自动跳过)
|
||||
npm run golden # 黄金结果:整套固定种子的战斗和对局
|
||||
GOLDEN_FULL=1 node --test test/golden.test.js # 同上,node:test 形式
|
||||
npm run golden:update # 有意改变玩法时重新生成
|
||||
npm run lint && npm run check:imports && npm run typecheck # 代码检查、导入边界、类型
|
||||
```
|
||||
|
||||
浏览器测试默认不跑,需要本机 Chrome(路径不标准时设置 `CHROME_PATH`),用环境变量打开:
|
||||
|
||||
```bash
|
||||
SP_E2E=1 node --test test/ui/mock.e2e.test.js # 界面端到端(不需要素材)
|
||||
SP_REAL_E2E=1 node --test test/ui/real.e2e.test.js # 真实对局(需要已下载的素材)
|
||||
RENDER_E2E=1 node --test 'test/render/*.browser.test.js' # 渲染
|
||||
SIM_E2E=1 node --test 'test/sim/*.browser.test.js' # 浏览器里的战斗模拟
|
||||
```
|
||||
|
||||
**黄金结果**([test/golden/README.md](test/golden/README.md)):只重构、不改玩法的提交不能改变
|
||||
`test/golden/*.json`;有意改变玩法时运行 `npm run golden:update`,把新文件和改动放在同一个提交里,并在提交说明里写清
|
||||
哪些场景变了、为什么。
|
||||
|
||||
## 3. 忠实原则
|
||||
|
||||
这是官方玩法的复刻,规则以官方为准:
|
||||
|
||||
- **出处**:官方数据表(`.cache/gamedata/excel/`:character_table、skill_table、uniequip_table / battle_equip_table、
|
||||
activity_table 的 act2autochess、enemy_database、关卡文件)和 [PRTS](https://prts.wiki/)(干员页、技能和天赋的「备注」、
|
||||
卫戍协议相关页面)。数值尽量取 blackboard,不要凭记忆写数字。官方实测的截图或录像也很有价值,请附在 Issue / PR 里。
|
||||
- **查不到出处**的细节按最简单、最接近官方的方式实现,在代码注释和 PR 描述里标 `[ASSUMED]`。维护者拍板的写成
|
||||
“the owner's decision of YYYY-MM-DD”。有意和官方不同的规则只能由维护者决定,并写进 docs/DESIGN.md。
|
||||
- **版权**:不要把技能描述、剧情等受版权保护的文本大段复制进代码、文档或 PR,引一小句并注明出处即可。不要提交任何游戏
|
||||
素材文件(`public/assets/` 已被 `.gitignore` 排除)。
|
||||
- **数据**:不要手改 `data/*.json`。改 `tools/build-data.mjs`,用 `node tools/build-data.mjs --offline` 重新生成,再比较
|
||||
JSON,确认只变了想改的部分。
|
||||
|
||||
## 4. 提交与 PR
|
||||
|
||||
- 一个 PR 只做一件事;干员的 kit 一个 PR 只放一名,便于审阅。
|
||||
- 提交信息一行写清改了什么(中文或英文都可以),相关的 Issue 写上编号,例如 `余 S2 casts with an enemy on its x-1 (#32)`。
|
||||
只写改动本身,不加 `Co-Authored-By:`、`Generated with …` 这类署名尾行(维护者的约定)。合并的 PR 会在 CHANGELOG
|
||||
里写明并致谢作者。
|
||||
- PR 描述写:改了什么、为什么、出处(数据表字段、PRTS 链接)、`[ASSUMED]` 的地方、跑了哪些测试、黄金结果里哪些场景变了。
|
||||
界面改动附截图。
|
||||
- 规则改了,相关文档一起改:docs/DESIGN.md(规则与契约)、docs/SIM.md、docs/META.md、docs/DATA.md 等。
|
||||
`test/docs-consistency.test.js` 固定了文档里的一些句子,改文档后跑一下。
|
||||
- 代码和注释用英文;玩家文档(README、PLAYING、DEPLOY)用简体中文,技术文档用英文。界面上的文字写中文并用 `t('…')`
|
||||
包起来,英文放进 `public/i18n/en.json`([docs/I18N.md](docs/I18N.md))。
|
||||
- 提交的代码以 GPL-3.0-or-later 发布。本项目坚持非商业,请不要加入广告、付费、打赏等任何变现功能。
|
||||
|
||||
## 5. 添加干员(自选编队)
|
||||
|
||||
自选编队里的每名 6★ 干员都需要一个自己的战斗文件(kit),写法见
|
||||
[server/sim/content/kits/README.md](server/sim/content/kits/README.md) 的「How to add an operator (自选)」。简要步骤:
|
||||
|
||||
1. 数据已经生成好:`data/backups.json` 的 `units[charId]`(各形态的数值、技能、天赋、模组)和 `tokens`(召唤物)。
|
||||
先对照官方数据表和 PRTS 核对。
|
||||
2. 新建 `server/sim/content/kits/ops/op-<codename>.js`(codename 是 charId 去掉 `char_<n>_`),默认导出只有一个键:charId。
|
||||
3. 把文件名加进 `server/sim/content/kits/index.js` 的 `OPERATOR_KIT_FILES`,这名干员就能在自选编队里选了。
|
||||
4. 写测试 `test/content/op_<codename>.test.js`(照 `test/content/op_siege.test.js` 的写法:每个形态、每个模组、每个技能),
|
||||
跑 `node --test test/content/kits_layout.test.js`,再 `npm run golden:update`(只应新增 `test/golden/diy.json` 的场景)。
|
||||
5. PR 里逐项勾选 README 里的「Fidelity checklist」。
|
||||
|
||||
目前池子里的 71 名干员都已经有 kit;改进现有的 kit 也按同样的清单来。相关讨论见 GitHub issue #136。
|
||||
|
||||
---
|
||||
|
||||
## English summary
|
||||
|
||||
- **Setup**: Node.js 22 or 24; `npm install`, `npm run setup` (art and audio — optional for tests), `npm run dev`.
|
||||
The code map is [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
|
||||
- **Tests**: run the targeted files (`node --test <file>`, `--test-name-pattern`), then `node --test` and
|
||||
`npm run golden` before a pull request; `npm run lint`, `npm run check:imports`, `npm run typecheck`. Browser suites
|
||||
are opt-in: `SP_E2E=1`, `SP_REAL_E2E=1` (needs downloaded art), `RENDER_E2E=1`, `SIM_E2E=1` (a local Chrome,
|
||||
`CHROME_PATH`). A refactor never changes `test/golden/*.json`; an intended gameplay change runs
|
||||
`npm run golden:update` and commits the digests with the change, naming the scenarios that moved.
|
||||
- **Fidelity**: the official tables (`.cache/gamedata/excel/`) and PRTS (including the 备注 notes) are the sources;
|
||||
numbers come from the blackboards. Anything no source settles is marked `[ASSUMED]` in the code and the pull
|
||||
request; owner decisions are cited with their date; deliberate deviations are the owner's call and go into
|
||||
docs/DESIGN.md. Do not paste copyrighted game text at length (quote briefly, cite) and never commit game assets.
|
||||
Never hand-edit `data/*.json`: change `tools/build-data.mjs`, rebuild with `--offline`, compare the JSON.
|
||||
- **Commits and pull requests**: one change per pull request, one operator per kit pull request. A one-line commit
|
||||
message saying what changed, with the issue number; no attribution trailers such as `Co-Authored-By:` or
|
||||
`Generated with …` (the maintainers' convention) — merged pull requests are credited in the CHANGELOG. Describe the
|
||||
sources, the `[ASSUMED]` items, the tests and the golden scenarios that moved; update the docs a rule change makes
|
||||
wrong. Code and comments in English; player-facing text in Chinese through `t('…')` with the English in
|
||||
`public/i18n/en.json`. Code is GPL-3.0-or-later; no monetisation features.
|
||||
- **Adding an operator (自选)**: follow "How to add an operator (自选)" in
|
||||
[server/sim/content/kits/README.md](server/sim/content/kits/README.md): `op-<codename>.js` keyed by the charId,
|
||||
registered in `OPERATOR_KIT_FILES`, a test `test/content/op_<codename>.test.js`, the fidelity checklist in the pull
|
||||
request. All 71 operators of the current pool have kits; improvements follow the same checklist (GitHub issue #136).
|
||||
@@ -170,6 +170,7 @@ npm start # 启动服务器:http://localhost:3000
|
||||
| [docs/WINDOWS.md](docs/WINDOWS.md) | Windows 便携包:怎么打一份「零安装」包(`scripts/make-windows-bundle.mjs`)、包里放了什么、授权注意事项 |
|
||||
| [docs/DESIGN.md](docs/DESIGN.md) | 架构与契约(英文):技术栈、目录分工、网络协议、渲染与 UI、各次试玩后的规则修订 |
|
||||
| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | 代码地图(英文):服务器、联机协议和前后端共用的战斗模拟,0.2.0 重构后的目录,数据流,黄金结果与导入边界,常见改动从哪里入手 |
|
||||
| [CONTRIBUTING.md](CONTRIBUTING.md) | 参与贡献:准备环境、运行测试、忠实原则、提交与 PR 约定、怎样添加自选干员(中文,末尾附英文摘要) |
|
||||
| [docs/SIM.md](docs/SIM.md) | 战斗模拟引擎参考(英文):钩子、技能描述格式、职业默认行为 |
|
||||
| [docs/META.md](docs/META.md) | 对局与经济引擎(英文):回合流程、商店、联防、最终攻势的实现细节 |
|
||||
| [docs/DATA.md](docs/DATA.md) | 由官方数据表生成的游戏数据(英文) |
|
||||
@@ -226,6 +227,7 @@ GOLDEN_FULL=1 node --test test/golden.test.js # 黄金结果:固定
|
||||
|
||||
欢迎提 Issue 反馈 bug、与官方规则不一致的地方或改进建议,也欢迎提交 Pull Request:
|
||||
|
||||
- 准备环境、运行测试、忠实原则和提交约定见 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
||||
- 提交前请运行 `node --test`,并同步更新相关文档;文档使用简体中文,代码与注释使用英文。
|
||||
- 提交的代码将以 GPL-3.0-or-later 发布。
|
||||
- 请不要提交任何游戏素材文件(`public/assets/` 等目录已被 `.gitignore` 排除)。
|
||||
@@ -241,3 +243,4 @@ An **unofficial, non-commercial fan remake** of Arknights' seasonal auto-chess t
|
||||
- **Play with friends:** create a co-op room and share the 4-letter key or the `?room=KEY` link. On a LAN, use the address printed at start; otherwise use a virtual-LAN tool, a tunnel or a VPS — see [docs/DEPLOY.md](docs/DEPLOY.md).
|
||||
- **Disclaimer:** not affiliated with or endorsed by Hypergryph or Yostar. All Arknights names, art, audio, text and data are © their respective owners and are **not** covered by this project's GPL licence. For study and personal non-commercial use only — no selling, paid distribution, paid servers or monetisation of any kind. Content will be removed on request of the rights holders. Provided "as is", without warranty.
|
||||
- **License:** code GPL-3.0-or-later ([LICENSE](LICENSE)); game assets excluded.
|
||||
- **Contributing:** [CONTRIBUTING.md](CONTRIBUTING.md) (an English summary at its end); the code map is [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
|
||||
@@ -4,7 +4,7 @@ For a newcomer who wants to find their way before changing something. This file
|
||||
in [DESIGN.md](DESIGN.md) (§5 the battle engine, §6 the match, §8 the protocol, §9 rendering, §14 client-side combat);
|
||||
the details in [SIM.md](SIM.md) (battle engine, hooks, SkillSpec), [META.md](META.md) (match engine, prep-phase
|
||||
effects), [DATA.md](DATA.md) (generated data), [ASSETS.md](ASSETS.md) (art and audio) and [I18N.md](I18N.md)
|
||||
(languages). How to set up and run the tests: [README · 开发与测试](../README.md#开发与测试).
|
||||
(languages). How to set up, test and send a change: [CONTRIBUTING.md](../CONTRIBUTING.md).
|
||||
|
||||
## 1. What runs where
|
||||
|
||||
@@ -158,7 +158,7 @@ public/assets/ ─────────▶ public/js/render/, public/js/audio
|
||||
- **Lint and types.** `npm run lint` is a correctness-only ESLint (`eslint.config.js`: warnings are allowed, errors
|
||||
are not); `npm run typecheck` checks a JSDoc slice (`jsconfig.json`; `types/README.md` says how to widen it). CI
|
||||
runs both once, and the tests on Ubuntu and Windows with Node 22 and 24 (`.github/workflows/ci.yml`).
|
||||
- **Tests.** `node --test` runs every suite that needs no browser; the browser suites are opt-in (README · 开发与测试).
|
||||
- **Tests.** `node --test` runs every suite that needs no browser; the browser suites are opt-in ([CONTRIBUTING.md](../CONTRIBUTING.md) §2).
|
||||
The battle harness `test/helpers/battleHarness.js` (SIM.md §10) sets up a battle in a few lines.
|
||||
|
||||
## 6. Where to start
|
||||
@@ -181,4 +181,4 @@ public/assets/ ─────────▶ public/js/render/, public/js/audio
|
||||
| HTTP, headers, static routes | `server/http/` | `test/version.test.js`, `test/client-static.test.js` |
|
||||
|
||||
Before changing a rule, read the DESIGN section that owns it (module headers cite their sections) and the official
|
||||
source; a rule no source settles is marked `[ASSUMED]` (`server/sim/content/kits/README.md`, "The fidelity rule").
|
||||
source; a rule no source settles is marked `[ASSUMED]` ([CONTRIBUTING.md](../CONTRIBUTING.md) §3).
|
||||
@@ -1,7 +1,7 @@
|
||||
// The newcomer docs name only paths that exist: every repository path in docs/ARCHITECTURE.md — inline code, the
|
||||
// diagrams in fenced blocks — and every relative Markdown link. A path with a placeholder (`<codename>`, `{a,b}`) or a
|
||||
// glob (`*`) is checked up to its last fixed directory; a path ending in `/` must be a directory. Outputs that a fresh
|
||||
// checkout does not have (downloaded art, vendored libraries, the official-data cache) are exempt.
|
||||
// The newcomer docs name only paths that exist: every repository path in docs/ARCHITECTURE.md and CONTRIBUTING.md
|
||||
// (inline code, the diagrams and commands in fenced blocks) and every relative Markdown link. A path with a
|
||||
// placeholder (`<codename>`, `{a,b}`) or a glob (`*`) is checked up to its last fixed directory; a path ending in `/`
|
||||
// must be a directory. Outputs that a fresh checkout does not have (downloaded art, vendored libraries) are exempt.
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { existsSync, readFileSync, statSync } from 'node:fs';
|
||||
@@ -9,7 +9,8 @@ import { dirname, join, normalize } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const DOCS = ['docs/ARCHITECTURE.md'];
|
||||
/** Each document with the least number of paths it names (a guard against an extraction that finds nothing). */
|
||||
const DOCS = [['docs/ARCHITECTURE.md', 100], ['CONTRIBUTING.md', 15]];
|
||||
/** A repository path starts with one of these top-level directories … */
|
||||
const TOP = /^(?:server|shared|public|data|tools|test|docs|types|scripts|\.github)\//;
|
||||
/** … or is one of these root files (README.md is left out: a bare `README.md` often means a folder's own). */
|
||||
@@ -52,12 +53,12 @@ function missing(path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (const docPath of DOCS) {
|
||||
for (const [docPath, least] of DOCS) {
|
||||
const md = readFileSync(join(ROOT, docPath), 'utf8');
|
||||
|
||||
test(`${docPath}: every repository path it names exists`, () => {
|
||||
const paths = namedPaths(md);
|
||||
assert.ok(paths.length >= 40, `only ${paths.length} paths found — is the extraction broken?`);
|
||||
assert.ok(paths.length >= least, `only ${paths.length} paths found — is the extraction broken?`);
|
||||
const bad = paths.map(missing).filter(Boolean);
|
||||
assert.deepEqual(bad, [], `stale paths in ${docPath}`);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user