Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 34 additions & 18 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@

## Project Overview

Fabric mod for Minecraft 1.21.11 (dedicated server only) that exposes a REST API + WebSocket for monitoring and controlling Minecraft servers. Uses `com.sun.net.httpserver` from the JDK (zero external dependencies for HTTP).
Fabric mod for Minecraft 26.1.2 (dedicated server only) that exposes a REST API + WebSocket for monitoring and controlling Minecraft servers. Uses `com.sun.net.httpserver` from the JDK (zero external dependencies for HTTP).

## Tech Stack

| Component | Version |
|-----------------|----------------------------|
| Minecraft | 1.21.11 |
| Fabric Loader | 0.18.4 |
| Fabric Loom | 1.15-SNAPSHOT |
| Fabric API | 0.141.3+1.21.11 |
| Java | 21 |
| Minecraft | 26.1.2 |
| Fabric Loader | 0.19.3 |
| Fabric Loom | 1.17.11 |
| Fabric API | 0.151.0+26.1.2 |
| Java | 25 |
| Gradle | 9.5.1 |
| Mappings | Mojang official |

## Project Structure
Expand All @@ -37,35 +38,39 @@ src/main/java/net/natxo/mcrestapi/
└── PlayerTracker.java # Thread-safe player data snapshots
```

## Inspecting Minecraft API (Mojang Mappings)
## Inspecting Minecraft API (deobfuscated)

Since the project uses Mojang official mappings, you can inspect any Minecraft class using:
Minecraft 26.1+ ships **deobfuscated** — official class/method names with parameter names, no mappings. Inspect any Minecraft class directly from the Loom-provided merged jar:

```bash
# Full class inspection
javap -p -classpath .gradle/loom-cache/minecraftMaven/net/minecraft/minecraft-merged-6dd721cd7d/1.21.11-loom.mappings.1_21_11.layered+hash.2198-v2/minecraft-merged-6dd721cd7d-1.21.11-loom.mappings.1_21_11.layered+hash.2198-v2.jar <fully.qualified.ClassName>
# Full class inspection (replace 26.1.2 with the target MC version)
javap -p -classpath ~/.gradle/caches/fabric-loom/26.1.2/minecraft-merged.jar <fully.qualified.ClassName>

# Search for specific methods
javap -p -classpath .gradle/loom-cache/minecraftMaven/net/minecraft/minecraft-merged-6dd721cd7d/1.21.11-loom.mappings.1_21_11.layered+hash.2198-v2/minecraft-merged-6dd721cd7d-1.21.11-loom.mappings.1_21_11.layered+hash.2198-v2.jar <ClassName> 2>&1 | grep -i "methodName"
# Search for a specific method
javap -p -classpath ~/.gradle/caches/fabric-loom/26.1.2/minecraft-merged.jar <ClassName> 2>&1 | grep -i "methodName"

# List classes in the jar
jar tf .gradle/loom-cache/minecraftMaven/net/minecraft/minecraft-merged-6dd721cd7d/1.21.11-loom.mappings.1_21_11.layered+hash.2198-v2/minecraft-merged-6dd721cd7d-1.21.11-loom.mappings.1_21_11.layered+hash.2198-v2.jar | grep -i "ClassName"
jar tf ~/.gradle/caches/fabric-loom/26.1.2/minecraft-merged.jar | grep -i "ClassName"

# Authlib (GameProfile, etc.) — uses record-style accessors (name() not getName())
javap -p -classpath ~/.gradle/caches/modules-2/files-2.1/com.mojang/authlib/7.0.61/efee1e6b54e863108576eb3b3ae71144626aaefc/authlib-7.0.61.jar <ClassName>
# Authlib (GameProfile, etc.) — record-style accessors (name() not getName())
# locate it with: find ~/.gradle -name 'authlib-*.jar'
```

### Key API differences in 1.21.11 Mojang Mappings
### Key API notes (Minecraft 26.1, official names)

- `GameProfile.name()` not `getName()` (authlib 7.x uses record-style)
- `GameProfile.name()` not `getName()` (authlib uses record-style)
- `ResourceKey.identifier()` not `location()`
- `ServerPlayer.server` is private — use `player.level().getServer()` instead
- `PlayerList.isOp()` takes `NameAndId`, not `GameProfile` — use `new NameAndId(gameProfile)`
- `DedicatedServer` has `getProperties()` for server.properties access
- `Settings.MutableValue` fields use `.get()` to read values
- `Difficulty.getSerializedName()` not `getKey()` (implements `StringRepresentable`)
- Weather/time moved off `LevelData`: use `Level.isRaining()` / `isThundering()`; the day time is `Level.getOverworldClockTime()` (26.1 WorldClock), and `getDayCount()` was removed (derive `clockTime / 24000`)

## Build & Run

Minecraft 26.1 requires **Java 25** — the Gradle JVM itself must run on JDK 25 (set `JAVA_HOME` to a JDK 25 if it isn't your default).

```bash
./gradlew build # Compile and produce mod jar
./gradlew runServer # Launch dedicated server with mod loaded (first run: accept EULA in run/eula.txt)
Expand All @@ -85,7 +90,18 @@ API testing collection in `McRestApi-Bruno/`. Open with Bruno, select the "Local
- **Minotaur plugin** configured in `build.gradle` for automated publishing
- **Modrinth token** stored in `.env` locally and in GitHub secret `MODRINTH_TOKEN`
- **CI/CD:** `.github/workflows/publish.yml` triggers on tag push (`v*`)
- **Two READMEs:** `README.md` (GitHub, full) and `MODRINTH_README.md` (Modrinth, compact)
- **Two READMEs:** `README.md` (GitHub, full, **per-branch**) and `MODRINTH_README.md` (the **single shared** Modrinth description — keep it version-agnostic)

