Files
zkldi_Tachi/old-docs/docs/game-support/client-impl.md
T
zk e363bd2532 docs: migrate from mkdocs to mdbook (#1558)
* docs: migrate from mkdocs to mdbook

- Rename old mkdocs docs/ to old-docs/ for reference
- Set up new docs/ with mdbook (book.toml + src/ tree)
- Mirror full nav structure from mkdocs.yml into SUMMARY.md
- Add Justfile-docs with docs-serve, docs-build, docs-check, docs-install recipes
- Import Justfile-docs from root Justfile
- Rewrite .github/workflows/docs.yml: build step uses taiki-e/install-action
  to install mdbook, split into separate build + deploy jobs, PR builds
  run the check step too

* ci(docs): pin actions to SHAs, install mdbook via release binary

* ci(docs): install mdbook from apt instead of curling a release binary

* dev: replace mkdocs python stack with mdbook in dev image

* ci(docs): apt only works on Debian; restore release binary install for Ubuntu CI

* docs: fix duplicate file entries in SUMMARY.md

* docs: remove docs-install recipe

* docs: remove site-url from book.toml to fix asset loading

* dev: install mdbook from upstream release binary, not Debian apt

The Debian package (0.4.x+ds) strips bundled font assets, leaving the
built site without fonts/fonts.css. Use the upstream tarball (same as CI)
so the theme is complete. Handles x86_64 and aarch64.

* docs: vendor mdbook tarballs in dev/mdbook/, install from there

Dockerfile.dev uses COPY + tar to install the right arch at build time.
CI extracts the x86_64 tarball directly from the checkout.
No network access required for either — and no stripped-fonts Debian package.

* fix: unwritten
2026-05-22 20:43:07 +01:00

166 lines
4.1 KiB
Markdown

# Client Implementation
As you probably can expect, the client implementation is entirely frontend-related stuff.
This includes things like how to render score tables for this GPT, and how to colour
in enums.
## Where do Client Implementations go?
Implementations should be written in `client/src/lib/game-implementations.tsx`.
It's fine to inline implementations here, but feel free to break out into a separate file (See what IIDX does for a reference) if you need to.
## `enumColours`
For all the ENUM metrics in this game, give all of their values colours.
!!! tip
The `COLOUR_SET` global is useful for this, as it provides consistent identity
throughout tachi.
## `enumIcons`
What [Font Awesome v5](https://fontawesome.com/v5/search) icon should we use to
represent each enum?
## `difficultyColours`
If this game uses fixed difficulties, give each difficulty name a colour here.
## `classColours`
Give each class value a colour. These are used to render the class badges.
## `ratingSystems`
Although all games have `level` and `levelNum`, some optional properties may be useful here.
This is an array of functions that take a chart and return information about it.
These are used to sort charts in tables when the `difficulty` header is used to sort.
!!! example
The `CreateRatingSys` util is used for this:
```ts
[
CreateRatingSys(
"NC Tier",
"Tierlist Ratings for Normal Clears.",
(c) => c.data.ncTier?.value,
(c) => c.data.ncTier?.text,
(c) => c.data.ncTier?.individualDifference
),
CreateRatingSys(
"HC Tier",
"Tierlist Ratings for Hard Clears.",
(c) => c.data.hcTier?.value,
(c) => c.data.hcTier?.text,
(c) => c.data.hcTier?.individualDifference
),
CreateRatingSys(
"EXHC Tier",
"Tierlist Ratings for EX-HARD Clears.",
(c) => c.data.exhcTier?.value,
(c) => c.data.exhcTier?.text,
(c) => c.data.exhcTier?.individualDifference
),
]
```
!!! example
ITG also uses this to leverage the sorting abilities of this: we want to sort
things on level, but if the level is the same, we want to break ties on BPM.
```ts
[
CreateRatingSys(
"BPM",
"How fast are the streams in this chart?",
(c) => c.data.streamBPM,
(c) => c.data.streamBPM?.toString()
),
]
```
## `scoreHeaders`
What should the headers be for the score cells when rendering scores for this GPT?
!!! example
```ts
[
["Score", "Score", NumericSOV((x) => x.scoreData.percent)],
["Deltas", "Deltas", NumericSOV((x) => x.scoreData.percent)],
["Lamp", "Lamp", NumericSOV((x) => x.scoreData.enumIndexes.lamp)],
]
```
will correspond to the headers in the red box
![](../images/headers.png)
## `scoreCoreCells`
When rendering a score row, how should we render the actual score information cells?
This function gets `sc`, which is either a score or a PB for this GPT, and `chart`; the chart this score was on.
!!! important
The amount of cells returned should be **EXACTLY** the same length as the headers.
!!! example
```ts
({ sc }) => (
<>
<MillionsScoreCell
score={sc.scoreData.score}
grade={sc.scoreData.grade}
colour={GetEnumColour(sc, "grade")}
/>
<PopnJudgementCell score={sc} />
<PopnLampCell score={sc} />
</>
),
```
will correspond to the cells in these columns.
![](../images/cells.png)
## `ratingCell`
How should we render the rating cell for this GPT?
This is a function that takes in the aforementioned `sc` and `chart`, alongside `rating`, which is the currently selected score rating algorithm.
!!! example
```ts
({ sc, chart, rating }) => (
<>
{rating === "blockRating" ? (
<td>
<strong>
{chart.data.rankedLevel === null
? "Unranked Chart."
: sc.calculatedData.blockRating === null
? "Failed"
: sc.calculatedData.blockRating}
</strong>
</td>
) : (
<RatingCell score={sc} rating={rating} />
)}
</>
),
```
will correspond to the cells in this column.
![](../images/ratingcell.png)
## That's it!
Congrats! If you've done this, the [Server Implementation](./server-impl.md) and the [Common Configuration](./common-config/index.md), you've just added full support for a game to Tachi! Nice job!