diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index fd35dd97..601ff23b 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -31,6 +31,11 @@ export default defineConfig({ link: '/playoffs', activeMatch: '/playoffs' }, + { + text: 'Technical', + link: '/technical', + activeMatch: '/technical' + }, { text: 'Contribute', link: '/contribute', @@ -148,6 +153,17 @@ export default defineConfig({ { text: 'Private Rooms', link: '/private_rooms' }, { text: 'Weekly Race', link: '/weekly_race' }, { text: 'Achievements', link: '/achievements' }, + ] + } + ], + '/technical': [ + { + text: 'Technical', + link: '/', + base: '/technical', + items: [ + { text: 'REST API', link: '/api' }, + { text: 'Ranked Spectator Accounts', link: '/ranked_spectator_accounts' }, { text: 'Replay System', link: '/replay_system' }, ] } diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css index 61364760..0dc56174 100644 --- a/docs/.vitepress/theme/style.css +++ b/docs/.vitepress/theme/style.css @@ -48,4 +48,14 @@ pointer-events: none; opacity: 0; transition: opacity 0.5s ease-in-out; -} \ No newline at end of file +} + +@media (min-width: 1280px) { + .VPHome .VPFeatures .container { + max-width: 1440px; + } + + .VPHome .VPFeatures .item.grid-4 { + width: 20%; + } +} diff --git a/docs/gameplay/index.md b/docs/gameplay/index.md index 6f799a61..6363450e 100644 --- a/docs/gameplay/index.md +++ b/docs/gameplay/index.md @@ -8,7 +8,6 @@ This section covers how Ranked matches work, as wells as information relating to - [RNG Standardization](./rng) - [Elo & Ranks](./elo_and_ranks) - [Match-Making](./matchmaking) -- [Private Rooms](./private_rooms) -- [Weekly Race](./weekly_race) -- [Achievements](./achievements) -- [Replay System](./replay_system) +- [Private Rooms](./private_rooms) +- [Weekly Race](./weekly_race) +- [Achievements](./achievements) diff --git a/docs/gameplay/weekly_race.md b/docs/gameplay/weekly_race.md index 071e6f6b..30544208 100644 --- a/docs/gameplay/weekly_race.md +++ b/docs/gameplay/weekly_race.md @@ -10,7 +10,7 @@ Weekly Race resets Monday at 12:00am UTC. The Weekly Race menu features the current week's leaderboard, up to the top 50 runs. The current week is listed at the top of the menu, with the option to view previous weeks' leaderboards. The time and date that Weekly Race resets is displayed in the user's local timezone, though note that the listed timezone may not match the timezone the user currently observes if in daylight savings or summer time. -Clicking a run on the leaderboard gives the option to watch the [replay](./replay_system) for the run. +Clicking a run on the leaderboard gives the option to watch the [replay](../technical/replay_system) for the run. ## Gameplay diff --git a/docs/index.md b/docs/index.md index 88161159..acc17e19 100644 --- a/docs/index.md +++ b/docs/index.md @@ -30,10 +30,14 @@ features: details: MCSR Ranked Playoffs are our official Minecraft Speedrunning Tournament. Learn more about it here. link: /playoffs linkText: Learn more + - title: Technical + icon: + details: API references, RSA tooling, replay systems, and other docs for building MCSR Ranked tools. + link: /technical + linkText: Learn more - title: For Contributors icon: details: Are you interested in contributing to our pages? Learn more about guidelines and tutorials here. link: /contribute linkText: Learn more --- - diff --git a/docs/technical/api.md b/docs/technical/api.md new file mode 100644 index 00000000..9fe2fcaa --- /dev/null +++ b/docs/technical/api.md @@ -0,0 +1,72 @@ +# REST API + +## Explanation +MCSR Ranked has a public REST API for reading ranked data such as users, matches, live data, leaderboards, and weekly races. + +The full API reference is available at [docs.mcsrranked.com](https://docs.mcsrranked.com/). This page is a wiki overview for finding the API and understanding the core conventions. + +## Endpoints +The API can be accessed from either base URL: + +```text +https://api.mcsrranked.com/ +https://mcsrranked.com/api/ +``` + +The dedicated API domain is preferred for new tools. + +## OpenAPI +The OpenAPI document is available here: + +```text +https://api.mcsrranked.com/openapi.yaml +``` + +Use the OpenAPI document for generated clients, schema validation, or checking the exact request and response formats. + +## Rate limits +Most endpoints are limited to `500` requests per `10` minutes. + +For an expanded rate limit, create a ticket in the MCSR Ranked Discord and request an API key. + +## Authentication +Many endpoints do not require authentication. + +For endpoints that need authentication, open an MCSR Ranked instance and use: + +```text +Profile > Settings > Generate & Copy API Private Key +``` + +Pass the key in the request header: + +```text +Private-Key: YOUR_KEY_HERE +``` + +## Time values +The API uses two common numeric time formats: + +| Type | Format | +| --- | --- | +| `Date` | Unix timestamp in seconds. | +| `Time` | Duration in milliseconds. | + +This differs from `spectate_match.json`, where `startTime` is a Unix timestamp in milliseconds. + +## Common objects +The API reference defines shared object types used across endpoints. + +| Object | Notes | +| --- | --- | +| `UserProfile` | UUID, nickname, role type, Elo rating/rank, and country. | +| `Achievement` | Achievement ID, earned date, payload data, level, and progress fields. | +| `MatchInfo` | Match metadata, players, spectators, result, seed information, VODs, timelines, and replay availability. | +| `MatchSeed` | Filtered seed metadata such as overworld type, nether type, End tower heights, and variations. | +| `UserIdentifier` | User lookup value. Can be UUID, nickname, or `discord.ID`. | +| `MatchType` | Numeric match type enum. | + +## Match data +Historical match data comes from the API. Live RSA event data comes from `spectate_match.json`. + +Use the API when you need stored match data, player profiles, standings, leaderboards, or public replay availability. Use RSA live match data when you need low-latency event overlay state from a private room, especially when `No Logs Mode` is enabled. diff --git a/docs/technical/index.md b/docs/technical/index.md new file mode 100644 index 00000000..7fabea83 --- /dev/null +++ b/docs/technical/index.md @@ -0,0 +1,9 @@ +# Technical Documentation + +This section covers MCSR Ranked systems that are most useful for event organizers, overlay developers, and tool developers. + +### Index + +- [REST API](./api) +- [Ranked Spectator Accounts](./ranked_spectator_accounts) +- [Replay System](./replay_system) diff --git a/docs/technical/ranked_spectator_accounts.md b/docs/technical/ranked_spectator_accounts.md new file mode 100644 index 00000000..ff314d27 --- /dev/null +++ b/docs/technical/ranked_spectator_accounts.md @@ -0,0 +1,352 @@ +# Ranked Spectator Accounts + +## Explanation +Ranked Spectator Accounts, usually called RSA, are special accounts for public tournaments, competitions, and events. They are meant for event production, not normal ranked or casual play. + +RSA can host larger private rooms, use event production features, open live spectator tools, and write live match data for overlays. + +## Getting RSA +To request RSA, join the MCSR Ranked Discord and create a ticket. Include detailed event information, such as: + +- Tournament or event name +- Main stream link +- Event date and time +- Minecraft account names that need RSA +- How long the accounts need RSA access + +After the event is reviewed, staff will ask for the RSA duration and Minecraft nicknames. During the approved period, RSA can be used for testing and for the event. + +After an account receives RSA access, restart the Minecraft instance before trying to use RSA features. If the game was already open when access was granted, the client may not pick up the updated account state until restart. + +## Private room features +By default, private rooms have a max player limit of `32` plus the host. If the private room host is an RSA, the room can be increased up to `100` players. + +If `No Logs Mode` is enabled in private room rules, the room limit can be increased up to `300` players. In this mode, match data is not sent to the API and the filtered seed option does not work. + +RSA also has supporter-style private room features, such as custom private room names. + +If an RSA generates a ranked world, cheats are enabled for that world. + +## Auto-accept join requests +RSA can automatically accept private room join requests from an `auto_accept.txt` file. This works with the private room `Join Requests` feature. + +The file is located in the MCSR Ranked global data folder: + +```text +/mcsrranked/auto_accept.txt +``` + +Typical paths are: + +| OS | Path | +| --- | --- | +| Windows | `C:/Users/USER_NAME/mcsrranked/auto_accept.txt` | +| macOS | `/Users/USER_NAME/mcsrranked/auto_accept.txt` | +| Linux | `/home/USER_NAME/mcsrranked/auto_accept.txt` | + +If the file does not exist, the mod creates an empty file. + +Add one player per line. Each line can be a nickname or UUID. UUIDs can be written with or without dashes. + +```text +RedLime +f67a12a9-aabf-4f48-bb0d-28d26e09562e +c4d2cce5a4594868b61bdd717d178636 +``` + +The mod normalizes entries by removing dashes, lowercasing them, and trimming whitespace. Empty lines are ignored. The file is reloaded when its modification time changes. + +## Live spectator tools +RSA has live spectator tools for following match progress during an online match. + +### Match spectator command +Spectators can open match data screens with `/matchspectate`: + +```text +/matchspectate timelines +/matchspectate split_compare +/matchspectate players +``` + +These commands are only available while an online match is playing and the local player is a spectator. + +| Command | Screen | +| --- | --- | +| `/matchspectate timelines` | Match timeline screen. | +| `/matchspectate split_compare` | Split comparison screen. | +| `/matchspectate players` | Match players screen. | + +These screens use the match timeline list and non-spectator player list. Spectator accounts are excluded from the compared player list. + +While spectating a playing match, the mod also keeps the matching spectator entries available in the game mode selection menu: + +- Match timelines +- Match splits comparison +- Match players + +### Live replay controller +RSA spectators can also use live replay tooling. In this mod version, the separate controller window is only initialized on Windows desktop environments. It is not created in headless environments. + +When available, the controller is a separate Swing window titled `Replay Remote Controller`. + +The controller is shown for spectator accounts when the match reaches the `START_GEN` status. The window is set to always stay on top while the match is starting. When the match reaches `END`, the controller is no longer forced on top, is sent behind other windows, and resets its match/replay state. + +The controller includes: + +- A status label showing either the current match status or current elapsed time. +- A player list. Clicking a player changes the replay focus to that player. +- `Follow Camera` +- `Follow Dimension` +- `Ghost Mode` +- `Display Name Tag` +- Live replay time controls with a numeric second value from `5` to `300`. +- `Before`, `After`, and `Live` buttons for moving around the live replay timeline. + +The live replay controller is separate from `spectate_match.json`. The controller changes the in-game spectator/replay view. The JSON file is the data source for external overlays and tools. + +There is also an in-game replay spectator menu. When a replay processor is active, the mod replaces the normal spectator command group with ranked replay controls. These controls are for interacting with the replay view from inside Minecraft. + +## Live match data +RSA writes `spectate_match.json` while an online match is active. This file is intended for external stream overlays and tournament tools. + +This is especially important when `No Logs Mode` is enabled, because that mode does not send match data to the API. + +The file is a snapshot of the current match state. It is not an event log. Each write replaces the whole file with the latest data. + +In a normal launcher setup, the file is written here: + +```text +/mcsrranked/spectate_match.json +``` + +Typical paths are: + +| OS | Path | +| --- | --- | +| Windows | `C:/Users/USER_NAME/mcsrranked/spectate_match.json` | +| macOS | `/Users/USER_NAME/mcsrranked/spectate_match.json` | +| Linux | `/home/USER_NAME/mcsrranked/spectate_match.json` | + +The mod uses the `/mcsrranked` folder when it is readable and writable. If it is not, the mod falls back to `/mcsrranked`. + +### Reading the file safely +The mod writes the whole JSON file asynchronously. There is no lock file and no explicit file lock, so a reader can catch the file while it is being written. + +Consumers should keep the last successfully parsed snapshot. A failed read or failed JSON parse should not clear the overlay. + +A safe read loop is: + +1. Check that `spectate_match.json` exists. +2. Optionally skip work if the file modification time has not changed. +3. Read the entire file as UTF-8. +4. Parse the JSON. +5. Validate the expected top-level fields. +6. If parsing succeeds, replace the in-memory snapshot. +7. If parsing fails, keep using the previous good snapshot. + +Some filesystems have coarse modification times. If exact change detection matters, compare file size, hash the contents, or parse at the interval your tool uses. + +### Top-level format +The JSON has this general shape: + +```json +{ + "matchType": "PRIVATE", + "category": "ANY", + "gameMode": "default", + "startTime": 1779153579055, + "players": [], + "completes": [], + "inventories": {}, + "counts": {}, + "timelines": [] +} +``` + +| Field | Description | +| --- | --- | +| `matchType` | Match type. Known values are `CASUAL`, `RANKED`, `PRIVATE`, `EVENT`, and `RUSH`. | +| `category` | Speedrun IGT category ID, such as `ANY`. | +| `gameMode` | Match game mode type. The normal mode is `default`. The mod also contains `most_percent` and `scout_route`. | +| `startTime` | Match start timestamp in Unix milliseconds once populated. Do not assume it is meaningful before the match start flow. | +| `players` | Player roster. | +| `completes` | Finished players, sorted by completion time ascending. | +| `inventories` | Latest tracked inventory and stat snapshot for each player, keyed by UUID. | +| `counts` | Reserved in this mod version. The writer creates it but does not populate it. | +| `timelines` | Timeline events in match-state list order. | + +Use the 32-character UUID string, without dashes, to join `players`, `timelines`, `inventories`, and `completes`. + +### Match lifecycle +Before the run starts, the file can contain match metadata and the player roster. Once the run is underway and `startTime` is set, treat it as stable. + +During play: + +- `timelines` grows as events arrive. +- Existing timeline entries should be treated as immutable. +- `inventories[uuid]` is replaced as inventory and stat updates arrive. +- Inventory keys can be missing or present with value `0`. +- `completes` fills as players finish. + +For resets, use a timeline entry with: + +```text +type = "projectelo.timeline.reset" +``` + +Do not infer a reset only from inventory values returning to zero. + +After the match, the file may remain on disk. Detect a new match by changes to `startTime`, the roster, or your event setup. Do not assume the file disappears. + +### Players +Each `players` entry looks like this: + +```json +{ + "uuid": "f67a12a9aabf4f48bb0d28d26e09562e", + "nickname": "ignBees", + "roleType": 0, + "eloRate": null, + "eloRank": null, + "country": null +} +``` + +| Field | Description | +| --- | --- | +| `uuid` | Player UUID as a 32-character string with no dashes. | +| `nickname` | Display name. | +| `roleType` | Integer role badge/type used by the mod UI. | +| `eloRate` | Elo rating integer, or `null` when not present. | +| `eloRank` | Elo rank integer, or `null` when not present. | +| `country` | Country code string, or `null` when not present. | + +Consumers should expect `uuid`, `nickname`, and `roleType` for each player. In private or event-style contexts, `eloRate`, `eloRank`, and `country` are commonly `null`. + +The JSON is written with null values included. + +### Timelines +Each `timelines` entry looks like this: + +```json +{ + "uuid": "f67a12a9aabf4f48bb0d28d26e09562e", + "type": "story.mine_stone", + "time": 76026, + "data": ["16", "60", "65"], + "shown": true +} +``` + +| Field | Description | +| --- | --- | +| `uuid` | UUID of the player that produced the event. | +| `type` | Event type string. | +| `time` | Event time in milliseconds from the match timer. | +| `data` | Event-specific payload values as strings. JSON null values can also appear. | +| `shown` | Whether the event was shown in the in-game timeline UI. | + +Timeline types: + +| Type | Meaning | +| --- | --- | +| `projectelo.timeline.reset` | Reset event. | +| `projectelo.timeline.complete` | Completion event. The mod renders `data[0]` as a text argument and parses `data[1]` as a millisecond time for display. | +| `projectelo.timeline.eliminate` | Elimination event. | +| Any type not starting with `projectelo` | Minecraft advancement event. | + +Advancement display names resolve through: + +```text +advancements..title +``` + +For example, `story.mine_stone` maps to `advancements.story.mine_stone.title`. + +If a timeline type ends in `.root`, the mod treats it as an advancement root event. + +### Inventories +`inventories` is keyed by player UUID: + +```json +{ + "f67a12a9aabf4f48bb0d28d26e09562e": { + "oak_planks": 3, + "planks": 3, + "iron_nugget": 0, + "white_bed": 0, + "beds": 0, + "blazeKills": 0, + "piglinBarters": 0 + } +} +``` + +Tracked item keys are Minecraft item IDs without the `minecraft:` namespace. The writer includes entries currently present in the tracked inventory map, then always appends: + +- `blazeKills` +- `piglinBarters` + +Tracked item families and items include: + +```text +planks/log variants, iron_nugget, white_bed, glowstone_dust, diamond, +gold_ingot, splash_potion, potion, blaze_powder, iron_ingot, ender_pearl, +obsidian, respawn_anchor, crying_obsidian, glowstone, string, ender_eye, +gold_block, wool variants, blaze_rod +``` + +Optional aliases: + +| Alias | Written when the tracked map contains | +| --- | --- | +| `wools` | `white_wool` | +| `beds` | `white_bed` | +| `planks` | `oak_planks` | +| `logs` | `oak_log` | + +When an alias is missing, fall back to the representative item key or `0`. + +### Completes +Each `completes` entry looks like this: + +```json +{ + "player": "f67a12a9aabf4f48bb0d28d26e09562e", + "time": 738123 +} +``` + +| Field | Description | +| --- | --- | +| `player` | UUID matching a player in `players`. | +| `time` | Completion time in milliseconds. | + +Players with completion time `0` are not included. The array is sorted by `time` ascending before writing. + +### Overlay usage +Common ways to consume the file: + +- Use `players` as the roster and index players by UUID. +- Use `timelines` for live events such as advancements, resets, eliminations, and finishes. +- Sort timeline entries by `time` if your display needs deterministic ordering. +- Use `completes` for final standings. +- Use `inventories[uuid]` for live item counters. +- Prefer inventory aliases like `beds`, `logs`, `planks`, and `wools` when present. +- Ignore `counts` in this mod version. + +Timeline and completion times in this JSON are milliseconds. `startTime` is a Unix timestamp in milliseconds. The mod converts timeline time to game ticks with: + +```text +ceil(time / 50) +``` + +Use milliseconds for sorting and display unless your overlay specifically needs ticks. + +## RSA rules +RSA is for approved event and tournament use. It is not for general play. + +- Follow the MCSR Ranked Guidelines. +- Do not use RSA for purposes outside the approved event or testing period. +- Do not share RSA access with anyone who was not directly approved by staff. +- Do not use an RSA account to play ranked or casual mode. diff --git a/docs/gameplay/replay_system.md b/docs/technical/replay_system.md similarity index 100% rename from docs/gameplay/replay_system.md rename to docs/technical/replay_system.md