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

4.1 KiB

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 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 and the Common Configuration, you've just added full support for a game to Tachi! Nice job!