### Minecraft version support (branches)

The mod supports multiple Minecraft versions in parallel — **one branch per MC line**:

- `main` always tracks the **latest** Minecraft version (currently `26.1.2`). Older lines live on a branch named by MC version (e.g. `1.21.11`) and keep getting back-support.
- New MC version → branch off `main`, port it, merge into `main` via PR; the previous version stays on its own branch.
- Per branch, set the versions in `gradle.properties`, `fabric.mod.json`, and `gameVersions` in `build.gradle`.
- Modrinth versions stay unique across lines via `versionNumber = "${mod_version}+${minecraft_version}"` (e.g. `2.0.0+26.1.2`). Keep `mod_version` ranges disjoint per line (26.1 → `2.x`, 1.21.11 → `1.x`) so git tags never collide.
- `MODRINTH_README.md` is the shared Modrinth body across **every** version — never pin it to a single MC version. The GitHub `README.md` is per-branch and may be version-specific.
- Minecraft 26.1+ needs **Java 25**; the CI workflows already run on Java 25.

### How to publish a new version

Expand Down
18 changes: 10 additions & 8 deletions MODRINTH_README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# MCRestAPI

A Fabric mod for Minecraft 1.21.11 that exposes a REST API and real-time event stream (SSE) for monitoring and controlling dedicated Minecraft servers. Built on top of the JDK's built-in HTTP server with zero external dependencies.
A Fabric mod that exposes a REST API and real-time event stream (SSE) for monitoring and controlling dedicated Minecraft servers. Built on top of the JDK's built-in HTTP server with zero external dependencies. Available for **Minecraft 1.21.11 and 26.1+** — download the file that matches your version.

---

Expand All @@ -18,19 +18,21 @@ A Fabric mod for Minecraft 1.21.11 that exposes a REST API and real-time event s
- CORS configuration with per-origin allowlist
- API keys hashed with PBKDF2-SHA256 (never stored in plain text)
- Zero external dependencies (uses JDK built-in HTTP server)
- Virtual threads (Java 21) for lightweight concurrency
- Virtual threads for lightweight concurrency
- Server-side only (does not run on clients)

---

## Requirements

| Component | Version |
|----------------|-----------------|
| Minecraft | 1.21.11 |
| Fabric Loader | >= 0.18.4 |
| Fabric API | any |
| Java | >= 21 |
| Component | 1.21.11 line | 26.1 line |
|---------------|--------------|------------|
| Minecraft | 1.21.11 | 26.1.x |
| Java | >= 21 | >= 25 |
| Fabric Loader | >= 0.18.4 | >= 0.19.3 |
| Fabric API | any | any |

Each release on Modrinth is tagged with its supported Minecraft version — install the one matching your server.

---

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# MCRestAPI

A Fabric mod for Minecraft 1.21.11 that exposes a REST API and real-time event stream (SSE) for monitoring and controlling dedicated Minecraft servers. Built on top of the JDK's built-in HTTP server with zero external dependencies.
A Fabric mod for Minecraft 26.1.2 that exposes a REST API and real-time event stream (SSE) for monitoring and controlling dedicated Minecraft servers. Built on top of the JDK's built-in HTTP server with zero external dependencies.

