endfield-mcp
Version:
MCP Server for Arknights: Endfield (TypeScript / stdio + Streamable HTTP)
152 lines (151 loc) • 6.75 kB
TypeScript
/**
* Dataset specifications for Endfield-MCP.
*
* Each dataset describes one GitHub Release asset from the self-hosted
* EndFieldGameData mirror. The sync layer (`./sync.ts`) consumes these
* specs without knowing anything about Endfield's data shape.
*
* ## Mirror layout
*
* The mirror publishes one Release asset: `endfield-tables.zip`. Its
* internal structure (mirrored from endfield_research_kit's
* `export_full/structured/StreamingAssets/Table/`, with a cleaner top-level
* split between game tables and localization):
*
* ```
* endfield-tables.zip
* ├── tables/ # 10 core game tables
* │ ├── CharacterTable.json
* │ ├── EnemyTable.json
* │ ├── EnemyTemplateTable.json
* │ ├── EnemyDisplayInfoTable.json
* │ ├── EquipTable.json
* │ ├── EquipItemTable.json
* │ ├── CharProfessionTable.json
* │ ├── CharTypeTable.json
* │ ├── CharacterTagTable.json
* │ └── ItemTable.json
* └── i18n/ # 5 localization tables
* ├── CN.json (← I18nTextTable_CN.json, renamed for brevity)
* ├── EN.json
* ├── JP.json
* ├── TC.json
* └── KR.json
* ```
*
* ## Language coverage
*
* Five languages chosen for the fan-creation communities they serve:
* CN (Simplified Chinese, project default), EN, JP, TC (Traditional
* Chinese), KR. Each is ~9-12MB; combined with the 10 core tables the
* asset is ~64MB uncompressed (~15-20MB zipped).
*
* ## Schema notes
*
* Endfield's data architecture separates values from localization: most
* tables store `{id, text}` objects where `text` is empty and `id` is an
* int64 hash. The actual string lives in the i18n table under that hash.
* The `data/texts.ts` module owns that lookup; readers stay ignorant of
* which language is active.
*/
import { type ReleaseArchiveSpec, type ReleaseSpec } from "./sync.js";
/**
* GitHub coordinates of the self-hosted Endfield data mirror.
*
* The mirror is live (3aKHP/EndFieldGameData) with two published Releases:
* v0.2.0 (endfield-tables.zip) and v0.3.0 (endfield-story-CN.zip). These
* coordinates feed the sync layer's download URL construction; changing
* them here re-points all dataset fetches.
*/
export declare const MIRROR_OWNER = "3aKHP";
export declare const MIRROR_REPO = "EndFieldGameData";
/**
* The 10 core game tables every release must ship.
*
* Pinned from the first endfield_research_kit export (2026-06). These cover
* the v0.2 character-focused scope; items/enemies/equips ship too so later
* minor versions can add domains without a mirror re-release.
*/
export declare const TABLE_FILES: readonly string[];
/**
* The five localization tables every release must ship.
*
* File names are shortened from the upstream `I18nTextTable_<LANG>.json`
* convention to keep zip paths tidy. Language codes match the upstream
* suffix: CN, EN, JP, TC, KR.
*/
export declare const I18N_FILES: readonly string[];
/** All files the release asset must contain (tables + localization). */
export declare const ALL_REQUIRED_FILES: readonly string[];
/** The five languages the mirror ships, in canonical order. */
export declare const SUPPORTED_LANGUAGES: readonly string[];
/** Default language when no preference is given. */
export declare const DEFAULT_LANGUAGE = "CN";
/** Identifies one upstream Release dataset. */
export interface ReleaseDatasetSpec {
/** Stable id for logging / cache partitioning, e.g. "tables". */
readonly datasetId: string;
readonly owner: string;
readonly repo: string;
/** Asset filename in the GitHub Release, e.g. "endfield-tables.zip". */
readonly assetName: string;
/** Paths that must exist inside the asset zip / extracted root. */
readonly requiredFiles: readonly string[];
}
/** Build a {@link ReleaseSpec} for plain-zip download (no extraction). */
export declare function releaseSpecForDataset(dataset: ReleaseDatasetSpec, localZip: string): ReleaseSpec;
/** Build a {@link ReleaseArchiveSpec} for download-and-extract. */
export declare function archiveSpecForDataset(dataset: ReleaseDatasetSpec, localZip: string, localRoot: string): ReleaseArchiveSpec;
/**
* GameData tables — the core v0.2.0 dataset.
*
* Bundles the 10 core game tables and 5 localization tables into one
* ~15-20MB zip asset. One Release per upstream game update; tag is bare
* semver (v0.2.0, v0.2.1, ...).
*/
export declare const GAMEDATA_TABLES: ReleaseDatasetSpec;
/**
* Story dialogue bundle (CN) — the v0.3.0 dataset.
*
* Ships the catalog (index/missions/actors/search) and all 9271 conv
* files. ~19MB zipped. requiredFiles only lists the catalog — conv/
* entries are read on-demand by story reader (per-scene fetch), not
* validated at startup (9271 files would make the check expensive).
*/
export declare const STORY_REQUIRED_FILES: readonly string[];
export declare const STORY_CN: ReleaseDatasetSpec;
/**
* Worldview bundle — the v0.4.0 dataset.
*
* In-game PRTS archive system (documents/records/multimedia/investigations)
* plus the in-game encyclopedia (wiki entries/categories/groups). Schema
* differs from the tables/story bundles in one way: most Prts entries carry
* a `contentId` that is a **string key** (e.g. "text_v0d8_10"), not a direct
* int64 hash. That key resolves through the root-level RichContentTable.json:
* `RichContentTable[contentId]` = `{title: {id, text}, contentList: [{content: {id, text}}, ...]}`,
* and each segment's `content.id` is an int64 hash looked up in i18n/CN.json
* (shipped in the v0.2.0 tables bundle). The i18n table is intentionally NOT
* re-bundled here.
*
* Layout (see local_dev/docs/worldview-mirror-build.md for the build spec):
*
* ```
* endfield-worldview.zip
* ├── prts/ (11 narrative tables)
* ├── wiki/ (4 encyclopedia tables — narrative subset only)
* └── RichContentTable.json (contentId → {title, contentList[]} bridge, root-level)
* ```
*
* ~2.7MB raw. The 6 mechanistic Wiki tables (craft/drop/tutorial) are
* deliberately excluded — v0.4 is lore-first (ROADMAP decision principle).
*/
export declare const WORLDVIEW_REQUIRED_FILES: readonly string[];
export declare const WORLDVIEW: ReleaseDatasetSpec;
/**
* Validate a downloaded dataset zip against its required-file list.
*
* Returns the list of missing required entries (empty = valid). Errors
* opening the zip are reported as a single descriptive entry so callers
* can surface them without distinguishing "corrupt" from "incomplete".
*/
export declare function validateDatasetZip(dataset: ReleaseDatasetSpec, zipPath: string): string[];