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
20 changes: 16 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ jobs:
java: [
21, # Current Java LTS & minimum supported by Minecraft
]
# Stonecutter-managed Minecraft versions. Add new versions here.
version: [ '1.21.11' ]
task: [ build, runServertest, runClienttest ]
runs-on: ubuntu-latest
steps:
Expand All @@ -69,12 +71,22 @@ jobs:
- name: make gradle wrapper executable
run: chmod +x ./gradlew
- name: build
if: ${{ matrix.task != 'runClienttest' }}
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1.0.1
with:
run: ./gradlew ${{ matrix.task }} --stacktrace
run: ./gradlew :${{ matrix.version }}:${{ matrix.task }} --stacktrace
- name: build (runClienttest)
if: ${{ matrix.task == 'runClienttest' }}
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1.0.1
env:
# Prevent OpenAL / SDL audio init crashes in headless CI clients.
ALSOFT_DRIVERS: "null"
SDL_AUDIODRIVER: "dummy"
with:
run: ./gradlew :${{ matrix.version }}:${{ matrix.task }} --stacktrace
- name: capture build artifacts
if: ${{ matrix.task == 'build' && matrix.java == '17' }} # Only upload artifacts built from latest java on one OS
if: ${{ matrix.task == 'build' }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: Artifacts
path: build/libs/
name: Artifacts-${{ matrix.version }}
path: versions/${{ matrix.version }}/build/libs/
34 changes: 29 additions & 5 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ name: Publish on GitHub, CurseForge & Modrinth
on:
push:
tags:
- "v*"
- "v*+*"

permissions:
contents: read

concurrency:
group: publish-${{ github.ref }}
cancel-in-progress: false

jobs:
check:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -38,8 +42,28 @@ jobs:
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
with:
cache-disabled: true
- name: Validate tag format and version mapping
env:
GITHUB_REF_NAME: ${{ github.ref_name }}
MOD_VERSION: ${{ steps.tag.outputs._0 }}
MC_VERSION: ${{ steps.tag.outputs._1 }}
run: |
set -euo pipefail
if ! [[ "${GITHUB_REF_NAME}" =~ ^v[0-9A-Za-z._-]+\+[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then
echo "::error::Tag '${GITHUB_REF_NAME}' is not in the expected 'v<modver>+<mcver>' format (e.g. v0.0.1+1.21.11). The mod version must consist of [0-9A-Za-z._-]."
exit 1
fi
if [ -z "${MOD_VERSION}" ] || [ -z "${MC_VERSION}" ]; then
echo "::error::Failed to split tag into mod version ('${MOD_VERSION}') and Minecraft version ('${MC_VERSION}')."
exit 1
fi
if [ ! -f "versions/${MC_VERSION}/gradle.properties" ]; then
echo "::error::versions/${MC_VERSION}/gradle.properties not found. Make sure the Minecraft version is registered in settings.gradle.kts."
exit 1
fi
echo "Tag OK: mod=${MOD_VERSION}, mc=${MC_VERSION}"
- name: Build with Gradle
run: ./gradlew build
run: ./gradlew :${{ steps.tag.outputs._1 }}:build
env:
MOD_VERSION: ${{ steps.tag.outputs._0 }}

Expand Down Expand Up @@ -71,12 +95,12 @@ jobs:
modrinth-id: ${{ secrets.MODRINTH_ID }}
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
name: ${{ steps.rawtag.outputs.tag }}
version: ${{ steps.tag.outputs._0 }}
version: ${{ steps.tag.outputs._0 }}+${{ steps.tag.outputs._1 }}
version-type: release
changelog: ${{ steps.tag_message.outputs.message }}
java: 21
loaders: fabric
game-versions: ${{ steps.tag.outputs._1 }}
files: |
build/libs/fabpose-${{ steps.tag.outputs._0 }}+${{ steps.tag.outputs._1 }}.jar
build/libs/fabpose-${{ steps.tag.outputs._0 }}+${{ steps.tag.outputs._1 }}-sources.jar
versions/${{ steps.tag.outputs._1 }}/build/libs/fabpose-${{ steps.tag.outputs._0 }}+${{ steps.tag.outputs._1 }}.jar
versions/${{ steps.tag.outputs._1 }}/build/libs/fabpose-${{ steps.tag.outputs._0 }}+${{ steps.tag.outputs._1 }}-sources.jar
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ bin/
# fabric

run/

# Stonecutter migration baseline (local only)

temp/
33 changes: 27 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,44 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview
FabPose is a Minecraft mod for Fabric servers that allows players to take various poses (/sit, /lay, /spin, /swim). It's a fork of FabSit aiming for feature parity with the Spigot GSit mod.

The `main` branch manages modern Minecraft versions (1.21.11+) via [Stonecutter](https://stonecutter.kikugie.dev/). Legacy versions (1.21.10 and earlier) remain on their dedicated branches and are not Stonecutter-managed.

## Key Development Commands

### Build and Run
Stonecutter exposes a subproject per Minecraft version under `versions/<mc>`. Prefix Gradle tasks with `:<mc>:` to target a specific version, or use `chiseledBuild` to build every active version at once.

```bash
./gradlew build # Build the project
./gradlew runServer # Start development server
./gradlew runClient # Start development client
./gradlew runServertest # Run server-side tests
./gradlew runClienttest # Run client-side tests
./gradlew chiseledBuild # Build every active version
./gradlew :1.21.11:build # Build a specific version
./gradlew :1.21.11:runServer # Start development server
./gradlew :1.21.11:runClient # Start development client
./gradlew :1.21.11:runServertest # Run server-side tests
./gradlew :1.21.11:runClienttest # Run client-side tests
```

Build outputs land in `versions/<mc>/build/libs/`.

### Adding a new Minecraft version
1. Add the version string to `versions(...)` in `settings.gradle.kts`.
2. Create `versions/<new-mc>/gradle.properties` mirroring an existing one with `minecraft_version` / `loader_version` / `fabric_version` / `flk_version` adjusted.
3. Add the version to `matrix.version` in `.github/workflows/build.yml`.
4. (Optional) Update the active version pointer in `stonecutter.gradle.kts` (`stonecutter active "<mc>"`) if you want IDE imports and bare `./gradlew build` to target the new version by default.
5. Run `./gradlew :<new-mc>:build` to validate locally.

### Code Quality
```bash
./gradlew lintKotlin # Run Kotlin linter (Kotlinter)
./gradlew formatKotlin # Auto-format Kotlin code
```

### Release Tags
Releases are triggered by pushing tags matching `v<modver>+<mcver>` (e.g. `v1.4.2+1.21.11`).
The `publish.yml` workflow validates the tag with the regex
`^v[0-9A-Za-z._-]+\+[0-9]+\.[0-9]+(\.[0-9]+)?$` and uploads
`versions/<mcver>/build/libs/fabpose-<modver>+<mcver>.jar` (and `-sources.jar`).
The `<mcver>` segment must match an existing `versions/<mcver>/gradle.properties`.

## Architecture Overview

### Language Structure
Expand All @@ -33,7 +54,7 @@ FabPose is a Minecraft mod for Fabric servers that allows players to take variou
1. **Pose Management**: Uses invisible armor stand entities to handle player poses
2. **Command System**: Brigadier-based commands with permission checks
3. **Networking**: Custom packets for client-server synchronization
4. **Configuration**: JSON5 config at `config/fabsit.json`, reloadable via `/fabpose reload`
4. **Configuration**: JSON config at `config/fabsit.json` (Gson-based), reloadable via `/fabpose reload`

### Key Components
- `PoseManager`: Central pose state management
Expand Down
37 changes: 31 additions & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,23 @@ val serverTest = "servertest"
val clientTest = "clienttest"
sourceSets {
val main by main
main.java.srcDirs(rootProject.file("src/main/java"))
main.resources.srcDirs(rootProject.file("src/main/resources"))
val classPathConfig =
closureOf<SourceSet> {
compileClasspath += main.compileClasspath
compileClasspath += main.output
runtimeClasspath += main.runtimeClasspath
runtimeClasspath += main.output
}
create(serverTest, classPathConfig)
create(clientTest, classPathConfig)
create(serverTest, classPathConfig).apply {
java.srcDirs(rootProject.file("src/$serverTest/java"))
resources.srcDirs(rootProject.file("src/$serverTest/resources"))
}
create(clientTest, classPathConfig).apply {
java.srcDirs(rootProject.file("src/$clientTest/java"))
resources.srcDirs(rootProject.file("src/$clientTest/resources"))
}
}
val serverTestSourceSet = sourceSets.getByName(serverTest)
val clientTestSourceSet = sourceSets.getByName(clientTest)
Expand All @@ -55,7 +63,7 @@ val loaderVersion = project.property("loader_version").toString()
val fabricVersion = project.property("fabric_version").toString()
val flkVersion = project.property("flk_version").toString()
dependencies {
// To change the versions see the gradle.properties file
// To change the versions see versions/<mc>/gradle.properties
minecraft("com.mojang:minecraft:$minecraftVersion")
mappings(loom.officialMojangMappings())
modImplementation("net.fabricmc:fabric-loader:$loaderVersion")
Expand Down Expand Up @@ -96,7 +104,7 @@ dependencies {
}

loom {
accessWidenerPath.set(file("src/main/resources/fabpose.accesswidener"))
accessWidenerPath.set(rootProject.file("src/main/resources/fabpose.accesswidener"))
runtimeOnlyLog4j.set(true)

runs {
Expand All @@ -116,7 +124,7 @@ loom {
configName = clientTest
vmArgs(
"-Dfabric-api.gametest",
"-Dfabric.api.gametest.report-file=${project.layout.buildDirectory}/$name/junit.xml",
"-Dfabric.api.gametest.report-file=${project.layout.buildDirectory.get()}/$name/junit.xml",
)
runDir = "build/$clientTest"
setSource(clientTestSourceSet)
Expand All @@ -133,6 +141,10 @@ loom {
}
}

tasks.withType<AbstractCopyTask>().configureEach {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}

tasks.processResources {
inputs.properties(
"loader_version" to loaderVersion,
Expand Down Expand Up @@ -172,10 +184,21 @@ kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
sourceSets {
named("main") {
kotlin.srcDirs(rootProject.file("src/main/kotlin"))
}
named(serverTest) {
kotlin.srcDirs(rootProject.file("src/$serverTest/kotlin"))
}
named(clientTest) {
kotlin.srcDirs(rootProject.file("src/$clientTest/kotlin"))
}
}
}

tasks.jar {
from("LICENSE") {
from(rootProject.file("LICENSE")) {
rename { "${it}_${project.base.archivesName}" }
}
}
Expand Down Expand Up @@ -250,6 +273,8 @@ fun startXvfb(xvfb: String): Pair<Process, String> {
}

val cleanupXvfbTask = tasks.register("cleanupXvfb") {
group = "verification"
description = "Stops the Xvfb process started for runClienttest, if any."
doLast {
xvfbState.orNull?.let { process ->
if (process.isAlive) {
Expand Down
9 changes: 0 additions & 9 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G

# Fabric Properties
# check these on https://fabricmc.net/develop
minecraft_version=1.21.11
loader_version=0.18.3

# Mod Properties
maven_group=net.yukulab
archives_base_name=fabpose

# Dependencies
fabric_version=0.140.2+1.21.11
flk_version=1.13.8+kotlin.2.3.0
Loading
Loading