summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSho Sakuma <me@m1sk9.dev>2026-04-04 21:57:48 +0900
committerGitHub <noreply@github.com>2026-04-04 21:57:48 +0900
commitbd92ba60d39b4956a580cc56b83ecf55dd348aea (patch)
tree6b548cf832405c008a8370efb52468fa71ad86b3
parent78a6d7ae3fc3c6fcb98a048889cd489d34a10978 (diff)
parenta20c385528fc1d7515ded0c0978cda05e66c3c98 (diff)
downloadLunaticChat-bd92ba60d39b4956a580cc56b83ecf55dd348aea.tar.gz
LunaticChat-bd92ba60d39b4956a580cc56b83ecf55dd348aea.tar.bz2
LunaticChat-bd92ba60d39b4956a580cc56b83ecf55dd348aea.zip
Merge pull request #165 from m1sk9/feat/velocity-support
feat: Improving Velocity's Cycling Compatibility
-rw-r--r--.claude/commands/release.md33
-rw-r--r--.github/workflows/nightly.yaml111
-rw-r--r--.github/workflows/release-paper.yaml140
-rw-r--r--.github/workflows/release-velocity.yaml139
-rw-r--r--.github/workflows/release.yaml62
-rw-r--r--CHANGELOG.md5
-rw-r--r--CLAUDE.md37
-rw-r--r--build.gradle.kts1
-rw-r--r--codecov.yml3
-rw-r--r--engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessage.kt3
-rw-r--r--engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolVersion.kt43
-rw-r--r--engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessageCodecTest.kt22
-rw-r--r--engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolBackwardCompatibilityTest.kt140
-rw-r--r--engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolVersionTest.kt34
-rw-r--r--gradle.properties4
-rw-r--r--platform-paper/build.gradle.kts2
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/lcv/VelocityStatusCommand.kt10
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/velocity/VelocityConnectionManager.kt5
-rw-r--r--platform-velocity/build.gradle.kts2
-rw-r--r--platform-velocity/src/main/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandler.kt14
-rw-r--r--platform-velocity/src/test/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandlerTest.kt3
21 files changed, 638 insertions, 175 deletions
diff --git a/.claude/commands/release.md b/.claude/commands/release.md
new file mode 100644
index 0000000..ff3edc1
--- /dev/null
+++ b/.claude/commands/release.md
@@ -0,0 +1,33 @@
+---
+description: リリースの準備とタグ作成を行う。引数に paper, velocity, both のいずれかを指定。
+---
+
+# Release Command
+
+引数: `$ARGUMENTS`
+
+## 手順
+
+1. `gradle.properties` から `paperVersion` と `velocityVersion` を読み取る
+2. 引数に応じてリリース対象を決定:
+ - `paper` → Paper のみリリース
+ - `velocity` → Velocity のみリリース
+ - `both` → 両方同時リリース
+ - 引数なし → ユーザーに確認する
+3. リリース前チェック:
+ - `./gradlew ktlintCheck` が通ること
+ - `./gradlew test` が通ること
+ - 対象プラットフォームの `shadowJar` がビルドできること
+ - git working tree がクリーンであること
+ - 該当タグがまだ存在しないこと
+4. チェックがすべて通ったら、タグの作成をユーザーに提案する:
+ - `paper` → `git tag paper/v{paperVersion}`
+ - `velocity` → `git tag velocity/v{velocityVersion}`
+ - `both` → `git tag v{paperVersion}` (Paper/Velocity バージョンが異なる場合は注意を促す)
+5. タグを push するかユーザーに確認する (`git push origin {tag}`)
+
+## 注意事項
+
+- タグの作成と push は必ずユーザーの確認を得てから行うこと
+- `both` の場合、Paper と Velocity のバージョンが異なる場合はその旨を明示すること
+- プロトコルバージョン (`ProtocolVersion.kt`) の変更がある場合は、後方互換テスト (`ProtocolBackwardCompatibilityTest`) が通っていることを確認すること
diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml
deleted file mode 100644
index 33de763..0000000
--- a/.github/workflows/nightly.yaml
+++ /dev/null
@@ -1,111 +0,0 @@
-name: Nightly Release
-
-on:
- push:
- branches:
- - main
- paths-ignore:
- - 'docs/**'
- - '*.md'
- - 'LICENSE'
-
-concurrency:
- group: nightly-release
- cancel-in-progress: true
-
-jobs:
- build:
- runs-on: ubuntu-24.04
- outputs:
- version: ${{ steps.version.outputs.version }}
- paper_jar_name: ${{ steps.version.outputs.paper_jar_name }}
- velocity_jar_name: ${{ steps.version.outputs.velocity_jar_name }}
- steps:
- - name: Checkout repository
- uses: actions/checkout@v6
-
- - name: Compute nightly version
- id: version
- run: |
- BASE_VERSION=$(grep '?: "' build.gradle.kts | head -1 | sed 's/.*?: "\(.*\)"/\1/')
- if [ -z "$BASE_VERSION" ]; then
- BASE_VERSION="1.0.0"
- fi
- SHORT_HASH=$(git rev-parse --short HEAD)
- NIGHTLY_VERSION="${BASE_VERSION}-nightly.${SHORT_HASH}"
- echo "version=$NIGHTLY_VERSION" >> $GITHUB_OUTPUT
- echo "paper_jar_name=LunaticChat-${NIGHTLY_VERSION}.jar" >> $GITHUB_OUTPUT
- echo "velocity_jar_name=LunaticChat-${NIGHTLY_VERSION}-velocity.jar" >> $GITHUB_OUTPUT
- echo "Nightly version: $NIGHTLY_VERSION"
-
- - name: Setup Development Environment
- uses: jdx/mise-action@v4
-
- - name: Setup Gradle
- uses: gradle/actions/setup-gradle@v6
-
- - name: Grant execute permission for gradlew
- run: chmod +x gradlew
-
- - name: Build with shadowJar
- run: |
- ./gradlew :platform-paper:shadowJar :platform-velocity:shadowJar \
- -Pversion="${{ steps.version.outputs.version }}" \
- -PisNightly=true \
- --parallel --no-daemon
-
- - name: Upload build artifacts
- uses: actions/upload-artifact@v7
- with:
- name: LunaticChat-nightly-${{ steps.version.outputs.version }}
- path: |
- platform-paper/build/libs/${{ steps.version.outputs.paper_jar_name }}
- platform-velocity/build/libs/${{ steps.version.outputs.velocity_jar_name }}
- retention-days: 7
-
- release:
- runs-on: ubuntu-24.04
- needs: build
- permissions:
- contents: write
- steps:
- - name: Checkout repository
- uses: actions/checkout@v6
-
- - name: Download build artifact
- uses: actions/download-artifact@v8
- with:
- name: LunaticChat-nightly-${{ needs.build.outputs.version }}
-
- - name: Delete existing nightly release
- env:
- GH_TOKEN: ${{ github.token }}
- run: |
- gh release delete nightly --yes --cleanup-tag 2>/dev/null || true
-
- - name: Create nightly release
- env:
- GH_TOKEN: ${{ github.token }}
- run: |
- VERSION=${{ needs.build.outputs.version }}
- PAPER_JAR_NAME=${{ needs.build.outputs.paper_jar_name }}
- VELOCITY_JAR_NAME=${{ needs.build.outputs.velocity_jar_name }}
-
- gh release create nightly \
- "platform-paper/build/libs/$PAPER_JAR_NAME" \
- "platform-velocity/build/libs/$VELOCITY_JAR_NAME" \
- --title "Nightly Build ($VERSION)" \
- --prerelease \
- --notes "$(cat <<'EOF'
- ## ⚠️ Nightly Build
-
- This is an automatically generated nightly build from the latest `main` branch.
-
- **Warning:** This build may be unstable or contain bugs. Do not use in production environments.
-
- - **Version:** `${{ needs.build.outputs.version }}`
- - **Commit:** ${{ github.sha }}
-
- If you encounter any issues, please report them on [GitHub Issues](https://github.com/m1sk9/LunaticChat/issues).
- EOF
- )"
diff --git a/.github/workflows/release-paper.yaml b/.github/workflows/release-paper.yaml
new file mode 100644
index 0000000..49c9af8
--- /dev/null
+++ b/.github/workflows/release-paper.yaml
@@ -0,0 +1,140 @@
+name: Release Paper
+
+on:
+ push:
+ tags:
+ - 'paper/v*'
+
+jobs:
+ validate:
+ runs-on: ubuntu-24.04
+ outputs:
+ version: ${{ steps.get_version.outputs.version }}
+ jar_name: ${{ steps.get_version.outputs.jar_name }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+
+ - name: Extract version from tag and validate
+ id: get_version
+ run: |
+ TAG_VERSION=${GITHUB_REF#refs/tags/paper/v}
+ GRADLE_VERSION=$(grep '^paperVersion=' gradle.properties | cut -d'=' -f2)
+
+ if [ "$TAG_VERSION" != "$GRADLE_VERSION" ]; then
+ echo "Error: Tag version ($TAG_VERSION) does not match gradle.properties paperVersion ($GRADLE_VERSION)"
+ exit 1
+ fi
+
+ echo "version=$TAG_VERSION" >> $GITHUB_OUTPUT
+ echo "jar_name=LunaticChat-$TAG_VERSION.jar" >> $GITHUB_OUTPUT
+ echo "Version validation passed: $TAG_VERSION"
+
+ - name: Check if release already exists
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ TAG_NAME=${GITHUB_REF#refs/tags/}
+ if gh release view "$TAG_NAME" &> /dev/null; then
+ echo "Error: Release $TAG_NAME already exists"
+ exit 1
+ fi
+ echo "Release validation passed: $TAG_NAME does not exist yet"
+
+ build:
+ runs-on: ubuntu-24.04
+ needs: validate
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+
+ - name: Setup Development Environment
+ uses: jdx/mise-action@v4
+
+ - name: Setup Gradle
+ uses: gradle/actions/setup-gradle@v6
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+
+ - name: Build Paper shadowJar
+ run: ./gradlew :platform-paper:shadowJar --no-daemon
+
+ - name: Upload build artifact
+ uses: actions/upload-artifact@v7
+ with:
+ name: LunaticChat-paper-v${{ needs.validate.outputs.version }}
+ path: platform-paper/build/libs/${{ needs.validate.outputs.jar_name }}
+ retention-days: 90
+
+ release:
+ runs-on: ubuntu-24.04
+ needs: [validate, build]
+ permissions:
+ contents: write
+ env:
+ MODRINTH_CHANNEL: release
+ MODRINTH_PAPER_GAME_VERSIONS: "26.1.x"
+ MODRINTH_MC_VERSION: "26.1"
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+
+ - name: Download build artifact
+ uses: actions/download-artifact@v8
+ with:
+ name: LunaticChat-paper-v${{ needs.validate.outputs.version }}
+
+ - name: Create GitHub Release
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ TAG_NAME=${GITHUB_REF#refs/tags/}
+ VERSION=${{ needs.validate.outputs.version }}
+ JAR_NAME=${{ needs.validate.outputs.jar_name }}
+
+ cat > /tmp/release-notes.md <<EOF
+ LunaticChat Paper/Folia v${VERSION} has been released.
+
+ ## Download
+
+ - [GitHub](https://github.com/m1sk9/LunaticChat/releases/tag/${TAG_NAME})
+ - [Modrinth (Paper/Folia)](https://modrinth.com/plugin/lunaticchat/version/${VERSION})
+
+ ## What's new
+
+ ### Highlights
+
+ ### New Features
+
+ ### Improvements
+
+ ### Changes
+
+ ### Bug Fixes
+
+ ### Notes
+
+ EOF
+
+ gh release create "$TAG_NAME" \
+ "platform-paper/build/libs/$JAR_NAME" \
+ --title "$TAG_NAME" \
+ --draft \
+ --notes-file /tmp/release-notes.md
+
+ - name: Publish to Modrinth (Paper/Folia)
+ uses: cloudnode-pro/modrinth-publish@0be4916ad5f081d936eb5615aa35ce3f0949979c # v2
+ with:
+ token: ${{ secrets.MODRINTH_TOKEN }}
+ name: v${{ needs.validate.outputs.version }} (Paper/Folia v${{ env.MODRINTH_MC_VERSION }})
+ project: ${{ secrets.MODRINTH_PROJECT_ID }}
+ version: ${{ needs.validate.outputs.version }}
+ channel: ${{ env.MODRINTH_CHANNEL }}
+ changelog: |-
+ Please refer to the [GitHub release](https://github.com/m1sk9/LunaticChat/releases/tag/paper/v${{ needs.validate.outputs.version }}) for update details.
+ loaders: |-
+ paper
+ folia
+ game-versions: ${{ env.MODRINTH_PAPER_GAME_VERSIONS }}
+ files: platform-paper/build/libs/${{ needs.validate.outputs.jar_name }}
diff --git a/.github/workflows/release-velocity.yaml b/.github/workflows/release-velocity.yaml
new file mode 100644
index 0000000..6a78ea5
--- /dev/null
+++ b/.github/workflows/release-velocity.yaml
@@ -0,0 +1,139 @@
+name: Release Velocity
+
+on:
+ push:
+ tags:
+ - 'velocity/v*'
+
+jobs:
+ validate:
+ runs-on: ubuntu-24.04
+ outputs:
+ version: ${{ steps.get_version.outputs.version }}
+ jar_name: ${{ steps.get_version.outputs.jar_name }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+
+ - name: Extract version from tag and validate
+ id: get_version
+ run: |
+ TAG_VERSION=${GITHUB_REF#refs/tags/velocity/v}
+ GRADLE_VERSION=$(grep '^velocityVersion=' gradle.properties | cut -d'=' -f2)
+
+ if [ "$TAG_VERSION" != "$GRADLE_VERSION" ]; then
+ echo "Error: Tag version ($TAG_VERSION) does not match gradle.properties velocityVersion ($GRADLE_VERSION)"
+ exit 1
+ fi
+
+ echo "version=$TAG_VERSION" >> $GITHUB_OUTPUT
+ echo "jar_name=LunaticChat-$TAG_VERSION-velocity.jar" >> $GITHUB_OUTPUT
+ echo "Version validation passed: $TAG_VERSION"
+
+ - name: Check if release already exists
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ TAG_NAME=${GITHUB_REF#refs/tags/}
+ if gh release view "$TAG_NAME" &> /dev/null; then
+ echo "Error: Release $TAG_NAME already exists"
+ exit 1
+ fi
+ echo "Release validation passed: $TAG_NAME does not exist yet"
+
+ build:
+ runs-on: ubuntu-24.04
+ needs: validate
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+
+ - name: Setup Development Environment
+ uses: jdx/mise-action@v4
+
+ - name: Setup Gradle
+ uses: gradle/actions/setup-gradle@v6
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+
+ - name: Build Velocity shadowJar
+ run: ./gradlew :platform-velocity:shadowJar --no-daemon
+
+ - name: Upload build artifact
+ uses: actions/upload-artifact@v7
+ with:
+ name: LunaticChat-velocity-v${{ needs.validate.outputs.version }}
+ path: platform-velocity/build/libs/${{ needs.validate.outputs.jar_name }}
+ retention-days: 90
+
+ release:
+ runs-on: ubuntu-24.04
+ needs: [validate, build]
+ permissions:
+ contents: write
+ env:
+ MODRINTH_CHANNEL: release
+ MODRINTH_VELOCITY_GAME_VERSIONS: |-
+ 1.21.x
+ 26.1.x
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+
+ - name: Download build artifact
+ uses: actions/download-artifact@v8
+ with:
+ name: LunaticChat-velocity-v${{ needs.validate.outputs.version }}
+
+ - name: Create GitHub Release
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ TAG_NAME=${GITHUB_REF#refs/tags/}
+ VERSION=${{ needs.validate.outputs.version }}
+ JAR_NAME=${{ needs.validate.outputs.jar_name }}
+
+ cat > /tmp/release-notes.md <<EOF
+ LunaticChat Velocity v${VERSION} has been released.
+
+ ## Download
+
+ - [GitHub](https://github.com/m1sk9/LunaticChat/releases/tag/${TAG_NAME})
+ - [Modrinth (Velocity)](https://modrinth.com/plugin/lunaticchat/version/${VERSION}-velocity)
+
+ ## What's new
+
+ ### Highlights
+
+ ### New Features
+
+ ### Improvements
+
+ ### Changes
+
+ ### Bug Fixes
+
+ ### Notes
+
+ EOF
+
+ gh release create "$TAG_NAME" \
+ "platform-velocity/build/libs/$JAR_NAME" \
+ --title "$TAG_NAME" \
+ --draft \
+ --notes-file /tmp/release-notes.md
+
+ - name: Publish to Modrinth (Velocity)
+ uses: cloudnode-pro/modrinth-publish@0be4916ad5f081d936eb5615aa35ce3f0949979c # v2
+ with:
+ token: ${{ secrets.MODRINTH_TOKEN }}
+ name: v${{ needs.validate.outputs.version }} (Velocity)
+ project: ${{ secrets.MODRINTH_PROJECT_ID }}
+ version: ${{ needs.validate.outputs.version }}-velocity
+ channel: ${{ env.MODRINTH_CHANNEL }}
+ changelog: |-
+ Please refer to the [GitHub release](https://github.com/m1sk9/LunaticChat/releases/tag/velocity/v${{ needs.validate.outputs.version }}) for update details.
+ loaders: velocity
+ game-versions: ${{ env.MODRINTH_VELOCITY_GAME_VERSIONS }}
+ files: platform-velocity/build/libs/${{ needs.validate.outputs.jar_name }}
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index aaa22a3..f8000b4 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -9,32 +9,25 @@ jobs:
validate:
runs-on: ubuntu-24.04
outputs:
- version: ${{ steps.get_version.outputs.version }}
+ paper_version: ${{ steps.get_version.outputs.paper_version }}
+ velocity_version: ${{ steps.get_version.outputs.velocity_version }}
paper_jar_name: ${{ steps.get_version.outputs.paper_jar_name }}
velocity_jar_name: ${{ steps.get_version.outputs.velocity_jar_name }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
- - name: Extract version from build.gradle.kts
+ - name: Extract versions from gradle.properties
id: get_version
run: |
- VERSION=$(grep ' version = ' build.gradle.kts | sed 's/.*version = "\(.*\)"/\1/')
- echo "version=$VERSION" >> $GITHUB_OUTPUT
- echo "paper_jar_name=LunaticChat-$VERSION.jar" >> $GITHUB_OUTPUT
- echo "velocity_jar_name=LunaticChat-$VERSION-velocity.jar" >> $GITHUB_OUTPUT
- echo "Extracted version: $VERSION"
+ PAPER_VERSION=$(grep '^paperVersion=' gradle.properties | cut -d'=' -f2)
+ VELOCITY_VERSION=$(grep '^velocityVersion=' gradle.properties | cut -d'=' -f2)
- - name: Validate tag matches version
- run: |
- TAG_VERSION=${GITHUB_REF#refs/tags/v}
- GRADLE_VERSION="${{ steps.get_version.outputs.version }}"
-
- if [ "$TAG_VERSION" != "$GRADLE_VERSION" ]; then
- echo "Error: Tag version ($TAG_VERSION) does not match build.gradle.kts version ($GRADLE_VERSION)"
- exit 1
- fi
- echo "Version validation passed: $TAG_VERSION"
+ echo "paper_version=$PAPER_VERSION" >> $GITHUB_OUTPUT
+ echo "velocity_version=$VELOCITY_VERSION" >> $GITHUB_OUTPUT
+ echo "paper_jar_name=LunaticChat-$PAPER_VERSION.jar" >> $GITHUB_OUTPUT
+ echo "velocity_jar_name=LunaticChat-$VELOCITY_VERSION-velocity.jar" >> $GITHUB_OUTPUT
+ echo "Paper version: $PAPER_VERSION, Velocity version: $VELOCITY_VERSION"
- name: Check if release already exists
env:
@@ -69,7 +62,7 @@ jobs:
- name: Upload build artifacts
uses: actions/upload-artifact@v7
with:
- name: LunaticChat-v${{ needs.validate.outputs.version }}
+ name: LunaticChat-${{ github.ref_name }}
path: |
platform-paper/build/libs/${{ needs.validate.outputs.paper_jar_name }}
platform-velocity/build/libs/${{ needs.validate.outputs.velocity_jar_name }}
@@ -94,28 +87,30 @@ jobs:
- name: Download build artifact
uses: actions/download-artifact@v8
with:
- name: LunaticChat-v${{ needs.validate.outputs.version }}
-
- - name: Get current date
- id: get_date
- run: echo "current_date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
+ name: LunaticChat-${{ github.ref_name }}
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG_NAME=${GITHUB_REF#refs/tags/}
- VERSION=${{ needs.validate.outputs.version }}
+ PAPER_VERSION=${{ needs.validate.outputs.paper_version }}
+ VELOCITY_VERSION=${{ needs.validate.outputs.velocity_version }}
PAPER_JAR_NAME=${{ needs.validate.outputs.paper_jar_name }}
VELOCITY_JAR_NAME=${{ needs.validate.outputs.velocity_jar_name }}
cat > /tmp/release-notes.md <<EOF
- LunaticChat v${VERSION} has been released.
+ LunaticChat ${TAG_NAME} has been released.
+
+ | Platform | Version |
+ |----------|---------|
+ | Paper/Folia | ${PAPER_VERSION} |
+ | Velocity | ${VELOCITY_VERSION} |
## Download
- - [GitHub](https://github.com/m1sk9/LunaticChat/releases/tag/v${VERSION})
- - Modrinth: [Paper/Folia](https://modrinth.com/plugin/lunaticchat/version/${VERSION}), [Velocity](https://modrinth.com/plugin/lunaticchat/version/${VERSION}-velocity)
+ - [GitHub](https://github.com/m1sk9/LunaticChat/releases/tag/${TAG_NAME})
+ - Modrinth: [Paper/Folia](https://modrinth.com/plugin/lunaticchat/version/${PAPER_VERSION}), [Velocity](https://modrinth.com/plugin/lunaticchat/version/${VELOCITY_VERSION}-velocity)
## What's new
@@ -144,12 +139,12 @@ jobs:
uses: cloudnode-pro/modrinth-publish@0be4916ad5f081d936eb5615aa35ce3f0949979c # v2
with:
token: ${{ secrets.MODRINTH_TOKEN }}
- name: v${{ needs.validate.outputs.version }} (Paper/Folia v${{ env.MODRINTH_MC_VERSION }})
+ name: v${{ needs.validate.outputs.paper_version }} (Paper/Folia v${{ env.MODRINTH_MC_VERSION }})
project: ${{ secrets.MODRINTH_PROJECT_ID }}
- version: ${{ needs.validate.outputs.version }}
+ version: ${{ needs.validate.outputs.paper_version }}
channel: ${{ env.MODRINTH_CHANNEL }}
changelog: |-
- Please refer to the [GitHub release](https://github.com/m1sk9/LunaticChat/releases/tag/v${{ needs.validate.outputs.version }}) for update details.
+ Please refer to the [GitHub release](https://github.com/m1sk9/LunaticChat/releases/tag/${{ github.ref_name }}) for update details.
loaders: |-
paper
folia
@@ -160,13 +155,12 @@ jobs:
uses: cloudnode-pro/modrinth-publish@0be4916ad5f081d936eb5615aa35ce3f0949979c # v2
with:
token: ${{ secrets.MODRINTH_TOKEN }}
- name: v${{ needs.validate.outputs.version }} (Velocity)
+ name: v${{ needs.validate.outputs.velocity_version }} (Velocity)
project: ${{ secrets.MODRINTH_PROJECT_ID }}
- version: ${{ needs.validate.outputs.version }}-velocity
+ version: ${{ needs.validate.outputs.velocity_version }}-velocity
channel: ${{ env.MODRINTH_CHANNEL }}
changelog: |-
- Please refer to the [GitHub release](https://github.com/m1sk9/LunaticChat/releases/tag/v${{ needs.validate.outputs.version }}) for update details.
+ Please refer to the [GitHub release](https://github.com/m1sk9/LunaticChat/releases/tag/${{ github.ref_name }}) for update details.
loaders: velocity
game-versions: ${{ env.MODRINTH_VELOCITY_GAME_VERSIONS }}
files: platform-velocity/build/libs/${{ needs.validate.outputs.velocity_jar_name }}
-
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 73cf134..09a5710 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,10 +8,7 @@
- Support for Paper 1.21.X, Folia 1.21.X (and later) has been dropped.
- We have added warnings when running `/lc status` or `/lcv status`, or when logging in, if you are using the Nightly version.
- Fixed an issue where, while Romaji conversion was enabled, chat events were not registered under certain conditions, making Romaji conversion unavailable.
-- We have started distributing Nightly releases
- - Whenever updates are made to `main` (the default branch), we will distribute the current build via GitHub Releases
- - Since the Nightly version is a work-in-progress, it may be unstable or contain bugs. If you encounter any issues, please report them on GitHub Issues
- - **The Nightly version is not available on Modrinth**.
+- Improving Velocity's Cycling Compatibility.
### v0.11.0
diff --git a/CLAUDE.md b/CLAUDE.md
index f579ca9..10e78dd 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -84,6 +84,43 @@ docs/ → VitePress documentation site
| `listener` | Event listeners (chat, join/quit, plugin messages) |
| `velocity` | Cross-server integration |
+## Versioning & Release
+
+### Version Management
+
+Paper and Velocity have independent versions, managed in `gradle.properties`:
+
+```properties
+paperVersion=1.0.0
+velocityVersion=1.0.0
+```
+
+### Release Tags
+
+| Tag Pattern | Workflow | Target |
+|-------------|----------|--------|
+| `paper/v1.1.0` | `release-paper.yaml` | Paper/Folia JAR only |
+| `velocity/v1.0.1` | `release-velocity.yaml` | Velocity JAR only |
+| `v1.2.0` | `release.yaml` | Both (e.g., engine changes) |
+
+### Protocol Version (`engine/protocol/ProtocolVersion.kt`)
+
+Paper-Velocity compatibility is gated by protocol version only. Plugin version is not used for compatibility checks.
+
+| Level | When to bump | Deployment order |
+|-------|-------------|-----------------|
+| **PATCH** | Add optional fields, new sub-channels | Any order |
+| **MINOR** | Add required fields | Velocity first, then Paper |
+| **MAJOR** | Wire format changes | Simultaneous |
+
+`MIN_SUPPORTED_MINOR` controls the acceptance window for older MINOR versions.
+
+### Backward Compatibility
+
+- `PluginMessageCodec` uses `ignoreUnknownKeys = true` to ignore unknown fields
+- `ProtocolBackwardCompatibilityTest` verifies backward compat via JSON snapshots
+- Protocol changes must include snapshot tests
+
## Code Conventions
- Follows [Kotlin Coding Conventions](https://kotlinlang.org/docs/coding-conventions.html), enforced by Ktlint
diff --git a/build.gradle.kts b/build.gradle.kts
index 1228449..b4196d7 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -14,7 +14,6 @@ plugins {
allprojects {
group = "dev.m1sk9"
- version = findProperty("version")?.toString()?.takeIf { it != "unspecified" } ?: "1.0.0"
repositories {
mavenCentral()
diff --git a/codecov.yml b/codecov.yml
index e23de4b..453e954 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -21,6 +21,9 @@ ignore:
# VelocityStatusCommand is tightly coupled to VelocityConnectionManager and has no unit-test
# infrastructure; it is exercised via end-to-end multi-server tests.
- "platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/lcv/VelocityStatusCommand.kt"
+ # VelocityConnectionManager is tightly coupled to the Paper/Bukkit runtime (Plugin, Player,
+ # PluginMessageListener) and is exercised via end-to-end multi-server tests.
+ - "platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/velocity/VelocityConnectionManager.kt"
# The Velocity platform has no unit-test infrastructure; it is exercised via end-to-end
# multi-server tests (docker compose).
- "platform-velocity/src/main/kotlin/**"
diff --git a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessage.kt b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessage.kt
index cee7837..f7090da 100644
--- a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessage.kt
+++ b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessage.kt
@@ -34,6 +34,9 @@ sealed interface PluginMessage {
val compatible: Boolean,
val velocityVersion: String,
val error: String? = null,
+ val protocolMajor: Int = ProtocolVersion.MAJOR,
+ val protocolMinor: Int = ProtocolVersion.MINOR,
+ val protocolPatch: Int = ProtocolVersion.PATCH,
) : PluginMessage
/**
diff --git a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolVersion.kt b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolVersion.kt
index a57d3a7..3cbb2cd 100644
--- a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolVersion.kt
+++ b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolVersion.kt
@@ -4,26 +4,63 @@ package dev.m1sk9.lunaticChat.engine.protocol
* LunaticChat protocol version definition
*
* Manages version of communication protocol between Paper and Velocity.
- * MAJOR.MINOR must match; differences in PATCH are compatible.
+ *
+ * ## Version Bump Rules
+ *
+ * **PATCH** (e.g., 1.0.0 -> 1.0.1):
+ * - Add optional fields with default values to existing message types.
+ * - Add new sub-channels that peers can safely ignore.
+ * - No deployment coordination required.
+ *
+ * **MINOR** (e.g., 1.0.x -> 1.1.0):
+ * - Add required fields to existing messages.
+ * - Add sub-channels whose absence degrades functionality.
+ * - Deployment order: update Velocity first, then Paper servers.
+ * - Set [MIN_SUPPORTED_MINOR] to control the deprecation window.
+ *
+ * **MAJOR** (e.g., 1.x.x -> 2.0.0):
+ * - Remove or rename existing sub-channels or fields.
+ * - Change wire format or encoding.
+ * - Requires simultaneous deployment of all components.
+ *
+ * ## Adding a New Message Type (sub-channel)
+ * 1. Add the data class to [PluginMessage].
+ * 2. Add a sub-channel constant to [PluginMessageCodec.SubChannel].
+ * 3. Add encode/decode branches in [PluginMessageCodec].
+ * 4. Add a backward compatibility snapshot to ProtocolBackwardCompatibilityTest.
+ * 5. Bump PATCH if the new sub-channel is optional, MINOR if it is required.
*/
object ProtocolVersion {
const val MAJOR = 1
const val MINOR = 0
const val PATCH = 0
+ /**
+ * Minimum MINOR version this build can interoperate with (same MAJOR).
+ *
+ * When bumping MINOR, set this to the oldest MINOR version that should still
+ * be accepted. This allows a controlled deprecation window for rolling updates.
+ */
+ const val MIN_SUPPORTED_MINOR = 0
+
val version: String = "$MAJOR.$MINOR.$PATCH"
/**
* Checks if specified protocol version is compatible
*
+ * Compatibility rules:
+ * - MAJOR must match exactly.
+ * - Remote MINOR must be >= [MIN_SUPPORTED_MINOR] and <= [MINOR].
+ * - PATCH is always ignored.
+ *
* @param major Major version
* @param minor Minor version
- * @return true if MAJOR and MINOR match
+ * @return true if compatible
*/
fun isCompatible(
major: Int,
minor: Int,
- ): Boolean = MAJOR == major && MINOR == minor
+ ): Boolean = MAJOR == major && minor in MIN_SUPPORTED_MINOR..MINOR
/**
* Checks compatibility from version string
diff --git a/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessageCodecTest.kt b/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessageCodecTest.kt
index c792aad..e848780 100644
--- a/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessageCodecTest.kt
+++ b/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessageCodecTest.kt
@@ -45,6 +45,28 @@ class PluginMessageCodecTest {
}
@Test
+ fun `encode and decode HandshakeResponse with protocol version fields`() {
+ val original =
+ PluginMessage.HandshakeResponse(
+ compatible = true,
+ velocityVersion = "0.11.0",
+ protocolMajor = 1,
+ protocolMinor = 1,
+ protocolPatch = 0,
+ )
+
+ val encoded = PluginMessageCodec.encode(original)
+ val decoded = PluginMessageCodec.decode(encoded)
+
+ assertIs<PluginMessage.HandshakeResponse>(decoded)
+ assertEquals(original.compatible, decoded.compatible)
+ assertEquals(original.velocityVersion, decoded.velocityVersion)
+ assertEquals(1, decoded.protocolMajor)
+ assertEquals(1, decoded.protocolMinor)
+ assertEquals(0, decoded.protocolPatch)
+ }
+
+ @Test
fun `encode and decode HandshakeResponse incompatible round-trip`() {
val original =
PluginMessage.HandshakeResponse(
diff --git a/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolBackwardCompatibilityTest.kt b/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolBackwardCompatibilityTest.kt
new file mode 100644
index 0000000..217baef
--- /dev/null
+++ b/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolBackwardCompatibilityTest.kt
@@ -0,0 +1,140 @@
+package dev.m1sk9.lunaticChat.engine.protocol
+
+import java.io.ByteArrayOutputStream
+import java.io.DataOutputStream
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertIs
+import kotlin.test.assertNull
+import kotlin.test.assertTrue
+
+/**
+ * Backward compatibility tests using fixed JSON snapshots.
+ *
+ * These snapshots represent the wire format of protocol version 1.0.0.
+ * DO NOT MODIFY these constants after commit — they are the compatibility contract.
+ * If a test fails after modifying a @Serializable class, it means backward compatibility is broken.
+ */
+class ProtocolBackwardCompatibilityTest {
+ companion object {
+ // Protocol 1.0.0 snapshots — NEVER MODIFY after commit
+ const val HANDSHAKE_V1_0_0 =
+ """{"pluginVersion":"0.10.0","protocolMajor":1,"protocolMinor":0,"protocolPatch":0}"""
+
+ const val HANDSHAKE_RESPONSE_V1_0_0 =
+ """{"compatible":true,"velocityVersion":"0.10.0"}"""
+
+ const val HANDSHAKE_RESPONSE_INCOMPATIBLE_V1_0_0 =
+ """{"compatible":false,"velocityVersion":"0.10.0","error":"Version mismatch"}"""
+
+ const val STATUS_RESPONSE_V1_0_0 =
+ """{"velocityVersion":"0.10.0","protocolVersion":"1.0.0","online":true}"""
+
+ const val GLOBAL_CHAT_V1_0_0 =
+ """{"messageId":"abc-123","serverName":"lobby","playerId":"00000001-0000-0000-0000-000000000000","playerName":"TestPlayer","message":"Hello, world!","timestamp":1000}"""
+ }
+
+ private fun buildRawMessage(
+ subChannel: String,
+ json: String,
+ ): ByteArray {
+ val out = ByteArrayOutputStream()
+ val dataOut = DataOutputStream(out)
+ dataOut.writeUTF(subChannel)
+ dataOut.writeUTF(json)
+ return out.toByteArray()
+ }
+
+ @Test
+ fun `current codec can decode protocol 1_0_0 Handshake`() {
+ val data = buildRawMessage("handshake", HANDSHAKE_V1_0_0)
+ val decoded = PluginMessageCodec.decode(data)
+
+ assertIs<PluginMessage.Handshake>(decoded)
+ assertEquals("0.10.0", decoded.pluginVersion)
+ assertEquals(1, decoded.protocolMajor)
+ assertEquals(0, decoded.protocolMinor)
+ assertEquals(0, decoded.protocolPatch)
+ }
+
+ @Test
+ fun `current codec can decode HandshakeResponse without protocol version fields`() {
+ val data = buildRawMessage("handshake_response", HANDSHAKE_RESPONSE_V1_0_0)
+ val decoded = PluginMessageCodec.decode(data)
+
+ assertIs<PluginMessage.HandshakeResponse>(decoded)
+ assertTrue(decoded.compatible)
+ assertEquals("0.10.0", decoded.velocityVersion)
+ assertNull(decoded.error)
+ // Protocol fields should get defaults when missing from old format
+ assertEquals(ProtocolVersion.MAJOR, decoded.protocolMajor)
+ assertEquals(ProtocolVersion.MINOR, decoded.protocolMinor)
+ assertEquals(ProtocolVersion.PATCH, decoded.protocolPatch)
+ }
+
+ @Test
+ fun `current codec can decode HandshakeResponse incompatible without protocol version fields`() {
+ val data = buildRawMessage("handshake_response", HANDSHAKE_RESPONSE_INCOMPATIBLE_V1_0_0)
+ val decoded = PluginMessageCodec.decode(data)
+
+ assertIs<PluginMessage.HandshakeResponse>(decoded)
+ assertEquals(false, decoded.compatible)
+ assertEquals("Version mismatch", decoded.error)
+ }
+
+ @Test
+ fun `current codec can decode protocol 1_0_0 StatusResponse`() {
+ val data = buildRawMessage("status_response", STATUS_RESPONSE_V1_0_0)
+ val decoded = PluginMessageCodec.decode(data)
+
+ assertIs<PluginMessage.StatusResponse>(decoded)
+ assertEquals("0.10.0", decoded.velocityVersion)
+ assertEquals("1.0.0", decoded.protocolVersion)
+ assertTrue(decoded.online)
+ }
+
+ @Test
+ fun `current codec can decode protocol 1_0_0 GlobalChatMessage`() {
+ val data = buildRawMessage("global_chat", GLOBAL_CHAT_V1_0_0)
+ val decoded = PluginMessageCodec.decode(data)
+
+ assertIs<PluginMessage.GlobalChatMessage>(decoded)
+ assertEquals("abc-123", decoded.messageId)
+ assertEquals("lobby", decoded.serverName)
+ assertEquals("00000001-0000-0000-0000-000000000000", decoded.playerId)
+ assertEquals("TestPlayer", decoded.playerName)
+ assertEquals("Hello, world!", decoded.message)
+ assertEquals(1000L, decoded.timestamp)
+ }
+
+ @Test
+ fun `current codec ignores unknown fields in Handshake`() {
+ val json =
+ """{"pluginVersion":"0.10.0","protocolMajor":1,"protocolMinor":0,"protocolPatch":0,"futureField":"value"}"""
+ val data = buildRawMessage("handshake", json)
+ val decoded = PluginMessageCodec.decode(data)
+
+ assertIs<PluginMessage.Handshake>(decoded)
+ assertEquals("0.10.0", decoded.pluginVersion)
+ }
+
+ @Test
+ fun `current codec ignores unknown fields in GlobalChatMessage`() {
+ val json =
+ """{"messageId":"abc","serverName":"lobby","playerId":"pid","playerName":"Test","message":"Hello","timestamp":1000,"futureField":"value","anotherField":42}"""
+ val data = buildRawMessage("global_chat", json)
+ val decoded = PluginMessageCodec.decode(data)
+
+ assertIs<PluginMessage.GlobalChatMessage>(decoded)
+ assertEquals("abc", decoded.messageId)
+ assertEquals("Hello", decoded.message)
+ }
+
+ @Test
+ fun `StatusRequest sub-channel decodes correctly with empty JSON`() {
+ val data = buildRawMessage("status_request", "{}")
+ val decoded = PluginMessageCodec.decode(data)
+
+ assertIs<PluginMessage.StatusRequest>(decoded)
+ }
+}
diff --git a/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolVersionTest.kt b/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolVersionTest.kt
index 7f05039..e8b9df8 100644
--- a/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolVersionTest.kt
+++ b/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolVersionTest.kt
@@ -54,4 +54,38 @@ class ProtocolVersionTest {
fun `isCompatible string with two parts should return false`() {
assertFalse(ProtocolVersion.isCompatible("1.0"))
}
+
+ @Test
+ fun `MIN_SUPPORTED_MINOR should be less than or equal to MINOR`() {
+ assertTrue(ProtocolVersion.MIN_SUPPORTED_MINOR <= ProtocolVersion.MINOR)
+ }
+
+ @Test
+ fun `isCompatible with minor equal to MINOR should return true`() {
+ assertTrue(ProtocolVersion.isCompatible(ProtocolVersion.MAJOR, ProtocolVersion.MINOR))
+ }
+
+ @Test
+ fun `isCompatible with minor equal to MIN_SUPPORTED_MINOR should return true`() {
+ assertTrue(ProtocolVersion.isCompatible(ProtocolVersion.MAJOR, ProtocolVersion.MIN_SUPPORTED_MINOR))
+ }
+
+ @Test
+ fun `isCompatible with minor above MINOR should return false`() {
+ assertFalse(ProtocolVersion.isCompatible(ProtocolVersion.MAJOR, ProtocolVersion.MINOR + 1))
+ }
+
+ @Test
+ fun `isCompatible with minor below MIN_SUPPORTED_MINOR should return false`() {
+ assertFalse(ProtocolVersion.isCompatible(ProtocolVersion.MAJOR, ProtocolVersion.MIN_SUPPORTED_MINOR - 1))
+ }
+
+ @Test
+ fun `isCompatible string with minor below MIN_SUPPORTED_MINOR should return false`() {
+ assertFalse(
+ ProtocolVersion.isCompatible(
+ "${ProtocolVersion.MAJOR}.${ProtocolVersion.MIN_SUPPORTED_MINOR - 1}.0",
+ ),
+ )
+ }
}
diff --git a/gradle.properties b/gradle.properties
index 7750c23..9ab6267 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -3,3 +3,7 @@ kotlin.code.style=official
org.gradle.jvmargs=-Xmx2G
org.gradle.parallel=true
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
+
+# Platform versions (can be released independently)
+paperVersion=1.0.0
+velocityVersion=1.0.0
diff --git a/platform-paper/build.gradle.kts b/platform-paper/build.gradle.kts
index 9e494c8..6194c5a 100644
--- a/platform-paper/build.gradle.kts
+++ b/platform-paper/build.gradle.kts
@@ -6,6 +6,8 @@ plugins {
id("org.jetbrains.dokka")
}
+version = findProperty("paperVersion")?.toString() ?: "0.0.0"
+
repositories {
maven("https://repo.papermc.io/repository/maven-public/") {
name = "papermc-repo"
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/lcv/VelocityStatusCommand.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/lcv/VelocityStatusCommand.kt
index 439a29a..6c0596e 100644
--- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/lcv/VelocityStatusCommand.kt
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/lcv/VelocityStatusCommand.kt
@@ -4,7 +4,6 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder
import dev.m1sk9.lunaticChat.engine.command.CommandResult
import dev.m1sk9.lunaticChat.engine.permission.LunaticChatPermissionNode
import dev.m1sk9.lunaticChat.engine.protocol.ProtocolVersion
-import dev.m1sk9.lunaticChat.paper.BuildInfo
import dev.m1sk9.lunaticChat.paper.LunaticChat
import dev.m1sk9.lunaticChat.paper.command.annotation.Command
import dev.m1sk9.lunaticChat.paper.command.annotation.Permission
@@ -140,15 +139,6 @@ class VelocityStatusCommand(
),
)
- // Nightly warning
- if (BuildInfo.isNightly) {
- sendMessage(
- MessageFormatter
- .format(languageManager.getMessage("general.nightlyWarning"))
- .color(NamedTextColor.YELLOW),
- )
- }
-
// Connection status details
connectionStatus.forEach { line ->
line?.let { sendMessage(it) }
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/velocity/VelocityConnectionManager.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/velocity/VelocityConnectionManager.kt
index 1a7e667..a93cb9f 100644
--- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/velocity/VelocityConnectionManager.kt
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/velocity/VelocityConnectionManager.kt
@@ -197,7 +197,10 @@ class VelocityConnectionManager(
if (response.compatible) {
state = ConnectionState.CONNECTED
velocityVersion = response.velocityVersion
- logger.info("Successfully connected to Velocity (version: ${response.velocityVersion})")
+ logger.info(
+ "Successfully connected to Velocity (version: ${response.velocityVersion}, " +
+ "protocol: ${response.protocolMajor}.${response.protocolMinor}.${response.protocolPatch})",
+ )
future.complete(HandshakeResult.Success(response.velocityVersion))
} else {
state = ConnectionState.FAILED
diff --git a/platform-velocity/build.gradle.kts b/platform-velocity/build.gradle.kts
index f6a4e57..dcd9f9a 100644
--- a/platform-velocity/build.gradle.kts
+++ b/platform-velocity/build.gradle.kts
@@ -4,6 +4,8 @@ plugins {
id("com.gradleup.shadow")
}
+version = findProperty("velocityVersion")?.toString() ?: "0.0.0"
+
repositories {
maven("https://repo.papermc.io/repository/maven-public/") {
name = "papermc-repo"
diff --git a/platform-velocity/src/main/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandler.kt b/platform-velocity/src/main/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandler.kt
index 74da9f2..382eba4 100644
--- a/platform-velocity/src/main/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandler.kt
+++ b/platform-velocity/src/main/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandler.kt
@@ -84,16 +84,7 @@ class PluginMessageHandler(
"Plugin=${handshake.pluginVersion}, Protocol=${handshake.protocolMajor}.${handshake.protocolMinor}.${handshake.protocolPatch}",
)
- // Plugin version check (exact match required)
- val versionMatch = handshake.pluginVersion == pluginVersion
- if (!versionMatch) {
- val error = "Plugin version mismatch: Paper=${handshake.pluginVersion}, Velocity=$pluginVersion"
- logger.error(error)
- sendHandshakeResponse(connection, false, error)
- return
- }
-
- // Protocol version check (MAJOR.MINOR must match)
+ // Protocol version check (MAJOR must match, MINOR within supported range)
val protocolCompatible =
ProtocolVersion.isCompatible(
handshake.protocolMajor,
@@ -126,6 +117,9 @@ class PluginMessageHandler(
compatible = compatible,
velocityVersion = pluginVersion,
error = error,
+ protocolMajor = ProtocolVersion.MAJOR,
+ protocolMinor = ProtocolVersion.MINOR,
+ protocolPatch = ProtocolVersion.PATCH,
)
val data = PluginMessageCodec.encode(response)
diff --git a/platform-velocity/src/test/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandlerTest.kt b/platform-velocity/src/test/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandlerTest.kt
index 876eec6..3342331 100644
--- a/platform-velocity/src/test/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandlerTest.kt
+++ b/platform-velocity/src/test/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandlerTest.kt
@@ -69,7 +69,7 @@ class PluginMessageHandlerTest {
}
@Test
- fun `onPluginMessage should reject version mismatch handshake`() {
+ fun `onPluginMessage should accept different plugin version with matching protocol`() {
val (handler, _, _) = createHandler(pluginVersion = "0.10.0")
val connection = createServerConnection()
@@ -85,6 +85,7 @@ class PluginMessageHandlerTest {
handler.onPluginMessage(event)
+ // Should still send a response (successful handshake)
verify { connection.sendPluginMessage(any<ChannelIdentifier>(), any<ByteArray>()) }
}