Fabric client bridge for servers: custom key bindings synced from the host, optional raw-key hooks for addons, and a handshake so Paper/Fabric servers can require this mod. Install only on servers that use KrepAPI. Needs Fabric API.
KrepAPI is a Fabric mod that lets multiplayer servers (via Paper/Spigot plugin messages or a Fabric server with the same protocol) define extra key bindings on your client, react to raw key input when allowed, and optionally require that you have this mod installed.
You only need KrepAPI if the server you play on uses it. It does nothing useful in single-player unless you run matching server-side code yourself.
Features
Server key bindings — The server sends a list of actions (ids, labels, default GLFW keys, categories). They show up under a dedicated controls category (e.g. “KrepAPI (this server)”). Pressing a bound key notifies the server.
Handshake — Server can verify protocol version and mod version; misconfigured or missing clients can be disconnected (server policy).
Raw key pipeline (for developers) — Companion mods can register listeners to observe keys early and optionally block vanilla handling for that event when the design allows (e.g. server-approved overrides).
Fabric dedicated server — The same mod jar can load on a Fabric server to handle the protocol without Paper.
Requirements
Minecraft — Match the version your KrepAPI build targets.
Fabric Loader
Fabric API
For server owners & developers
Server integration uses a small binary protocol (plugin channels / custom payloads). Reference implementations and docs live in the project repository (protocol layout, Paper sample plugin, client API).
License
See the distributed mod metadata and project license. All rights reserved unless otherwise stated in the Modrinth “License” field.
### Added
**Client update checks, download flow, and in-game update UI**
- **`UpdateChecker`** (both **`fabric-1-21`** and **`fabric-26-1`**): On client startup, asynchronously fetches [`.system/mod/versions.json`](https://raw.githubusercontent.com/RafaelK-F/KrepAPI/main/.system/mod/versions.json) from GitHub, resolves the **latest** mod version and the **JAR filename** for the running Minecraft version, compares semver against the installed mod using **`KrepapiBuildVersion`** (`:protocol`), and loads **every** listed version’s changelog markdown from `.system/mod/changelog/{version-with-dashes}.md` into `versionList` + `allChangelogs` for the menu.
- **`UpdateDownloader`**: On explicit user action only, downloads the update JAR over HTTP into **`mods/KrepAPI-update-{latestVersion}.jar`** with a **volatile progress** field; guards against overlapping downloads; surfaces errors without crashing the client.
- **`/krepapi menu`**: Client command to open the update / changelog screen at any time.
- **`KrepapiUpdateToast`**: One-time toast when an update is available; **opens the menu** (no direct download from the toast).
- **`KrepapiUpdateHud`**: Optional **`[KrepAPI ↑]`** indicator when an update is available; **click opens the menu** (no direct download from the HUD).
**Fabric 1.21.x — MoulConfig update menu**
- **MoulConfig** (`modern-1.21.11`) is **bundled via GradleUp Shadow**: dependency is **relocated** to `net.shik.krepapi.deps.moulconfig`, **`kotlin-stdlib`** is included for MoulConfig’s Kotlin internals, and a **hand-authored** `META-INF/services/...IMinecraft` entry points at the relocated platform class (Shadow does not rewrite service files by default).
- **Access widener** (`krepapi.accesswidener`): Merged the subset of MoulConfig widener entries that **validate** on Minecraft **1.21.11** with official Mojang mappings so the shaded UI can render without `IllegalAccessError`.
- **Screen**: **`KrepapiMenuScreen`** builds a MoulConfig **`GuiComponent`** tree (outer **`VANILLA`** panel, light **header strip**, full-width rule, two **`DARK_RECT`** columns). **Left**: “Changelogs” header, magenta underline, dashed separator, **clickable version rows** (cyan + underline when selected). **Right**: gray subtitle, **scaled headline** “Changelog entries for v…”, and formatted markdown body (yellow `#`, magenta `##`, green `###`, gray bullets; first top-level bullet after a `##` section highlighted in white to match the reference layout).
- **Headline scaling**: Dynamic `min(2×, fit-to-width)` so long titles are **not clipped** inside the scroll viewport (fixes truncated `v1.` style glitches).
- **ModMenu**: **`modImplementation`** ModMenu **17.0.0-beta.2**, dedicated **`KrepapiModMenuEntry`** → **`KrepapiModMenuIntegration`** config screen factory; **`fabric.mod.json`** extended with **`custom.modmenu`** (homepage, issues, GitHub link, `update_checker`).
- **Helper UI types** in **`net.shik.krepapi.client.ui.KrepapiMenuComponents`**: `FullWidthRuleComponent`, `VersionNavRow`, `ChangelogHeadlineComponent`, `StatusBadgeComponent`.
**Fabric 26.1.x — Vanilla update menu**
- **`KrepapiMenuScreen26`**: Same update semantics and markdown presentation using **vanilla** `Screen` widgets (no MoulConfig / Shadow on this loader).
**Structured debug logging (optional)**
- **Paper** (`KrepapiPaperPlugin`) and **Fabric server** networking: when **`debug-logging`** / JVM flag / marker file enables it, append **NDJSON-style** lines for handshake, disconnects, version kicks, and mouse/key events to a timestamped file under the server’s KrepAPI data folder.
### Fixed
- **Menu state**: **`selectedVersion`** is only reset when missing from the remote **`versionList`** (or initially unset), so reopening the menu keeps the user’s chosen changelog version.
- **Changelog headline**: Avoids **fixed 2× scale** that exceeded the scroll clip and **cut off** the version suffix; headline now scales to the available width (up to 2×).
- **Layout**: Header row width accounts for **panel insets** so the top bar, rule, and body columns stay **aligned** at the designed inner width.
### Changed
- **`fabric-1-21/build.gradle`**: **GradleUp Shadow** plugin, **`shadowModImpl` / `shadowImpl`** configurations, **`remapJar`** input from **`shadowJar`**; **Maven Central** for Kotlin; **ModMenu** and **MoulConfig** coordinates as used at build time.
- **`fabric.mod.json`** (1.21): **`contact`**, **`custom.modmenu`**, **`entrypoints.modmenu`** via **`KrepapiModMenuEntry`**; mixin / client resource layout adjusted (e.g. consolidated **`krepapi.mixins.json`**, removed redundant client mixin descriptor where applicable).
- **`fabric-26-1`**: Aligns **`fabric.mod.json`**, mixin config, and **access widener** with the split client/main setup used for the update UI.
- **`gradle.properties`**: **`mod_version`** set to **1.3.0** for this release line.
- **Paper**: Minor **`config.yml`** / **`KrepapiPaperVersionGate`** / **`KrepapiPaperPlugin`** tweaks for default **minimum mod build** alignment with the published mod.
- **`:protocol` tests**: **`KrepapiBuildVersionTest`**, **`KrepapiVersionPolicyTest`**, **`KrepapiVersionRequirementTest`** updated for current version policy behavior.
- **Repository hygiene**: **`.gitignore`** entries for editor-generated **`*Edit conflict*`** / **`*Name clash*`** duplicate source files.
### Documentation
- Remote **`versions.json`** and **changelog** paths remain documented in-repo under **`.system/mod/`** for the update checker URLs used by the client.
### Added
**Client update checks, download flow, and in-game update UI**
- **`UpdateChecker`** (both **`fabric-1-21`** and **`fabric-26-1`**): On client startup, asynchronously fetches [`.system/mod/versions.json`](https://raw.githubusercontent.com/RafaelK-F/KrepAPI/main/.system/mod/versions.json) from GitHub, resolves the **latest** mod version and the **JAR filename** for the running Minecraft version, compares semver against the installed mod using **`KrepapiBuildVersion`** (`:protocol`), and loads **every** listed version’s changelog markdown from `.system/mod/changelog/{version-with-dashes}.md` into `versionList` + `allChangelogs` for the menu.
- **`UpdateDownloader`**: On explicit user action only, downloads the update JAR over HTTP into **`mods/KrepAPI-update-{latestVersion}.jar`** with a **volatile progress** field; guards against overlapping downloads; surfaces errors without crashing the client.
- **`/krepapi menu`**: Client command to open the update / changelog screen at any time.
- **`KrepapiUpdateToast`**: One-time toast when an update is available; **opens the menu** (no direct download from the toast).
- **`KrepapiUpdateHud`**: Optional **`[KrepAPI ↑]`** indicator when an update is available; **click opens the menu** (no direct download from the HUD).
**Fabric 1.21.x — MoulConfig update menu**
- **MoulConfig** (`modern-1.21.11`) is **bundled via GradleUp Shadow**: dependency is **relocated** to `net.shik.krepapi.deps.moulconfig`, **`kotlin-stdlib`** is included for MoulConfig’s Kotlin internals, and a **hand-authored** `META-INF/services/...IMinecraft` entry points at the relocated platform class (Shadow does not rewrite service files by default).
- **Access widener** (`krepapi.accesswidener`): Merged the subset of MoulConfig widener entries that **validate** on Minecraft **1.21.11** with official Mojang mappings so the shaded UI can render without `IllegalAccessError`.
- **Screen**: **`KrepapiMenuScreen`** builds a MoulConfig **`GuiComponent`** tree (outer **`VANILLA`** panel, light **header strip**, full-width rule, two **`DARK_RECT`** columns). **Left**: “Changelogs” header, magenta underline, dashed separator, **clickable version rows** (cyan + underline when selected). **Right**: gray subtitle, **scaled headline** “Changelog entries for v…”, and formatted markdown body (yellow `#`, magenta `##`, green `###`, gray bullets; first top-level bullet after a `##` section highlighted in white to match the reference layout).
- **Headline scaling**: Dynamic `min(2×, fit-to-width)` so long titles are **not clipped** inside the scroll viewport (fixes truncated `v1.` style glitches).
- **ModMenu**: **`modImplementation`** ModMenu **17.0.0-beta.2**, dedicated **`KrepapiModMenuEntry`** → **`KrepapiModMenuIntegration`** config screen factory; **`fabric.mod.json`** extended with **`custom.modmenu`** (homepage, issues, GitHub link, `update_checker`).
- **Helper UI types** in **`net.shik.krepapi.client.ui.KrepapiMenuComponents`**: `FullWidthRuleComponent`, `VersionNavRow`, `ChangelogHeadlineComponent`, `StatusBadgeComponent`.
**Fabric 26.1.x — Vanilla update menu**
- **`KrepapiMenuScreen26`**: Same update semantics and markdown presentation using **vanilla** `Screen` widgets (no MoulConfig / Shadow on this loader).
**Structured debug logging (optional)**
- **Paper** (`KrepapiPaperPlugin`) and **Fabric server** networking: when **`debug-logging`** / JVM flag / marker file enables it, append **NDJSON-style** lines for handshake, disconnects, version kicks, and mouse/key events to a timestamped file under the server’s KrepAPI data folder.
### Fixed
- **Menu state**: **`selectedVersion`** is only reset when missing from the remote **`versionList`** (or initially unset), so reopening the menu keeps the user’s chosen changelog version.
- **Changelog headline**: Avoids **fixed 2× scale** that exceeded the scroll clip and **cut off** the version suffix; headline now scales to the available width (up to 2×).
- **Layout**: Header row width accounts for **panel insets** so the top bar, rule, and body columns stay **aligned** at the designed inner width.
### Changed
- **`fabric-1-21/build.gradle`**: **GradleUp Shadow** plugin, **`shadowModImpl` / `shadowImpl`** configurations, **`remapJar`** input from **`shadowJar`**; **Maven Central** for Kotlin; **ModMenu** and **MoulConfig** coordinates as used at build time.
- **`fabric.mod.json`** (1.21): **`contact`**, **`custom.modmenu`**, **`entrypoints.modmenu`** via **`KrepapiModMenuEntry`**; mixin / client resource layout adjusted (e.g. consolidated **`krepapi.mixins.json`**, removed redundant client mixin descriptor where applicable).
- **`fabric-26-1`**: Aligns **`fabric.mod.json`**, mixin config, and **access widener** with the split client/main setup used for the update UI.
- **`gradle.properties`**: **`mod_version`** set to **1.3.0** for this release line.
- **Paper**: Minor **`config.yml`** / **`KrepapiPaperVersionGate`** / **`KrepapiPaperPlugin`** tweaks for default **minimum mod build** alignment with the published mod.
- **`:protocol` tests**: **`KrepapiBuildVersionTest`**, **`KrepapiVersionPolicyTest`**, **`KrepapiVersionRequirementTest`** updated for current version policy behavior.
- **Repository hygiene**: **`.gitignore`** entries for editor-generated **`*Edit conflict*`** / **`*Name clash*`** duplicate source files.
### Documentation
- Remote **`versions.json`** and **changelog** paths remain documented in-repo under **`.system/mod/`** for the update checker URLs used by the client.
### Added
**Server-driven mouse capture (Fabric client + Fabric/Paper server reference)**
- New custom payload / plugin message channels:
- `krepapi:s2c_mouse_capture` — enable or configure capture (`enabled`, `flags`, `consumeVanilla`).
- `krepapi:c2s_mouse_action` — client reports button presses/releases, scroll deltas, and optionally normalized cursor position on each forwarded event.
- **Flags** (combine with bitwise OR): mouse buttons (left/right/middle via GLFW button id), scroll wheel, optional **cursor on events** (cursor as 0–1 in scaled window coordinates).
- **Capability** `SERVER_MOUSE_CAPTURE` in `c2s_client_info`. Reference implementations only send `s2c_mouse_capture` if the client advertised this bit, so older KrepAPI clients without mouse support are not sent an unregistered payload.
- **Wire format** is defined in `ProtocolMessages` (same bytes on Fabric custom play payloads and Paper `sendPluginMessage`); `c2s_mouse_action` is self-describing so Paper can decode without remembering prior S2C state.
- **Client:** `MouseCaptureState`, `MouseMixin` on `MouseHandler` (`onButton` with `MouseButtonInfo`, `onScroll`), same eligibility gate as raw keyboard capture (in play, network handler present, player or screen open).
- **Server:** Fabric — `KrepapiFabricServerNetworking.registerMouseActionListener`, `sendMouseCaptureConfig`, `getClientCapabilities`; handshake entry stores the client capability bitmask after a successful `c2s_client_info`. Paper — outgoing/incoming channel registration, `KrepapiPaperPlugin.sendMouseCaptureConfig`, `getClientCapabilities`, session capability map, debug logging for mouse actions.
**Build version requirement expressions (legacy / ranges)**
- [`KrepapiVersionRequirement`](protocol/src/main/java/net/shik/krepapi/protocol/KrepapiVersionRequirement.java) parses `minimum-mod-version` / Fabric `minimumModVersion` / plugin registrations: bare `X.Y.Z` remains `>=` (compatible); also `X.Y.Z>`, `=X.Y.Z`, `<X.Y.Z`, `X.Y.x` / `X.Y.*`, two-part `X.Y` for a minor line; middle wildcards (`1.x.2`) are rejected.
- [`KrepapiVersionPolicy`](protocol/src/main/java/net/shik/krepapi/protocol/KrepapiVersionPolicy.java) evaluates the **intersection** of all requirements; [`KrepapiKickReasons.forVersionCheckFailure`](protocol/src/main/java/net/shik/krepapi/protocol/KrepapiKickReasons.java) formats kicks. `s2c_hello.minModVersion` is a summary (max floor if all are minima, else joined specs).
- Paper: invalid `minimum-mod-version` disables the plugin on enable; invalid combined requirements kick on join with a misconfiguration message. Fabric: logs invalid default settings at register; per-join validation with the same kick text.
**Minecraft 26.1 Fabric build (`:fabric-26-1`)**
- Second Fabric artifact from the same repo: **`KrepAPI-mc26.1`** (mod id still `krepapi`), targeting **26.1–26.1.1**, **Loader 0.18.6**, **Fabric API 0.144.3+26.1**, **`net.fabricmc.fabric-loom` 1.15-SNAPSHOT** (unobfuscated game; no `remapJar`), **Java 25** toolchain.
- Source tree is a fork of the 1.21 module with 26.1-specific wiring: **`PayloadTypeRegistry.clientboundPlay` / `serverboundPlay`**, **`KeyMappingHelper.registerKeyMapping`** (`fabric-key-mapping-api-v1`).
### Fixed
**`overrideVanilla` server bindings vs. player keybinds**
- Vanilla blocking for `overrideVanilla` previously compared each key event only to the **`defaultKey`** from `s2c_bindings`. After rebinding in Minecraft’s controls, the wrong key (or no key) could still be consumed or missed.
- The client now keeps a list of `actionId`s with `overrideVanilla` and, on every key event, resolves the current `KeyMapping` and uses **`KeyMapping.matches(KeyEvent)`** so suppression follows the **live** binding ([`KrepapiKeyPipeline`](fabric-1-21/src/client/java/net/shik/krepapi/client/KrepapiKeyPipeline.java)).
- `ServerBindingManager.applyBindings` registers all server `KeyMapping`s before applying the override list, so lookups are always valid when the list is installed.
### Changed
- **Two-part requirement strings:** A value with exactly two numeric segments (e.g. `1.2`) is now a **minor line** (`1.2.x`), not a shorthand for `>= 1.2.0`. Use `1.2.0` or `1.2.0>` for a minimum floor.
**Gradle layout**
- Fabric mod for **1.21.4–1.21.11** lives in **`fabric-1-21`** → **`KrepAPI-mc1.21`** JAR (remapped Loom as before). Root project is an aggregator; run **`gradlew build`** to produce **both** Fabric JARs plus `protocol` and `paper-plugin`.
- **Gradle Wrapper** updated to **9.4.0** (recommended for Loom 1.15 / 26.1).
**Build targets (this repository)**
- **Fabric `fabric-1-21` (`KrepAPI-mc1.21`):** **Minecraft 1.21.11**, **Fabric Loader 0.18.2**, **Fabric API 0.139.4+1.21.11**, **`net.fabricmc.fabric-loom-remap` 1.14-SNAPSHOT**, **official Mojang mappings** (`loom.officialMojangMappings()`), **Java 21**. `fabric.mod.json` range **`>=1.21.4 <=1.21.11`**.
- **Networking (Fabric 1.21):** play payloads use **`CustomPacketPayload`**, **`CustomPacketPayload.Type`**, and **`StreamCodec`** on **`RegistryFriendlyByteBuf`** (Mojang names; not Yarn `CustomPayload` / `PacketCodec` / `RegistryByteBuf`).
- **Paper reference plugin:** `paper-api` **1.21.11-R0.1-SNAPSHOT**, `plugin.yml` **`api-version: '1.21.11'`**, default **minimum mod build** tracks the mod (see `gradle.properties` `mod_version`).
- Server-driven keys on **1.21.x** use **`KeyBindingHelper.registerKeyBinding`** (`net.fabricmc.fabric.api.client.keybinding.v1`).
### Documentation
- [`docs/protocol.md`](docs/protocol.md) — wire format, channels, build requirement expressions, kick reasons, `minModVersion` summary semantics.
- [`docs/client-api.md`](docs/client-api.md) — mouse capture, intercept keys, handshake fields, Fabric server settings, `fabric.mod.json` depends, version API examples.
- [`docs/paper-plugin.md`](docs/paper-plugin.md) — config examples, `versionGate`, capabilities / `sendMouseCaptureConfig`, Gradle note.
### Added
**Server-driven mouse capture (Fabric client + Fabric/Paper server reference)**
- New custom payload / plugin message channels:
- `krepapi:s2c_mouse_capture` — enable or configure capture (`enabled`, `flags`, `consumeVanilla`).
- `krepapi:c2s_mouse_action` — client reports button presses/releases, scroll deltas, and optionally normalized cursor position on each forwarded event.
- **Flags** (combine with bitwise OR): mouse buttons (left/right/middle via GLFW button id), scroll wheel, optional **cursor on events** (cursor as 0–1 in scaled window coordinates).
- **Capability** `SERVER_MOUSE_CAPTURE` in `c2s_client_info`. Reference implementations only send `s2c_mouse_capture` if the client advertised this bit, so older KrepAPI clients without mouse support are not sent an unregistered payload.
- **Wire format** is defined in `ProtocolMessages` (same bytes on Fabric custom play payloads and Paper `sendPluginMessage`); `c2s_mouse_action` is self-describing so Paper can decode without remembering prior S2C state.
- **Client:** `MouseCaptureState`, `MouseMixin` on `MouseHandler` (`onButton` with `MouseButtonInfo`, `onScroll`), same eligibility gate as raw keyboard capture (in play, network handler present, player or screen open).
- **Server:** Fabric — `KrepapiFabricServerNetworking.registerMouseActionListener`, `sendMouseCaptureConfig`, `getClientCapabilities`; handshake entry stores the client capability bitmask after a successful `c2s_client_info`. Paper — outgoing/incoming channel registration, `KrepapiPaperPlugin.sendMouseCaptureConfig`, `getClientCapabilities`, session capability map, debug logging for mouse actions.
**Build version requirement expressions (legacy / ranges)**
- [`KrepapiVersionRequirement`](protocol/src/main/java/net/shik/krepapi/protocol/KrepapiVersionRequirement.java) parses `minimum-mod-version` / Fabric `minimumModVersion` / plugin registrations: bare `X.Y.Z` remains `>=` (compatible); also `X.Y.Z>`, `=X.Y.Z`, `<X.Y.Z`, `X.Y.x` / `X.Y.*`, two-part `X.Y` for a minor line; middle wildcards (`1.x.2`) are rejected.
- [`KrepapiVersionPolicy`](protocol/src/main/java/net/shik/krepapi/protocol/KrepapiVersionPolicy.java) evaluates the **intersection** of all requirements; [`KrepapiKickReasons.forVersionCheckFailure`](protocol/src/main/java/net/shik/krepapi/protocol/KrepapiKickReasons.java) formats kicks. `s2c_hello.minModVersion` is a summary (max floor if all are minima, else joined specs).
- Paper: invalid `minimum-mod-version` disables the plugin on enable; invalid combined requirements kick on join with a misconfiguration message. Fabric: logs invalid default settings at register; per-join validation with the same kick text.
**Minecraft 26.1 Fabric build (`:fabric-26-1`)**
- Second Fabric artifact from the same repo: **`KrepAPI-mc26.1`** (mod id still `krepapi`), targeting **26.1–26.1.1**, **Loader 0.18.6**, **Fabric API 0.144.3+26.1**, **`net.fabricmc.fabric-loom` 1.15-SNAPSHOT** (unobfuscated game; no `remapJar`), **Java 25** toolchain.
- Source tree is a fork of the 1.21 module with 26.1-specific wiring: **`PayloadTypeRegistry.clientboundPlay` / `serverboundPlay`**, **`KeyMappingHelper.registerKeyMapping`** (`fabric-key-mapping-api-v1`).
### Fixed
**`overrideVanilla` server bindings vs. player keybinds**
- Vanilla blocking for `overrideVanilla` previously compared each key event only to the **`defaultKey`** from `s2c_bindings`. After rebinding in Minecraft’s controls, the wrong key (or no key) could still be consumed or missed.
- The client now keeps a list of `actionId`s with `overrideVanilla` and, on every key event, resolves the current `KeyMapping` and uses **`KeyMapping.matches(KeyEvent)`** so suppression follows the **live** binding ([`KrepapiKeyPipeline`](fabric-1-21/src/client/java/net/shik/krepapi/client/KrepapiKeyPipeline.java)).
- `ServerBindingManager.applyBindings` registers all server `KeyMapping`s before applying the override list, so lookups are always valid when the list is installed.
### Changed
- **Two-part requirement strings:** A value with exactly two numeric segments (e.g. `1.2`) is now a **minor line** (`1.2.x`), not a shorthand for `>= 1.2.0`. Use `1.2.0` or `1.2.0>` for a minimum floor.
**Gradle layout**
- Fabric mod for **1.21.4–1.21.11** lives in **`fabric-1-21`** → **`KrepAPI-mc1.21`** JAR (remapped Loom as before). Root project is an aggregator; run **`gradlew build`** to produce **both** Fabric JARs plus `protocol` and `paper-plugin`.
- **Gradle Wrapper** updated to **9.4.0** (recommended for Loom 1.15 / 26.1).
**Build targets (this repository)**
- **Fabric `fabric-1-21` (`KrepAPI-mc1.21`):** **Minecraft 1.21.11**, **Fabric Loader 0.18.2**, **Fabric API 0.139.4+1.21.11**, **`net.fabricmc.fabric-loom-remap` 1.14-SNAPSHOT**, **official Mojang mappings** (`loom.officialMojangMappings()`), **Java 21**. `fabric.mod.json` range **`>=1.21.4 <=1.21.11`**.
- **Networking (Fabric 1.21):** play payloads use **`CustomPacketPayload`**, **`CustomPacketPayload.Type`**, and **`StreamCodec`** on **`RegistryFriendlyByteBuf`** (Mojang names; not Yarn `CustomPayload` / `PacketCodec` / `RegistryByteBuf`).
- **Paper reference plugin:** `paper-api` **1.21.11-R0.1-SNAPSHOT**, `plugin.yml` **`api-version: '1.21.11'`**, default **minimum mod build** tracks the mod (see `gradle.properties` `mod_version`).
- Server-driven keys on **1.21.x** use **`KeyBindingHelper.registerKeyBinding`** (`net.fabricmc.fabric.api.client.keybinding.v1`).
### Documentation
- [`docs/protocol.md`](docs/protocol.md) — wire format, channels, build requirement expressions, kick reasons, `minModVersion` summary semantics.
- [`docs/client-api.md`](docs/client-api.md) — mouse capture, intercept keys, handshake fields, Fabric server settings, `fabric.mod.json` depends, version API examples.
- [`docs/paper-plugin.md`](docs/paper-plugin.md) — config examples, `versionGate`, capabilities / `sendMouseCaptureConfig`, Gradle note.
## What's New
### Raw Keyboard Capture (`SERVER_RAW_CAPTURE` — capability bit `1 << 2`)
Servers can now instruct the client to forward raw GLFW keyboard events (key code, scancode, action, modifier mask, monotone sequence number) back to the server via the new `c2s_raw_key` packet.
The server sends `s2c_raw_capture` with a `RawCaptureConfig` that specifies:
- **mode** — `OFF` (0), `ALL` (1), or `WHITELIST` (2, only keys in the key list are forwarded)
- **consumeVanilla** — whether the client should suppress Minecraft's own handling of captured keys
- **whitelistKeys** — list of GLFW key codes (max 256) used when mode is `WHITELIST`
New Fabric server-side API in `KrepapiFabricServerNetworking`:
- `sendRawCaptureConfig(ServerPlayerEntity, RawCaptureConfig)` — push a capture config to a specific player
- `registerRawKeyListener(RawKeyListener)` / `unregisterRawKeyListener(RawKeyListener)` — subscribe to incoming `c2s_raw_key` events with per-listener isolation (exceptions are caught and logged per listener)
Client-side state is managed by the new `RawCaptureState` class. Capture is cleared automatically on disconnect.
### Hardcoded-Key Intercept (`INTERCEPT_KEYS` — capability bit `1 << 3`)
Servers can now suppress specific vanilla key behaviors that are not exposed through `KeyMapping` — keys like Escape, F3, Tab, F1, and F5 that Minecraft handles internally.
The server sends `s2c_intercept_keys` with a list of `InterceptEntry` records, each containing a **slot ID** and a **blockVanilla** flag:
| Slot constant | Key | Vanilla behaviour blocked |
|---|---|---|
| `INTERCEPT_SLOT_ESCAPE` (0) | Escape | Opens game/pause menu |
| `INTERCEPT_SLOT_F3` (1) | F3 | Toggles debug HUD |
| `INTERCEPT_SLOT_TAB` (2) | Tab | Shows player list |
| `INTERCEPT_SLOT_F1` (3) | F1 | Toggles HUD visibility |
| `INTERCEPT_SLOT_F5` (4) | F5 | Cycles perspective / chunk reload |
An empty entry list clears all intercepts. Maximum 32 entries per sync.
Two new mixins implement the Escape suppression specifically: `GameMenuScreenMixin` cancels the `keyPressed` handler in `GameMenuScreen`, and `MinecraftClientMixin` cancels `openGameMenu` — both guard behind `InterceptKeyState.blockVanillaForSlot`. F3/Tab/F1/F5 are handled inside the existing `KeyboardMixin` path via `InterceptKeyState.shouldConsumeVanillaKey`.
Client-side state is managed by the new `InterceptKeyState` class. Intercepts are cleared automatically on disconnect.
### Key Release and Repeat Events for Bound Keys
`KrepapiKeyPipeline` previously only dispatched events for `GLFW_PRESS` (action == 1). It now also dispatches for `GLFW_REPEAT` and `GLFW_RELEASE` on bound keys.
A new `OVERRIDE_HELD_KEYS` set tracks which keys are currently held with an active binding so that repeat and release events are only forwarded if the press was already consumed — preventing orphaned release events from leaking through. The held-key set is cleared on disconnect and when server overrides are reset.
This is a **wire-transparent** change (no new packets), but it is a **behaviour change** for existing listeners: handlers that only expected press events will now also receive repeat and release.
### New Protocol Messages and Packet Types
Three new packet channels are registered in `KrepapiNetworking` and `PayloadTypeRegistry`:
| Channel | Direction | Payload class |
|---|---|---|
| `s2c_raw_capture` | S2C | `KrepapiRawCaptureS2CPayload` |
| `s2c_intercept_keys` | S2C | `KrepapiInterceptKeysS2CPayload` |
| `c2s_raw_key` | C2S | `KrepapiRawKeyC2SPayload` |
The `:protocol` module gains four new record types (`RawCaptureConfig`, `RawKeyEvent`, `InterceptEntry`, `InterceptKeysSync`) and the corresponding `encode*` / `decode*` pairs in `ProtocolMessages`, with explicit size caps and input validation on decode.
### Unit Tests for New Protocol Messages
`ProtocolMessagesTest` gains four new tests covering the new encode/decode pairs:
- `rawCaptureConfigRoundTrip` — full roundtrip including whitelist keys
- `rawKeyEventRoundTrip` — all five fields preserved
- `interceptKeysSyncRoundTrip` — multi-entry list and `blockVanilla` flag
- `encodeRawCaptureRejectsTooManyKeys` — verifies the 256-key hard cap is enforced at encode time
## Notes
- KrepAPI is client-side only and compatible with vanilla servers. A 1.0.x client simply won't advertise the new capability bits — the new features are silently skipped, nothing breaks.
- Developers building on KrepAPI: check the client-advertised `protocolVersion` in the handshake before sending `s2c_raw_capture` or `s2c_intercept_keys` to ensure the client supports them.
- Install a **Fabric API** build that matches the Minecraft version.
### KrepAPI (for developers)
- **Build version** (Fabric mod release / `c2s_client_info.modVersion`, `gradle.properties` → `mod_version`): **1.1.0**
- **Wire protocol version** (`KrepapiProtocolVersion.CURRENT`, `protocolVersion` in `s2c_hello` / `c2s_client_info`): **2** — bumped because two new S2C packet channels and one new C2S packet channel were added. A 1.0.x client simply won't advertise the new capability bits (`SERVER_RAW_CAPTURE`, `INTERCEPT_KEYS`) and will not receive the new packets — nothing breaks. For developers building on top of KrepAPI: check the client-advertised `protocolVersion` before sending 1.1.0-only packets.
## KrepAPI 1.0.1-Mod
## Issues that have been fixed
### ByteBuffer with Fixed-Size Allocation — No Bounds Check
In `ProtocolMessages`, `ByteBuffer.allocate(65536)` is used for bindings and `512` for `Hello/ClientInfo`. If a payload grows larger (e.g. many bindings with long strings), there's a silent `BufferOverflowException` crash. No dynamic growth, no try-catch.
### c2s_key_action Handler is Empty
In the Fabric server code, the receiver for `KrepapiKeyActionC2SPayload` is completely empty — `registerGlobalReceiver(..., (payload, context) -> {})`. Key presses on the Fabric server are therefore silently ignored. This isn't a bug if you're using Paper, but it's entirely undocumented for Fabric server users.
### No Input Validation in `ProtocolBuf.readUtf()`
Without a length limit on UTF string reads, a malicious client can send arbitrarily long strings in `actionId` or `modVersion`. This leads to uncontrolled heap growth during decoding. Standard practice in Minecraft protocol code: a fixed max-byte limit per string.
### Dispatch Doesn't Cancel on First Match
In `KrepapiKeyPipeline.dispatch()`, `consume = true` is set when a listener returns `true`, but all subsequent listeners still run. This is a deliberate non-early-exit, but it means: a low-priority listener cannot set `consume` back to `false` afterwards — the variable is never reset. As a result, all downstream listeners always run to completion, even if the event was already consumed long before.
### volatile Fields Instead of AtomicReference / Final Config
`requireClientOnDedicatedServer`, `minimumModVersion`, and `handshakeTimeoutTicks` are all `volatile`. For simple primitives this is correct, but for Strings (like `minimumModVersion`), `volatile` only protects the reference, not the contents — though this is sufficient here since Strings are immutable. Still stylistically questionable compared to a clean config class.
### Lexicographic Version Comparison
With `minimum-mod-version: "1.9"` and then `"1.10"` — lexicographically `"1.9" > "1.10"` (since `'9' > '1'`). The project documents this as a known limitation, but it's a real footgun for server admins. No real SemVer parser.
### HANDSHAKE_TICKS Map Doesn't Clean Up Reliably
In the tick event, `HANDSHAKE_TICKS.remove(id)` is only called on `e.answered` or timeout. If a player disconnects before the timeout fires, the entry is removed in the DISCONNECT event — but only from `HANDSHAKE`, not explicitly from `HANDSHAKE_TICKS` (which has its own separate remove). Fine in isolation, but with many rapid joins from unmodded clients the map could grow.
### No Unit Tests
None. The `:protocol` module would be ideal for encode/decode roundtrip tests — it's independent of Minecraft, pure Java. Tests would be especially important for the ByteBuffer handling and the version policy logic.
### Release Phase Not Wired Up
`KeyAction.PHASE_RELEASE` is defined in the code and transmitted in the protocol, but the client-side `KeyMapping` never fires a release event. Documented, but still an incomplete feature.
## Notes
- Install a **Fabric API** build that matches the Minecraft version.
### KrepAPI (for developers)
- **Build version** (Fabric mod release / `c2s_client_info.modVersion`, `gradle.properties` → `mod_version`): **1.0.1**
- **Wire protocol version** (`KrepapiProtocolVersion.CURRENT`, `protocolVersion` in `s2c_hello` / `c2s_client_info`): **1** — increment only when the binary packet layout or semantics change