[![Modrinth Downloads](https://img.shields.io/modrinth/dt/XZCgCz7D?label=Modrinth%20Downloads)](https://modrinth.com/mod/mcrestapi)
[![Modrinth Version](https://img.shields.io/modrinth/v/XZCgCz7D?label=Latest%20Version)](https://modrinth.com/mod/mcrestapi/versions)
Expand Down Expand Up @@ -52,7 +52,7 @@ A Fabric mod for Minecraft 1.21.11 that exposes a REST API and real-time event s
- CORS configuration with per-origin allowlist
- API keys hashed with PBKDF2-SHA256 (never stored in plain text)
- Zero external dependencies (uses JDK built-in HTTP server)
- Virtual threads (Java 21) for lightweight concurrency
- Virtual threads (Java 25) for lightweight concurrency
- Server-side only (does not run on clients)

---
Expand All @@ -61,10 +61,10 @@ A Fabric mod for Minecraft 1.21.11 that exposes a REST API and real-time event s

| Component | Version |
|----------------|-----------------|
| Minecraft | 1.21.11 |
| Minecraft | 26.1.2 |
| Fabric Loader | >= 0.18.4 |
| Fabric API | any |
| Java | >= 21 |
| Java | >= 25 |

---

Expand Down Expand Up @@ -231,7 +231,7 @@ curl -H "Authorization: Bearer mcsapi_xxx" http://localhost:8080/api/server

```json
{
"version": "1.21.11",
"version": "26.1.2",
"motd": "A Minecraft Server",
"server_port": 25565,
"online_mode": true,
Expand Down
21 changes: 11 additions & 10 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
id 'net.fabricmc.fabric-loom-remap' version "${loom_version}"
id 'net.fabricmc.fabric-loom' version "${loom_version}"
id 'maven-publish'
id 'com.modrinth.minotaur' version '2.+'
}
Expand All @@ -22,11 +22,11 @@ repositories {
dependencies {
// To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings loom.officialMojangMappings()
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"

implementation "net.fabricmc:fabric-loader:${project.loader_version}"

// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
implementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"

}

Expand All @@ -39,7 +39,7 @@ processResources {
}

tasks.withType(JavaCompile).configureEach {
it.options.release = 21
it.options.release = 25
}

java {
Expand All @@ -48,8 +48,8 @@ java {
// If you remove this line, sources will not be generated.
withSourcesJar()

sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
sourceCompatibility = JavaVersion.VERSION_25
targetCompatibility = JavaVersion.VERSION_25
}

jar {
Expand Down Expand Up @@ -79,10 +79,11 @@ file('.env').with { envFile ->
modrinth {
token = System.getenv("MODRINTH_TOKEN") ?: System.getProperty("MODRINTH_TOKEN")
projectId = "XZCgCz7D"
versionNumber = project.mod_version
// MC version is appended so 1.21.11 and 26.1 releases stay unique on Modrinth (e.g. 2.0.0+26.1.2)
versionNumber = "${project.mod_version}+${project.minecraft_version}"
versionType = "release"
uploadFile = remapJar
gameVersions = ["1.21.11"]
uploadFile = jar
gameVersions = ["26.1.2"]
loaders = ["fabric"]
changelog = System.getenv("CHANGELOG") ?: ""
syncBodyFrom = rootProject.file("MODRINTH_README.md").text
Expand Down
10 changes: 5 additions & 5 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ org.gradle.configuration-cache=false

# Fabric Properties
# check these on https://fabricmc.net/develop
minecraft_version=1.21.11
loader_version=0.18.4
loom_version=1.15-SNAPSHOT
minecraft_version=26.1.2
loader_version=0.19.3
loom_version=1.17.11

# Mod Properties
mod_version=1.2.0
mod_version=2.0.0
maven_group=net.natxo.mcrestapi
archives_base_name=mcrestapi

# Dependencies
fabric_api_version=0.141.3+1.21.11
fabric_api_version=0.151.0+26.1.2
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ private Map<String, Object> buildProperties() {

Map<String, Object> properties = new LinkedHashMap<>();
properties.put("gamemode", props.gameMode.get().getName());
properties.put("difficulty", props.difficulty.get().getKey());
properties.put("difficulty", props.difficulty.get().getSerializedName());
properties.put("hardcore", props.hardcore);
properties.put("allow_flight", props.allowFlight.get());
properties.put("whitelist", props.whiteList.get());
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/net/natxo/mcrestapi/endpoints/WorldEndpoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,12 @@ private Map<String, Object> buildGlobal(ServerLevel overworld) {

Map<String, Object> time = new LinkedHashMap<>();
time.put("game_time", levelData.getGameTime());
time.put("day_time", levelData.getDayTime());
time.put("day_count", overworld.getDayCount());
time.put("day_time", overworld.getOverworldClockTime());
time.put("day_count", overworld.getOverworldClockTime() / 24000L);

Map<String, Object> weather = new LinkedHashMap<>();
weather.put("raining", levelData.isRaining());
weather.put("thundering", levelData.isThundering());
weather.put("raining", overworld.isRaining());
weather.put("thundering", overworld.isThundering());

Map<String, Object> spawn = new LinkedHashMap<>();
spawn.put("x", spawnPos.getX());
Expand All @@ -114,7 +114,7 @@ private Map<String, Object> buildGlobal(ServerLevel overworld) {
global.put("seed", overworld.getSeed());
global.put("time", time);
global.put("weather", weather);
global.put("difficulty", levelData.getDifficulty().getKey());
global.put("difficulty", levelData.getDifficulty().getSerializedName());
global.put("difficulty_locked", levelData.isDifficultyLocked());
global.put("hardcore", levelData.isHardcore());
global.put("pvp", overworld.isPvpAllowed());
Expand Down
6 changes: 3 additions & 3 deletions src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
]
},
"depends": {
"fabricloader": ">=0.18.4",
"minecraft": "~1.21.11",
"java": ">=21",
"fabricloader": ">=0.19.3",
"minecraft": "~26.1.2",
"java": ">=25",
"fabric-api": "*"
}
}
Loading