diff options
| author | Sho Sakuma <me@m1sk9.dev> | 2026-08-05 04:56:51 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-08-05 04:56:51 +0900 |
| commit | ce1a6123f7d48100ba3b216746127ba269fe21fb (patch) | |
| tree | 4adc410cd8eb063e17035c184658b1045dd68ae4 | |
| parent | 82a592fe744ab4172c8f86fd2a71ea540575174a (diff) | |
| parent | 004cf9308b5d98737125509adee08b9018cce964 (diff) | |
| download | LunaticChat-1.3.0.tar.gz LunaticChat-1.3.0.tar.bz2 LunaticChat-1.3.0.zip | |
Merge pull request #267 from m1sk9/update/development-and-websitev1.3.0
docs: Move release notes onto the documentation site and correct the docs against the implementation
48 files changed, 1233 insertions, 327 deletions
diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index ec03038..1f3fc1e 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -2,7 +2,7 @@ description: リリースの準備とタグ作成を行う。引数に paper, velocity, both のいずれかを指定。 disable-model-invocation: true argument-hint: [paper|velocity|both] -allowed-tools: Bash(./gradlew *), Bash(git *), Read, Grep +allowed-tools: Bash(./gradlew *), Bash(git *), Bash(gh *), Read, Grep, WebFetch --- # Release @@ -28,9 +28,43 @@ allowed-tools: Bash(./gradlew *), Bash(git *), Read, Grep - `velocity` → `git tag velocity/v{velocityVersion}` - `both` → `git tag v{paperVersion}` (Paper/Velocity バージョンが異なる場合は注意を促す) 5. タグを push するかユーザーに確認する (`git push origin {tag}`) +6. push 後は「リリース後の監視」に進む + +## リリース後の監視 + +タグを push したら、リリースワークフローが完走するまで見届ける。ワークフローは `validate` → `build` → `release` の 3 ジョブで、`release` ジョブが GitHub Release の作成と Modrinth への公開を行う。 + +1. ワークフローの起動を確認する: + ``` + gh run list --limit 5 + ``` + タグ名に対応するワークフロー (`Release Paper` / `Release Velocity` / `Release`) が起動していること。数十秒待っても現れない場合はタグの push 自体が失敗していないか確認する +2. 完了まで追跡する: + ``` + gh run watch {run-id} --exit-status + ``` +3. 失敗した場合はジョブごとに原因を切り分け、ユーザーに報告する: + ``` + gh run view {run-id} --log-failed + ``` + - `validate` で失敗 → タグのバージョンと `gradle.properties` の不一致、または同名 Release が既に存在する + - `build` で失敗 → リリース前チェックで通っていたはずなので、CI 環境固有の問題を疑う + - `release` で失敗 → GitHub Release 作成か Modrinth 公開の失敗。Modrinth 側だけ失敗した場合、GitHub Release は既に作られているため再実行すると重複しうる点に注意する +4. 成功したら成果物を確認する: + ``` + gh release view {tag} --json isDraft,assets,url + ``` + - **Release は draft で作成される**。JAR が添付されていることを確認したうえで、公開するかユーザーに確認する (`gh release edit {tag} --draft=false`) + - 添付ファイル名が期待どおりであること (Paper は `LunaticChat-{version}.jar`、Velocity は `LunaticChat-{version}-velocity.jar`) +5. リリースノートのリンク先が存在することを確認する。Release と Modrinth の changelog はどちらもドキュメントサイトを指しているため、対応するページが未デプロイだとリンク切れになる: + - Paper: `https://lc.m1sk9.dev/changelog/paper/v{paperVersion}` + - Velocity: `https://lc.m1sk9.dev/changelog/velocity/v{velocityVersion}` +6. Modrinth に該当バージョンが公開されているかユーザーに確認を依頼する (Modrinth のバージョン一覧は API トークンなしでは追えないため、ここは目視確認を頼む) ## 注意事項 - タグの作成と push は必ずユーザーの確認を得てから行うこと - `both` の場合、Paper と Velocity のバージョンが異なる場合はその旨を明示すること - プロトコルバージョン (`ProtocolVersion.kt`) の変更がある場合は、後方互換テスト (`ProtocolBackwardCompatibilityTest`) が通っていることを確認すること +- リリースは draft で作られる。ワークフローが成功しても、draft を公開するまでリリースは完了していない +- 失敗したワークフローの再実行 (`gh run rerun`) は、`validate` の「Release が既に存在しないこと」チェックに引っかかる可能性がある。再実行の前に GitHub Release とタグの状態を確認し、対処方針をユーザーに提案すること diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 5489c70..3365568 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -16,16 +16,24 @@ If your issue is not already reported, please create a new issue using the provi We welcome contributions to LunaticChat! -LuckPerms adheres to the [Kotlin Coding Conventions](https://kotlinlang.org/docs/coding-conventions.html). +LunaticChat adheres to the [Kotlin Coding Conventions](https://kotlinlang.org/docs/coding-conventions.html). It also uses Ktlint. Merging pull requests into the main branch requires passing Ktlint checks. When submitting a pull request, remember to run `./gradlew ktlintFormat`. When making major changes that break backward compatibility, please discuss them beforehand via an issue or discussion. Such changes often cause confusion when made without prior discussion. +### Design Guide + +Our documentation site provides an overview of LunaticChat's design. + +We highly recommend reading it before you begin development. + +[Introduction / Developer Guide - LunaticChat Documentation](https://lc.m1sk9.dev/docs/developers/introduction) + ### Project Structure -LunaticChat uses a multi-module Gradle setup. +LunaticChat uses a multi-module Gradle setup. - `engine`: Contains code shared across all platforms. - `platform-paper`: Contains code specific to the Paper platform. @@ -35,4 +43,12 @@ LunaticChat uses a multi-module Gradle setup. LunaticChat comes with a Docker environment that can be launched as a debug server. -Running `./x start` will launch a Paper server with LunaticChat installed. +It is driven by `./x <action> <platform>`, where the platform is required: + +- `./x start paper` builds the plugin and starts a single Paper server (`localhost:25565`) +- `./x start folia` starts a single Folia server (`localhost:25565`) +- `./x start velocity` starts a Velocity proxy with two backend Paper servers (`localhost:25577`) + +Builds default to a nightly version derived from the current commit; pass `--stable` to build as a stable release. + +Run `./x help` for the remaining actions (`stop`, `log`, `clean`, `rcon`). diff --git a/.github/ISSUE_TEMPLATE/bug_report_folia.yml b/.github/ISSUE_TEMPLATE/bug_report_folia.yml new file mode 100644 index 0000000..3c7fada --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report_folia.yml @@ -0,0 +1,127 @@ +name: Bug Report (Folia) +description: Report a bug or unexpected behavior on a Folia server +labels: ["Type: bug", "Module: Folia"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report a bug! Please fill out the information below to help us fix the issue. + + Use this template only if the problem happens on **Folia**. If it also reproduces on plain Paper, please use the Paper template instead. If the problem involves a **Velocity** proxy, use the Velocity template. + + - type: textarea + id: description + attributes: + label: Bug Description + description: A clear and concise description of what the bug is. + placeholder: Describe the bug you encountered... + validations: + required: true + + - type: textarea + id: steps-to-reproduce + attributes: + label: Steps to Reproduce + description: Steps to reproduce the behavior + placeholder: | + 1. Run command '...' + 2. Send message '...' + 3. See error + validations: + required: true + + - type: textarea + id: expected-behavior + attributes: + label: Expected Behavior + description: What you expected to happen + placeholder: Describe what you expected to happen... + validations: + required: true + + - type: textarea + id: actual-behavior + attributes: + label: Actual Behavior + description: What actually happened + placeholder: Describe what actually happened... + validations: + required: true + + - type: dropdown + id: reproduces-on-paper + attributes: + label: Does this also happen on Paper? + description: Folia-only behavior usually points at a thread or region issue, so this narrows it down a lot. + options: + - "Folia only (does not happen on Paper)" + - "Happens on Paper too" + - "Not tested on Paper" + validations: + required: true + + - type: input + id: plugin-version + attributes: + label: LunaticChat Version + description: Run `/lc status` in-game to see the version and commit hash. + placeholder: "e.g., v1.3.0 (7c54422)" + validations: + required: true + + - type: input + id: folia-version + attributes: + label: Folia Version + description: Run `/version` on the server. + placeholder: "e.g., Folia 26.2-12" + validations: + required: true + + - type: input + id: minecraft-version + attributes: + label: Minecraft Version + description: What version of Minecraft are you running? + placeholder: "e.g., 26.2" + validations: + required: true + + - type: input + id: java-version + attributes: + label: Java Version + description: LunaticChat requires Java 25 or later. + placeholder: "e.g., Java 25" + validations: + required: true + + - type: checkboxes + id: enabled-features + attributes: + label: Enabled Features + description: Which optional features are enabled in `config.yml`? Most of them are off by default. + options: + - label: "`features.quickReplies` (/reply)" + - label: "`features.japaneseConversion` (romaji to Japanese)" + - label: "`features.channelChat`" + - label: "`features.velocityIntegration`" + + - type: textarea + id: logs + attributes: + label: Logs and Error Messages + description: "Please paste relevant logs or error messages here. Full stack traces matter on Folia — they show which thread the call came from. Setting `debug: true` in `config.yml` produces more detail." + placeholder: Paste your logs here... + render: shell + validations: + required: false + + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: Add any other context about the problem here (how many players and worlds were involved, screenshots, other installed plugins, related issues, etc.) + placeholder: Any additional information... + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report_paper.yml index 9d35bc8..dfddc26 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report_paper.yml @@ -1,12 +1,14 @@ -name: Bug Report -description: Report a bug or unexpected behavior in LunaticChat -labels: ["Type: bug"] +name: Bug Report (Paper) +description: Report a bug or unexpected behavior on a Paper server +labels: ["Type: bug", "Module: Paper"] body: - type: markdown attributes: value: | Thanks for taking the time to report a bug! Please fill out the information below to help us fix the issue. + If your server runs **Folia**, or if the problem involves a **Velocity** proxy (cross-server chat or cross-server direct messages), please use the corresponding template instead. + - type: textarea id: description attributes: @@ -50,26 +52,26 @@ body: id: plugin-version attributes: label: LunaticChat Version - description: What version of LunaticChat are you using? - placeholder: "e.g., v0.1.0" + description: Run `/lc status` in-game to see the version and commit hash. + placeholder: "e.g., v1.3.0 (7c54422)" validations: required: true - type: input - id: minecraft-version + id: paper-version attributes: - label: Minecraft Version - description: What version of Minecraft are you running? - placeholder: "e.g., 1.21.4" + label: Paper Version + description: Run `/version` on the server. + placeholder: "e.g., Paper 26.2-92" validations: required: true - type: input - id: paper-version + id: minecraft-version attributes: - label: Paper Version - description: What version of Paper are you using? - placeholder: "e.g., Paper-1.21.4-123" + label: Minecraft Version + description: What version of Minecraft are you running? + placeholder: "e.g., 26.2" validations: required: true @@ -77,16 +79,27 @@ body: id: java-version attributes: label: Java Version - description: What version of Java are you running? - placeholder: "e.g., Java 21" + description: LunaticChat requires Java 25 or later. + placeholder: "e.g., Java 25" validations: required: true + - type: checkboxes + id: enabled-features + attributes: + label: Enabled Features + description: Which optional features are enabled in `config.yml`? Most of them are off by default. + options: + - label: "`features.quickReplies` (/reply)" + - label: "`features.japaneseConversion` (romaji to Japanese)" + - label: "`features.channelChat`" + - label: "`features.velocityIntegration`" + - type: textarea id: logs attributes: label: Logs and Error Messages - description: Please paste relevant logs or error messages here + description: "Please paste relevant logs or error messages here. Setting `debug: true` in `config.yml` produces more detail." placeholder: Paste your logs here... render: shell validations: @@ -96,7 +109,7 @@ body: id: additional-context attributes: label: Additional Context - description: Add any other context about the problem here (screenshots, related issues, etc.) + description: Add any other context about the problem here (screenshots, other installed plugins, related issues, etc.) placeholder: Any additional information... validations: required: false diff --git a/.github/ISSUE_TEMPLATE/bug_report_velocity.yml b/.github/ISSUE_TEMPLATE/bug_report_velocity.yml new file mode 100644 index 0000000..1de18a2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report_velocity.yml @@ -0,0 +1,156 @@ +name: Bug Report (Velocity) +description: Report a bug involving the Velocity proxy, cross-server chat, or cross-server direct messages +labels: ["Type: bug", "Module: Velocity"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report a bug! Please fill out the information below to help us fix the issue. + + Use this template when the problem involves the **Velocity proxy** — cross-server global chat, cross-server direct messages, or the Paper/Folia to Velocity handshake. A bug that reproduces on a single backend server without the proxy belongs in the Paper or Folia template. + + Paper and Velocity compatibility is decided by an internal **protocol version**, not by the plugin version, so the `/lcv status` output below is the most important field on this form. + + - type: textarea + id: description + attributes: + label: Bug Description + description: A clear and concise description of what the bug is. + placeholder: Describe the bug you encountered... + validations: + required: true + + - type: textarea + id: steps-to-reproduce + attributes: + label: Steps to Reproduce + description: Steps to reproduce the behavior. Please include which server each player was on. + placeholder: | + 1. Player A on 'survival' sends '...' + 2. Player B on 'lobby' runs '...' + 3. See error + validations: + required: true + + - type: textarea + id: expected-behavior + attributes: + label: Expected Behavior + description: What you expected to happen + placeholder: Describe what you expected to happen... + validations: + required: true + + - type: textarea + id: actual-behavior + attributes: + label: Actual Behavior + description: What actually happened + placeholder: Describe what actually happened... + validations: + required: true + + - type: textarea + id: lcv-status + attributes: + label: Output of `/lcv status` + description: "Run `/lcv status` on an affected backend server, **after at least one player has joined** — the handshake is only sent once a player connects, so on an empty server the state is always `DISCONNECTED`. It reports the connection state, the plugin version of both sides, and this server's protocol version." + placeholder: Paste the output here... + validations: + required: true + + - type: input + id: velocity-plugin-version + attributes: + label: LunaticChat Version (Velocity) + description: The version of `LunaticChat-<version>-velocity.jar` installed on the proxy. + placeholder: "e.g., v1.2.0" + validations: + required: true + + - type: input + id: backend-plugin-version + attributes: + label: LunaticChat Version (backend Paper / Folia) + description: Run `/lc status` on a backend server. If your backend servers run different versions, list all of them. + placeholder: "e.g., v1.3.0 (7c54422)" + validations: + required: true + + - type: input + id: velocity-version + attributes: + label: Velocity Version + description: LunaticChat is built against the Velocity 4 API. Velocity 3.5.x and BungeeCord are not supported. + placeholder: "e.g., 4.0.0" + validations: + required: true + + - type: input + id: backend-server-version + attributes: + label: Backend Server Software + description: The software and version of the affected backend servers. + placeholder: "e.g., Paper 26.2-92 / Folia 26.2-12" + validations: + required: true + + - type: input + id: java-version + attributes: + label: Java Version + description: LunaticChat requires Java 25 or later. + placeholder: "e.g., Java 25" + validations: + required: true + + - type: checkboxes + id: enabled-features + attributes: + label: Enabled Cross-Server Features + description: Which `features.velocityIntegration` settings are enabled in the backend `config.yml`? All of them are off by default. + options: + - label: "`enabled`" + - label: "`crossServerGlobalChat`" + - label: "`crossServerDirectMessage`" + + - type: textarea + id: server-layout + attributes: + label: Server Layout + description: The backend servers behind the proxy and the `features.velocityIntegration.serverName` configured on each. Cross-server direct messages are routed by that name, so a mismatch with the Velocity configuration is a common cause. + placeholder: | + - lobby (serverName: "lobby") + - survival (serverName: "survival") + - creative (serverName: "Unknown") <- not configured + validations: + required: false + + - type: textarea + id: velocity-logs + attributes: + label: Velocity Proxy Logs + description: Please paste relevant logs or error messages from the proxy. + placeholder: Paste the proxy logs here... + render: shell + validations: + required: false + + - type: textarea + id: backend-logs + attributes: + label: Backend Server Logs + description: "Please paste relevant logs or error messages from the backend servers. Setting `debug: true` in `config.yml` produces more detail." + placeholder: Paste the backend logs here... + render: shell + validations: + required: false + + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: Add any other context about the problem here (screenshots, other installed plugins, related issues, etc.) + placeholder: Any additional information... + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 5bd060b..d90343e 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -7,6 +7,19 @@ body: value: | Thanks for suggesting a new feature! Please provide as much detail as possible. + - type: dropdown + id: platform + attributes: + label: Target Platform + description: Which platform would this feature apply to? A maintainer will add the matching `Module:` label. + options: + - "Paper" + - "Folia" + - "Velocity (proxy / cross-server)" + - "All platforms / not platform-specific" + validations: + required: true + - type: textarea id: feature-description attributes: diff --git a/.github/SECURITY.md b/.github/SECURITY.md index b2a5b43..8589d94 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -2,15 +2,49 @@ ## Supported Versions -LunaticChat only supports the latest version at any given time. -Older versions are not supported, and backports will not be provided unless absolutely necessary. +The Paper/Folia and Velocity plugins are released independently, and **only the latest release of each is supported**. Older releases are not supported, and backports will not be provided unless absolutely necessary. -### In Minecraft +| Component | Supported | +|-----------|-----------| +| LunaticChat for Paper / Folia (latest `paper/vX.Y.Z`) | Yes | +| LunaticChat for Velocity (latest `velocity/vX.Y.Z`) | Yes | +| Any earlier release | No | +| Nightly builds and CI artifacts | No | -The Minecraft version follows the `api-version` on which the latest LunaticChat runs. However, it fundamentally depends on the Paper API / Velocity API version. +### Platform requirements -(Example: If the `api-version` is `1.21`, operation on `1.21.X` is guaranteed.) +The supported platforms and runtime follow whatever the latest release is built against: + +| | Supported | +|---|---| +| Minecraft | The `api-version` the latest release declares (currently `26.2`), i.e. `26.2.x` | +| Server software | Paper, Folia, and Velocity 4 | +| Java | 25 or later | + +Spigot, BungeeCord, and Velocity 3.5.x are **not** supported, and there are no plans to support them. + +### Paper and Velocity combinations + +Paper–Velocity compatibility is decided by the internal **protocol version**, not by the plugin version. A combination that the protocol rejects will refuse to relay chat; that is intended behavior, not a vulnerability. See [Paper / Velocity Compatibility](https://lc.m1sk9.dev/docs/reference/compatibility) for the rules and the compatibility matrix. + +If a report involves a proxy setup, please include the plugin version of **both** sides and the output of `/lcv status`. ## Reporting a Vulnerability -Do not report security issues using Issues. Please report them by sending an encrypted email to [me@m1sk9.dev](mailto:me@m1sk9.dev). +**Do not report security issues through Issues or Discussions.** Please use one of the following private channels: + +1. **GitHub private vulnerability reporting** (preferred) — [open a draft security advisory](https://github.com/m1sk9/LunaticChat/security/advisories/new). This keeps the report private until a fix is published. +2. **Encrypted email** — [me@m1sk9.dev](mailto:me@m1sk9.dev), encrypted with the public key at [github.com/m1sk9.gpg](https://github.com/m1sk9.gpg). + +Please include as much of the following as you can: + +- The affected component (Paper/Folia or Velocity) and its version +- Minecraft and server software versions, and the Java version +- Which optional features were enabled in `config.yml` (`japaneseConversion`, `channelChat`, `velocityIntegration`, …), since most of them are off by default +- Steps to reproduce, and the impact you believe it has + +A fix is shipped as a new release of the affected platform. If you would like to be credited in the advisory, please say so in your report. + +### Vulnerabilities in dependencies + +Known vulnerabilities in third-party dependencies are tracked by Dependabot and do not need a private report — an ordinary issue or pull request is fine. Please do use a private channel if you can demonstrate that a dependency issue is actually exploitable through LunaticChat. diff --git a/.github/workflows/release-paper.yaml b/.github/workflows/release-paper.yaml index 9459ed6..3718163 100644 --- a/.github/workflows/release-paper.yaml +++ b/.github/workflows/release-paper.yaml @@ -94,35 +94,13 @@ jobs: 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 - + # The notes point at the changelog on the documentation site rather than + # repeating it here, so the release notes live in exactly one place. gh release create "$TAG_NAME" \ "platform-paper/build/libs/$JAR_NAME" \ --title "v${VERSION} (Paper/Folia)" \ --draft \ - --notes-file /tmp/release-notes.md + --notes "Release notes: [Paper/Folia v${VERSION}](https://lc.m1sk9.dev/changelog/paper/v${VERSION})" - name: Publish to Modrinth (Paper/Folia) uses: cloudnode-pro/modrinth-publish@8dc596b20b94959bf244180235d90729d05a674f # v2 @@ -133,7 +111,7 @@ jobs: 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. + Please refer to the [release notes](https://lc.m1sk9.dev/changelog/paper/v${{ needs.validate.outputs.version }}) for update details. loaders: |- paper folia diff --git a/.github/workflows/release-velocity.yaml b/.github/workflows/release-velocity.yaml index e71eef9..561e7ee 100644 --- a/.github/workflows/release-velocity.yaml +++ b/.github/workflows/release-velocity.yaml @@ -96,35 +96,13 @@ jobs: 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 - + # The notes point at the changelog on the documentation site rather than + # repeating it here, so the release notes live in exactly one place. gh release create "$TAG_NAME" \ "platform-velocity/build/libs/$JAR_NAME" \ --title "v${VERSION} (Velocity)" \ --draft \ - --notes-file /tmp/release-notes.md + --notes "Release notes: [Velocity v${VERSION}](https://lc.m1sk9.dev/changelog/velocity/v${VERSION})" - name: Publish to Modrinth (Velocity) uses: cloudnode-pro/modrinth-publish@8dc596b20b94959bf244180235d90729d05a674f # v2 @@ -135,7 +113,7 @@ jobs: 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. + Please refer to the [release notes](https://lc.m1sk9.dev/changelog/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 48be8f8..c139272 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -100,33 +100,13 @@ jobs: PAPER_JAR_NAME=${{ needs.validate.outputs.paper_jar_name }} VELOCITY_JAR_NAME=${{ needs.validate.outputs.velocity_jar_name }} + # The notes point at the changelog on the documentation site rather than + # repeating it here, so the release notes live in exactly one place. cat > /tmp/release-notes.md <<EOF - LunaticChat ${TAG_NAME} has been released. - - | Platform | Version | - |----------|---------| - | Paper/Folia | ${PAPER_VERSION} | - | Velocity | ${VELOCITY_VERSION} | - - ## Download - - - [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 - - ### Highlights - - ### New Features - - ### Improvements - - ### Changes - - ### Bug Fixes - - ### Notes + Release notes: + - [Paper/Folia v${PAPER_VERSION}](https://lc.m1sk9.dev/changelog/paper/v${PAPER_VERSION}) + - [Velocity v${VELOCITY_VERSION}](https://lc.m1sk9.dev/changelog/velocity/v${VELOCITY_VERSION}) EOF gh release create "$TAG_NAME" \ @@ -145,7 +125,7 @@ jobs: version: ${{ needs.validate.outputs.paper_version }} channel: ${{ env.MODRINTH_CHANNEL }} changelog: |- - Please refer to the [GitHub release](https://github.com/m1sk9/LunaticChat/releases/tag/${{ github.ref_name }}) for update details. + Please refer to the [release notes](https://lc.m1sk9.dev/changelog/paper/v${{ needs.validate.outputs.paper_version }}) for update details. loaders: |- paper folia @@ -161,7 +141,7 @@ jobs: 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/${{ github.ref_name }}) for update details. + Please refer to the [release notes](https://lc.m1sk9.dev/changelog/velocity/v${{ needs.validate.outputs.velocity_version }}) 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 54a1516..064e585 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,18 +2,49 @@ ## v1 -### v1.3.0 (UNRELEASED) +### v1.3.0 - Paper 26.2 (Minecraft 26.2) is now supported. - Support for Paper 26.1 has been dropped. Paper 26.2 bundles Adventure 5.2.0, which is not binary compatible with the Adventure 4.x shipped by 26.1, so `api-version` has been raised to `26.2` and the plugin will no longer load on 26.1 servers. -- Added cross-server direct messaging functionality +- Added cross-server direct messaging functionality. + - `/tell <player>@<server>` sends a direct message to a player on another backend server. Disabled by default; enable `features.velocityIntegration.crossServerDirectMessage`. - Fixed a bug where the player argument for `/tell` used partial matching (we switched from the old Bukkit API to the new API) +- `config.yml` is now read with KAML, which changes how a broken file is treated. + - A `config.yml` that cannot be read no longer disables the plugin; it starts on its defaults instead. + - A single unreadable value now falls back to its own default with a warning, instead of discarding every other setting in the file. + - `features.channelChat.messageLogging` is now actually read. It was documented but never loaded, so it stayed at its defaults regardless of what the file said. +- Fixed a bug where a slow Google IME reply could silently stop delivering a player's messages for the rest of their session. + - `GoogleIMEClient` reported a request timeout as a `CancellationException`, which the delivery queue worker correctly read as shutdown and ended its loop on — leaving the player's queue registered with nothing draining it. Triggered whenever `features.japaneseConversion.api.timeout` was set below the message conversion budget. Timeouts are now an ordinary exception. +- Fixed a bug where a single slow conversion pinned a word to hiragana permanently. + - A timed-out conversion was cached as though the API had answered, so that word rendered unconverted for the life of the cache. Timeouts now leave the cache untouched and the next message retries. +- Fixed a bug where `/reply` could not see a reply target that had just been recorded, and a duplicated recording that re-inserted entries already cleared on disconnect. +- Fixed a bug where a setting changed while a save was already pending was not written to disk. +- A failed save no longer aborts the remaining shutdown steps, so channel logs and the Velocity connection are always closed. +- File persistence moved behind a single storage layer. Every data file (`channels.json`, player settings, the conversion cache) is written atomically and debounced by construction rather than per call site, so a crash or a concurrent save can no longer leave a half-written file behind. +- Service teardown is now driven by a `StoppableService` type registered at construction, so a service cannot be missed from the shutdown path. +- Per-player delivery queues are now bounded. A player who sends faster than delivery drains is refused with a warning instead of building an unbounded backlog that arrives minutes late. +- Each player's messages are delivered in the order they were sent. Work still queued when a player disconnects is discarded rather than delivered to a player who has left, and work dropped at shutdown is logged instead of disappearing silently. +- Performance improvements. + - Direct message delivery and channel message logging no longer run on the tick thread. + - Cached romaji conversions no longer wait on the shared API concurrency limiter. A message whose every word was already cached could previously exhaust its conversion budget waiting for a permit it did not need, and be sent unconverted. + - The words of a message are converted concurrently. + - Player settings are only rewritten when something changed. Every player quit previously re-serialized every player stored in the file to write identical bytes. + - Clearing a player's active channel on quit no longer snapshots the channel caches when that player had no active channel. A mass disconnect previously paid a full snapshot per player within one tick. + - Direct message and channel message spy notification no longer builds its translation lookup and member set when no spy is online. + - Channel data writes are coalesced instead of rewriting the file on every change. + - Features that are turned off no longer cost anything at startup, and the HTTP client is only created when a feature actually needs it. +- Internal cleanup. Removed dead code and unreachable error paths: an unused spy accessor, a `Result` that could not fail (with three unreachable handlers and one unreachable user-facing message), a redundant feature-gate clause, and a duplicated error boundary beneath the delivery queue. + - Test suite: 561 → 569. +- The protocol version has been raised to 1.0.1 for the new direct message and presence sub-channels. + - This is a PATCH bump, so Paper and Velocity can be updated in any order. Cross-server direct messages require both sides to be updated. #### Velocity: v1.2.0 - Velocity 4.0.0 is now supported. - Support for Velocity 3.5.x has been dropped. -- Removed an unused YAML dependency, shrinking the Velocity JAR by roughly 1 MB. +- Added cross-server direct message relay and player presence tracking. +- The Velocity JAR shrank from about 8.2 MiB to about 2.6 MiB. + - Paper-only dependencies (Ktor and the romaji converter) were moved out of the shared module, so they are no longer bundled into the Velocity build. ### v1.2.2 @@ -4,17 +4,18 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -LunaticChat is a Minecraft chat plugin supporting Paper, Folia, and Velocity servers. It provides direct messaging, channel chat, and romaji-to-Japanese conversion. Velocity proxy support enables global chat relay across multiple servers. +LunaticChat is a Minecraft chat plugin supporting Paper, Folia, and Velocity servers. It provides direct messaging, channel chat, and romaji-to-Japanese conversion. Velocity proxy support relays global chat and (optionally) direct messages across backend servers. - **Language**: Kotlin (JVM 25) - **Build**: Gradle 9+ with Kotlin DSL -- **Tooling**: mise (Java zulu-25, Bun 1.3.11) +- **Tooling**: mise provisions Java and Bun (versions in `mise.toml`) +- **Docs site**: VitePress in `website/`, deployed to Cloudflare Workers (`lc.m1sk9.dev`) ## Common Commands ### Build ```bash -./gradlew clean build # Full build +./gradlew clean build # Full build (build depends on shadowJar) ./gradlew :platform-paper:shadowJar # Paper/Folia plugin JAR ./gradlew :platform-velocity:shadowJar # Velocity plugin JAR ``` @@ -23,8 +24,8 @@ LunaticChat is a Minecraft chat plugin supporting Paper, Folia, and Velocity ser ```bash ./gradlew test # Run all tests ./gradlew :engine:test # Run engine tests only -./gradlew :platform-paper:test # Run paper tests only ./gradlew :platform-paper:test --tests "*ChannelManagerTest" # Single test class +./gradlew test jacocoTestReport # What CI runs (coverage goes to Codecov) ``` ### Lint @@ -33,98 +34,122 @@ LunaticChat is a Minecraft chat plugin supporting Paper, Folia, and Velocity ser ./gradlew ktlintFormat # Auto-format ``` -### Debug Server (Docker) +### Documentation site (`website/`, Bun) ```bash -./x start # Velocity + 2 Paper servers -./x folia start # Single Folia server -./x rcon s1 # RCON console to Paper s1 -./x help # All available commands +bun install +bun run dev # VitePress dev server +bun run build # Also what CI verifies +bun run check # Biome format + lint, writing fixes +bun run format:check # What CI runs (biome ci .) ``` +### API docs (Dokka) +```bash +./gradlew :dokka:dokkaGenerate # Aggregated HTML into dokka/build/dokka/html +``` + +### Debug server (Docker) + +`./x <action> <platform> [--stable]` — the platform argument is required. + +```bash +./x start velocity # Velocity proxy + Paper s1 & s2 (localhost:25577) +./x start paper # Single Paper server (localhost:25565) +./x start folia # Single Folia server (localhost:25565) +./x rcon velocity s1 # RCON console to backend s1 +./x rcon paper # RCON console to the single Paper server +./x log velocity # Follow logs +./x clean folia # Stop and drop volumes +./x help +``` + +Builds default to a nightly version derived from the git short hash; `--stable` builds a stable release. Server versions are derived from the `paper-api` / `velocity-api` coordinates in the module `build.gradle.kts` files — those are the single source of truth, so never duplicate a Minecraft version into the compose files. + ## Architecture ### Module Structure ``` -engine/ → Platform-agnostic core (models, converters, protocol, exceptions) -platform-paper/ → Paper & Folia plugin (commands, listeners, config, services) -platform-velocity/ → Velocity proxy plugin (global chat relay between servers) +engine/ → Platform-agnostic shared kernel (models, protocol, permissions, exceptions) +platform-paper/ → Paper & Folia plugin (commands, listeners, config, services, converter) +platform-velocity/ → Velocity proxy plugin (cross-server relay) dokka/ → API documentation aggregator (no Kotlin source) -docs/ → VitePress documentation site +website/ → VitePress documentation site ``` -**Dependency flow**: `platform-paper` and `platform-velocity` both depend on `engine`. The engine has no Minecraft platform dependencies. +**Dependency flow**: `platform-paper` and `platform-velocity` both depend on `engine` as equal peers. `engine` has no Minecraft, Adventure, or Brigadier dependency — its only dependency is `kotlinx-serialization-json`, exposed via `api()`. Keep it that way: `engine` must stay testable on a bare JVM. + +`engine` exists to be the single source of truth for anything both sides must agree on — the wire protocol, persistence schemas, permission nodes, `CommandResult`, and the domain exception vocabulary. + +### Key Packages + +| Module | Package | Purpose | +|--------|---------|---------| +| engine | `protocol` | `PluginMessage`, `PluginMessageCodec`, `ProtocolVersion` — the Paper↔Velocity wire contract | +| engine | `chat.channel` | Channel domain model, roles, log entries (`@Serializable` persistence schema) | +| engine | `settings` | Player settings DTOs with UUID serialization | +| engine | `permission` | `LunaticChatPermissionNode` — permission node strings, referenced by type from `@Permission` | +| engine | `command` | `CommandResult` sealed hierarchy (`toBrigadierResult()` returns `Int`, no Brigadier dependency) | +| engine | `exception` | Domain exception hierarchy (mostly channel errors) | +| paper | `command` | Annotation-driven Brigadier command framework (`core`, `annotation`, `impl`, `setting`) | +| paper | `chat.handler` | DirectMessage / ChannelMessage / ChannelNotification handlers | +| paper | `chat.channel` | `ChannelManager` (source of truth), membership, storage, NDJSON logger | +| paper | `converter` | Romaji→Japanese: `KanaConverter`, `GoogleIMEClient` (Ktor), `ConversionCache` | +| paper | `config` | `config.yml` → `LunaticChatConfiguration` via KAML | +| paper | `velocity` | Plugin-messaging client, cross-server chat/DM, remote player registry | +| paper | `i18n` | `LanguageManager` (KAML), `MessageFormatter`, `ChatFormat` | +| velocity | `messaging` | `PluginMessageHandler`, cross-server chat and DM relays | +| velocity | `presence` | `PresenceTracker` — which backend a player is on | ### Key Architectural Patterns -**Service Container** (`platform-paper`): All services are held in an immutable `ServiceContainer` data class. `ServiceInitializer` handles initialization order and conditional feature setup. Optional features (channel chat, Velocity integration, Japanese conversion) are nullable fields. +**Service Container + Feature Gating** (`platform-paper`): manual DI, no framework. `ServiceInitializer` constructs services in dependency order and owns shutdown; the immutable `ServiceContainer` data class holds them. A disabled feature's service is `null`, so feature presence is expressed in the type system and command/listener/setting registration branches on null checks. Adding a feature means: config flag → conditional construction in `ServiceInitializer` → nullable field on `ServiceContainer` → null-checked registration. Never reintroduce `!!` to work around this. -**Feature Gating**: Features are toggled via `config.yml`. The `ServiceInitializer` conditionally creates services based on config, and the `ServiceContainer` holds them as nullable properties. +**Annotation-driven commands**: `@Command(name, aliases, description)`, `@Permission(KClass<out LunaticChatPermissionNode>)`, and `@PlayerOnly` are read via Kotlin reflection (hence the `kotlin-reflect` dependency) and mapped onto a Brigadier tree. Root commands extend `LunaticCommand` and carry `@Command`; nested subcommands extend `LunaticSubCommand` and declare `literal` / `permissionNode` / `aliases` as properties instead. `CommandRegistry` builds the trees inside Paper's `LifecycleEvents.COMMANDS`. Handlers return `CommandResult`; `handleResult()` turns it into an Adventure message plus the Brigadier int. -**Plugin Messaging Protocol**: Cross-server communication uses a custom `PluginMessageCodec` in the engine module. Paper servers encode/decode messages via this codec, and Velocity relays global chat between servers. DM and channel chat are local to each Paper server only. +**Chat routing** (`PlayerChatListener`): a leading `!` forces global. Active channel and no `!` → cancel the event and deliver via `ChannelMessageHandler` (local to the server). Otherwise → global chat, additionally relayed through Velocity when cross-server chat is enabled. Channel chat is always server-local; global chat and (when `crossServerDirectMessage` is on) direct messages cross the proxy. -### Key Packages (engine) +**Folia-safe concurrency**: async work goes through `plugin.server.asyncScheduler` or `PluginCoroutineScope` (SupervisorJob); Bukkit API calls come back via `scheduler.runTask`. `PerPlayerWorkQueue` serializes each player's outgoing messages so a cached conversion cannot overtake an uncached one. Never block the tick thread — the only sanctioned `runBlocking` is the bounded `withTimeoutOrNull` in `RomajiConversionHelper`. -| Package | Purpose | -|---------|---------| -| `chat.channel` | Channel data model and validation | -| `converter` | Google IME API client for romaji conversion | -| `exception` | Custom exception hierarchy (23 types) | -| `protocol` | Plugin messaging codec for Velocity communication | -| `settings` | Player settings models with UUID serialization | +**Persistence**: in-memory cache + debounced async write (`DebouncedSaver`) + synchronous save on shutdown, with `Path.writeTextAtomically()` for every write, since a shutdown save and a pending debounced save can race on the same file. Formats: KAML/YAML for languages and player settings, JSON for `channels.json` and the conversion cache, NDJSON for channel logs. -### Key Packages (platform-paper) +**Config robustness**: `ConfigManager` deserializes `config.yml` with KAML and, on a failure, prunes the offending key and retries so only that setting falls back to its default. A malformed config must never throw out of `onEnable` and disable the plugin. `LenientBoolean` keeps YAML 1.1 spellings (`yes`/`no`/`on`/`off`) working, since Bukkit used to accept them. -| Package | Purpose | -|---------|---------| -| `chat.handler` | DirectMessage, ChannelMessage, ChannelNotification handlers | -| `command` | Command registration and execution | -| `config` | YAML config loading via KAML | -| `i18n` | Language support (EN, JA) | -| `listener` | Event listeners (chat, join/quit, plugin messages) | -| `velocity` | Cross-server integration | +**i18n**: `resources/languages/{en,ja}.yml` are loaded and flattened to dotted keys. Lookup falls back selected-language → EN; a missing `en.yml` entry is fatal. Add every new user-facing string to both files. ## 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 +Paper and Velocity have independent versions in `gradle.properties` (`paperVersion`, `velocityVersion`). | 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) | +| `paper/v1.3.0` | `release-paper.yaml` | Paper/Folia JAR only | +| `velocity/v1.2.0` | `release-velocity.yaml` | Velocity JAR only | +| `v1.3.0` | `release.yaml` | Both (e.g. engine changes) | + +Use the `release` skill (`.claude/skills/release/SKILL.md`) to run the pre-release checks and propose tags. ### Protocol Version (`engine/protocol/ProtocolVersion.kt`) -Paper-Velocity compatibility is gated by protocol version only. Plugin version is not used for compatibility checks. +Paper–Velocity compatibility is gated by protocol version only; plugin version is never used for compatibility checks. MAJOR must match exactly, remote MINOR must fall in `MIN_SUPPORTED_MINOR..MINOR`, PATCH is ignored. | 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 | +| **PATCH** | Optional fields with defaults, ignorable new sub-channels | Any order | +| **MINOR** | Required fields, sub-channels whose absence degrades behavior | Velocity first, then Paper | +| **MAJOR** | Removals, renames, 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 +Adding a message type: data class in `PluginMessage` → constant in `PluginMessageCodec.SubChannel` → encode/decode branches → a snapshot in `ProtocolBackwardCompatibilityTest`. Backward compatibility rests on `ignoreUnknownKeys = true` plus defaulted fields; protocol changes must ship with snapshot tests. ## Code Conventions -- Follows [Kotlin Coding Conventions](https://kotlinlang.org/docs/coding-conventions.html), enforced by Ktlint -- PRs must pass `./gradlew ktlintCheck` +- Follows [Kotlin Coding Conventions](https://kotlinlang.org/docs/coding-conventions.html), enforced by Ktlint; PRs must pass `./gradlew ktlintCheck` +- Comments explain **why not** — a constraint, trade-off, or deliberately avoided approach that the code cannot state itself. The existing comments in `AtomicWrite.kt`, `ConfigManager.kt`, and `PerPlayerWorkQueue.kt` are the reference style - Tests use JUnit 5 + MockK; coroutine tests use `kotlinx-coroutines-test` +- Serialization is kotlinx-serialization, with KAML for YAML - Shadow JAR output: `LunaticChat-{version}.jar` (Paper), `LunaticChat-{version}-velocity.jar` (Velocity) -- Serialization uses kotlinx-serialization with KAML for YAML config files +- New Velocity-side dependencies are weighed against JAR size: `platform-velocity` deliberately pulls in no Ktor + +## Deeper Documentation + +`website/src/docs/developers/` holds long-form design docs (`architecture.md`, `engine.md`, `platform-paper.md`, `platform-velocity.md`, `resource.md`). Read them for rationale, but verify against the source — they lag behind refactors (they still place `converter` in `engine` and describe `ConfigManager` as reading Bukkit's `FileConfiguration`). Update them in the same change when you move a boundary they describe. diff --git a/README.ja.md b/README.ja.md deleted file mode 100644 index 3b96f9e..0000000 --- a/README.ja.md +++ /dev/null @@ -1,64 +0,0 @@ -# LunaticChat - -[](https://github.com/m1sk9/LunaticChat/actions/workflows/ci.yaml) -[](https://github.com/m1sk9/LunaticChat/actions/workflows/release.yaml) -[](https://github.com/m1sk9/LunaticChat/blob/main/LICENSE) - -[](https://codecov.io/github/m1sk9/LunaticChat) - -[English](./README.md) - -Paper・Folia・Velocity 向けの次世代チャットプラグイン. - -- [ドキュメント](https://lc.m1sk9.dev) -- [API ドキュメント](https://lc.api.m1sk9.dev) - -```shell -git clone git@github.com:m1sk9/LunaticChat.git -cd LunaticChat - -./gradlew shadowJar -``` - -_[Supports Minecraft 26.2](https://ja.minecraft.wiki/w/Java_Edition_26.2) | [Requires Java 25+ and Gradle 9+](.github/CONTRIBUTING.md)_ - -## インストール - -LunaticChat は以下のプラットフォームに対応しています. - -- Paper -- Velocity -- Folia - -ビルド済みのプラグインを [ダウンロードページ](https://lc.m1sk9.dev/download) から入手し,ファイルをサーバーの `plugins` フォルダに配置してサーバーを再起動してください. - -詳しくは [ドキュメント](https://lc.m1sk9.dev/docs/getting-started) を参照してください. - -> [!WARNING] -> LunaticChat は Paper / Folia サーバーのみをサポートしています.Spigot および BungeeCord はサポートしておらず,今後サポートする予定もありません.Spigot 環境では [LunaChat の fork](https://github.com/f1w3/LunaChat) の利用を推奨します. - -## 機能 - -- 1 対 1 のダイレクトメッセージ機能 (`/tell`,`/msg`) -- クイックリプライ機能 (`/reply`) -- ローマ字から日本語への変換 -- チャンネルチャット機能 -- マルチプラットフォーム対応 (Paper,Folia,Velocity) - -## Velocity 連携 - -LunaticChat は Velocity プロキシ配下の複数の Paper / Folia サーバー間でグローバルチャットを中継できます.Velocity プロキシと各バックエンドサーバーの両方にプラグインを導入することで,サーバーをまたいだチャットが可能になります. - -Paper と Velocity の互換性は,プラグインのバージョンではなく内部の **プロトコルバージョン** のみで判定されます.プロトコルバージョンが非互換な Paper / Velocity ビルドはチャットの中継を拒否するため,両者は常に揃えて更新してください. - -対応する組み合わせや設定の詳細は [ドキュメント](https://lc.m1sk9.dev) を参照してください. - -## ライセンス - -LunaticChat は [GNU General Public License v3.0](./LICENSE) のもとで公開されています. - -<sub> - ® 2026 m1sk9 - <br/> - LunaticChat は Mojang Studios または Microsoft とは関係ありません. -</sub> @@ -1,7 +1,5 @@ # LunaticChat -[日本語](./README.ja.md) - [](https://github.com/m1sk9/LunaticChat/actions/workflows/ci.yaml) [](https://github.com/m1sk9/LunaticChat/actions/workflows/release.yaml) [](https://github.com/m1sk9/LunaticChat/blob/main/LICENSE) @@ -13,45 +11,104 @@ A next-generation chat plugin for Paper, Folia and Velocity. - [Documentation](https://lc.m1sk9.dev) - [API Documentation](https://lc.api.m1sk9.dev) -```shell -git clone git@github.com:m1sk9/LunaticChat.git -cd LunaticChat +_[Supports Minecraft 26.2](https://minecraft.wiki/w/Java_Edition_26.2) | [Requires Java 25+ and Gradle 9+](.github/CONTRIBUTING.md)_ -./gradlew shadowJar -``` +## Features -_[Supports Minecraft 26.2](https://minecraft.wiki/w/Java_Edition_26.2) | [Requires Java 25+ and Gradle 9+](.github/CONTRIBUTING.md)_ +- **Channel Chat**: **Create and manage channels for group conversations between specific players. Includes private channels and moderation features.** +- **Direct Messages**: **Send 1-on-1 chats with `/tell` or `/msg` commands. Quickly reply to the last sender with `/reply`.** +- **Romaji Conversion**: **Automatically convert romaji input into Japanese. Fast performance powered by caching.** +- **Velocity Cross-Server Chat**: **Relay global chat across multiple servers via a Velocity proxy. Join conversations from any server.** +- **Flexible Configuration**: **Toggle features on/off with a YAML-based config file. Customize to fit your server's needs.** +- **Latest Version Support**: **Minimal external plugin dependencies, always supporting the latest Minecraft versions.** ## Installation -LunaticChat is compatible with the following platforms: +> [!WARNING] +> LunaticChat only supports Paper / Folia servers. Spigot and BungeeCord are not supported, and there are no plans to support them in the future. For Spigot environments, we recommend using a [fork of LunaChat](https://github.com/f1w3/LunaChat). + +### Requirements -- Paper -- Velocity -- Folia +| Item | Requirement | +|------|-------------| +| Minecraft | 26.2 or later | +| Java | 25 or later | +| Server | Paper, Folia, or Velocity | -Download the pre-built plugin from the [Download page](https://lc.m1sk9.dev/download), place the file in your server's `plugins` folder, and restart the server. +### Download -See the [Documentation](https://lc.m1sk9.dev/docs/getting-started). +Grab the plugin JAR from either of the following: -> [!WARNING] -> LunaticChat only supports Paper / Folia servers. Spigot and BungeeCord are not supported, and there are no plans to support them in the future. For Spigot environments, we recommend using a [fork of LunaChat](https://github.com/f1w3/LunaChat). +- [GitHub Releases](https://github.com/m1sk9/LunaticChat/releases) +- [Modrinth](https://modrinth.com/project/lunaticchat) -## Features +Paper and Folia use `LunaticChat-<version>.jar`; Velocity proxies use `LunaticChat-<version>-velocity.jar`. The two are versioned independently. + +### Paper / Folia + +1. Place `LunaticChat-<version>.jar` into the server's `plugins/` directory +2. Start (or restart) the server +3. `plugins/LunaticChat/config.yml` is generated automatically +4. Adjust the [configuration](#configuration) as needed and restart the server + +### Velocity + +1. Place `LunaticChat-<version>-velocity.jar` into the Velocity `plugins/` directory +2. Start (or restart) the proxy +3. Set `features.velocityIntegration.enabled` to `true` in the `config.yml` of every backend server +4. Restart the backend servers, then check the connection with `/lcv status` — the handshake is sent once a player joins, so an empty server reports `DISCONNECTED` + +For more detail, see [Getting Started](https://lc.m1sk9.dev/docs/getting-started). -- 1on1 Direct Messaging System (`/tell`, `/msg`) -- Quick Reply Functionality (`/reply`) -- Romaji to Japanese Conversion -- Channel Chat System -- Multi-platform support (Paper, Folia, Velocity) +## Configuration + +Server-wide settings live in `plugins/LunaticChat/config.yml`, generated on first startup. **Most features are disabled by default**, so enable the ones you want: + +```yaml +language: "en" # Plugin language (en / ja) + +features: + quickReplies: + enabled: true # /reply command + japaneseConversion: + enabled: false # Romaji to Japanese conversion + channelChat: + enabled: false # Channel chat + velocityIntegration: + enabled: false # Velocity proxy integration +``` + +There is no reload command — **restart the server to apply a change**. + +Chat output is customizable through `messageFormat` with placeholders such as `{sender}`, `{message}`, `{channel}` and `{server}`: + +```yaml +messageFormat: + channelMessageFormat: "§7[§b#{channel}§7] §e{sender}: §f{message}" +``` + +Players control their own preferences with `/lc settings` (direct message notifications, channel notifications, and romaji conversion). These are stored per UUID in `player-settings.yaml`. + +Every key, its type, and its default are listed in the [Configuration reference](https://lc.m1sk9.dev/docs/configuration). See also the [Player Settings reference](https://lc.m1sk9.dev/docs/reference/player-settings) and the [default `config.yml`](./platform-paper/src/main/resources/config.yml). ## Velocity Integration -LunaticChat can relay global chat across multiple Paper / Folia servers behind a Velocity proxy. Installing the plugin on both the Velocity proxy and each backend server lets players chat across servers. +LunaticChat can relay global chat across multiple Paper / Folia servers behind a Velocity proxy, and optionally deliver direct messages across servers. Install the plugin on both the Velocity proxy and each backend server. -Paper–Velocity compatibility is determined solely by an internal **protocol version**, not by the plugin version. Paper and Velocity builds with incompatible protocol versions will refuse to relay chat, so keep both sides updated together. +Paper–Velocity compatibility is determined solely by an internal **protocol version**, not by the plugin version. Builds with incompatible protocol versions will refuse to relay chat, so keep both sides updated together. + +For supported combinations and setup details, see [Paper / Velocity Compatibility](https://lc.m1sk9.dev/docs/reference/compatibility) and [Velocity Integration](https://lc.m1sk9.dev/docs/features/velocity). + +## Building from Source + +```shell +git clone git@github.com:m1sk9/LunaticChat.git +cd LunaticChat + +./gradlew shadowJar +``` -For supported combinations and setup details, see the [Documentation](https://lc.m1sk9.dev). +The JARs are written to `platform-paper/build/libs/` and `platform-velocity/build/libs/`. See [CONTRIBUTING](.github/CONTRIBUTING.md) for the development workflow, and the [Developer Guide](https://lc.m1sk9.dev/docs/developers/introduction) for the design overview. ## License diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts index 5237228..1d139d4 100644 --- a/website/.vitepress/config.mts +++ b/website/.vitepress/config.mts @@ -39,6 +39,10 @@ export default defineConfig({ }, }, outDir: './dist', + // Templates under assets/ are meant to be copied by hand when writing a new + // page, not served: changelog-default.md would otherwise publish a page of + // empty headings at /assets/changelog-default. + srcExclude: ['assets/**/*.md'], srcDir: 'src', themeConfig: { socialLinks: [ diff --git a/website/.vitepress/config/en.ts b/website/.vitepress/config/en.ts index 26076c9..b0bcb63 100644 --- a/website/.vitepress/config/en.ts +++ b/website/.vitepress/config/en.ts @@ -8,6 +8,7 @@ export const en: DefaultTheme.Config = { nav: [ { link: '/download', text: 'Download' }, { link: '/docs/getting-started', text: 'Documentation' }, + { link: '/changelog/overview', text: 'Changelog' }, ], sidebar: { '/docs/': [ @@ -105,5 +106,35 @@ export const en: DefaultTheme.Config = { ], }, ], + '/changelog/': [ + { + text: 'Paper', + items: [ + { + text: 'v1', + items: [ + { + link: '/changelog/paper/v1.3.0', + text: 'v1.3.0', + }, + ], + }, + ], + }, + { + text: 'Velocity', + items: [ + { + text: 'v1', + items: [ + { + link: '/changelog/velocity/v1.2.0', + text: 'v1.2.0', + }, + ], + }, + ], + }, + ], }, }; diff --git a/website/.vitepress/config/ja.ts b/website/.vitepress/config/ja.ts index 1f8f4c0..a497293 100644 --- a/website/.vitepress/config/ja.ts +++ b/website/.vitepress/config/ja.ts @@ -8,6 +8,7 @@ export const ja: DefaultTheme.Config = { nav: [ { link: '/ja/download', text: 'ダウンロード' }, { link: '/ja/docs/getting-started', text: 'ドキュメント' }, + { link: '/ja/changelog/overview', text: '更新履歴' }, ], sidebar: { '/ja/docs/': [ @@ -105,5 +106,41 @@ export const ja: DefaultTheme.Config = { ], }, ], + '/ja/changelog/': [ + /** + { + link: '/ja/changelog/overview', + text: 'パッチノート', + }, + */ + { + text: 'Paper', + items: [ + { + text: 'v1', + items: [ + { + link: '/ja/changelog/paper/v1.3.0', + text: 'v1.3.0', + }, + ], + }, + ], + }, + { + text: 'Velocity', + items: [ + { + text: 'v1', + items: [ + { + link: '/ja/changelog/velocity/v1.2.0', + text: 'v1.2.0', + }, + ], + }, + ], + }, + ], }, }; diff --git a/website/src/assets/changelog-default.md b/website/src/assets/changelog-default.md new file mode 100644 index 0000000..1e9be22 --- /dev/null +++ b/website/src/assets/changelog-default.md @@ -0,0 +1,26 @@ +--- +layout: doc +--- + +# vX.Y.Z + +<!-- +- Download: + - [GitHub](https://github.com/m1sk9/LunaticChat/releases/tag/paper/vX.Y.Z) + - [Modrinth](https://modrinth.com/plugin/lunaticchat/version/X.Y.Z) + +- Download: + - [GitHub](https://github.com/m1sk9/LunaticChat/releases/tag/velocity/vX.Y.Z) + - [Modrinth (Velocity)](https://modrinth.com/plugin/lunaticchat/version/X.Y.Z-velocity) + +--> + +## New Features + +## Improvements + +## Changes + +## Bug Fixes + +## Notes diff --git a/website/src/changelog/overview.md b/website/src/changelog/overview.md new file mode 100644 index 0000000..b0e4a97 --- /dev/null +++ b/website/src/changelog/overview.md @@ -0,0 +1,53 @@ +--- +layout: doc +--- + +# Changelog + +The release history of LunaticChat. + +## Versioning + +::: tip + +For the details of independent versioning in LunaticChat, see [Build, Release & Versioning](/docs/developers/resource). + +::: + +LunaticChat follows semantic versioning. A version number consists of the three numbers `MAJOR.MINOR.PATCH`, each raised on the following basis. + +| Position | Raised when | Example | +|----------|-------------|---------| +| **MAJOR** | Used for the move from the pre-release (v0) line to the first stable release. It has not been raised since entering the stable line | `v0.11.0` → `v1.0.0` | +| **MINOR** | A feature is added or changed, and **when a supported platform is dropped** | `v1.2.2` → `v1.3.0` (cross-server DM added, Paper 26.1 dropped) | +| **PATCH** | Bug fixes and dependency updates only. No functional changes | `v1.2.0` → `v1.2.1` (dependency updates only) | + +::: warning Dropping support is done in a MINOR bump + +Cutting a runtime environment loose — "support for Paper 26.1 has ended", "support for Velocity 3.5.x has ended" — **is done in a MINOR bump**. MAJOR is not raised for it, so there can be combinations that stop working across nothing more than a minor version update. Check the changelog for the target version before updating. + +::: + +### The Paper and Velocity builds have separate version numbers + +The plugin for Paper / Folia and the plugin for Velocity are released independently, so their version numbers advance separately. Numbers that do not line up are the normal state of affairs. + +Releases are cut with a tag naming the target. + +| Tag | Released target | +|-----|-----------------| +| `paper/vX.Y.Z` | The Paper / Folia build only | +| `velocity/vX.Y.Z` | The Velocity build only | +| `vX.Y.Z` | Both at once, for a change that affects them both — such as one in the shared `engine` module | + +### The plugin version does not express compatibility + +Whether a Paper build and a Velocity build can talk to each other is decided by the **protocol version** embedded in both, not by the plugin version. Builds with distant version numbers may be combinable, and conversely builds with adjacent numbers may not be. + +For details, see [Paper / Velocity Compatibility](/docs/reference/compatibility). + +### Supported versions + +Only **the latest release of each platform** is supported. Fixes are not backported to older versions unless it is unavoidable. + +Nightly builds are generated automatically from in-development commits. They are not formal releases and are therefore not supported. When a nightly build is in use, a warning is shown on join and when `/lc status` is run. diff --git a/website/src/changelog/paper/v1.3.0.md b/website/src/changelog/paper/v1.3.0.md new file mode 100644 index 0000000..599a59a --- /dev/null +++ b/website/src/changelog/paper/v1.3.0.md @@ -0,0 +1,60 @@ +--- +layout: doc +--- + +# v1.3.0 + +- Download: + - [GitHub](https://github.com/m1sk9/LunaticChat/releases/tag/paper/v1.3.0) + - [Modrinth](https://modrinth.com/plugin/lunaticchat/version/1.3.0) + +## New Features + +- Added support for Paper 26.2 (Minecraft 26.2) + - Support for Paper 26.1 has ended at the same time. + - Paper 26.2 bundles Adventure 5.2.0, which is not binary compatible with 26.1. + - Builds from v1.3.0 onward therefore no longer run on Paper 26.1. +- `/tell <player>@<server>` can now message a player on a server connected through Velocity + - Disabled by default. + - Enable it with `features.velocityIntegration.crossServerDirectMessage`. +- Raised the protocol version to `1.0.1`. + - Paper and Velocity can be updated in either order. + - Cross-server direct messages do, however, require both sides to be updated. + +## Improvements + +- A failed player data save no longer aborts the remaining shutdown steps, so channel logs and the Velocity connection are always closed. +- File persistence has been consolidated behind a single storage layer. + - As a result, a crash or a concurrent save can no longer leave a file in a half-written state. +- Each player's messages are now delivered in the order they were sent. + - Work still queued when a player disconnects is discarded rather than delivered to a player who has already logged out. +- Direct message delivery and channel message logging no longer run on the tick thread. +- Cached romaji conversion results no longer wait for a permit from the shared API concurrency limiter. + - Previously, even a message whose every word was already cached could exhaust its conversion budget waiting for a permit it did not need, and be sent unconverted. +- The words in a message are now converted concurrently. +- Player settings are only rewritten when something has changed. + - Previously, every player quit re-serialized every player stored in the file, even when the bytes were identical. +- When a player quits without an active channel, clearing their active channel no longer takes a snapshot of the channel caches. + - Previously, a mass disconnect produced one full snapshot per disconnecting player within a single tick. +- Spy notification for direct messages and channel messages no longer builds its translation lookup and member set when no recipient is online. +- Channel data writes are now batched instead of rewriting the whole file on every change. +- Performed internal cleanup. + +## Changes + +- `config.yml` is now read with KAML, which changes how a broken file is treated. + - An unreadable configuration file no longer disables the plugin; it starts on its default settings instead. + - When a single value cannot be read, the other settings in the file are no longer all discarded: only that value falls back to its default, with a warning. +- Per-player delivery queues are now bounded. + - A player who sends faster than delivery drains is refused with a warning, rather than building an unbounded backlog that arrives all at once minutes later. + +## Bug Fixes + +- Fixed the player argument of `/tell` matching partially + - This came from the switch away from the old Bukkit API. +- Fixed `features.channelChat.messageLogging` never actually being read and falling back to its default values + - The loading was never implemented despite being documented, so it stayed at its defaults regardless of what the file said. +- Fixed a slow Google IME reply silently stopping every subsequent message delivery for that player's session +- Fixed a single conversion timeout permanently pinning a word to its hiragana form +- Fixed `/reply` not recognizing a reply target that had just been recorded +- Fixed reply target entries that had already been cleared on disconnect being recorded again diff --git a/website/src/changelog/velocity/v1.2.0.md b/website/src/changelog/velocity/v1.2.0.md new file mode 100644 index 0000000..272fb50 --- /dev/null +++ b/website/src/changelog/velocity/v1.2.0.md @@ -0,0 +1,23 @@ +--- +layout: doc +--- + +# v1.2.0 + +- Download: + - [GitHub](https://github.com/m1sk9/LunaticChat/releases/tag/velocity/v1.2.0) + - [Modrinth (Velocity)](https://modrinth.com/plugin/lunaticchat/version/1.2.0-velocity) + +## New Features + +- Added support for Velocity 4.0.0 + - Support for the Velocity 3.5.x line has ended. +- Added cross-server direct message relay and player presence tracking. +- Raised the protocol version to `1.0.1`. + - Paper and Velocity can be updated in either order. + - Cross-server direct messages do, however, require both sides to be updated. + +## Improvements + +- The Velocity JAR shrank from about 8.2 MiB to about 2.6 MiB. + - Paper-only dependencies (Ktor and the romaji conversion module) were separated out of the shared module and are no longer bundled into the Velocity build. diff --git a/website/src/docs/configuration.md b/website/src/docs/configuration.md index 78430f3..f2cfe5b 100644 --- a/website/src/docs/configuration.md +++ b/website/src/docs/configuration.md @@ -6,6 +6,22 @@ layout: doc LunaticChat's configuration is managed in `plugins/LunaticChat/config.yml`. A default configuration file is generated on the server's first startup. +## Applying Changes + +There is no reload command. Edit `config.yml` and **restart the server** to apply a change. + +Boolean settings accept `true` / `false`, and also the `yes` / `no` / `on` / `off` spellings that Bukkit accepted historically, so a file written for an older release keeps working as it did. + +## Recovery From an Invalid File <Badge type="tip" text="v1.3.0~" /> + +A `config.yml` the plugin cannot use never stops the plugin from starting. + +- If a **single value** cannot be read, only that setting falls back to its default, and a warning naming the key is logged. Every other setting in the file is still honoured. +- If the file is **not valid YAML at all**, or cannot be read from disk, every setting falls back to its default and an error is logged. +- A file containing only comments is a valid way of saying "use the defaults" and is not reported as a problem. + +Check the server log after editing `config.yml`: a setting that quietly reverted to its default was reported there. + ## Global Settings | Key | Type | Default | Description | @@ -68,6 +84,20 @@ LunaticChat's configuration is managed in `plugins/LunaticChat/config.yml`. A de | `channelMessageFormat` | `§7[§b#{channel}§7] §e{sender}: §f{message}` | `{sender}`, `{message}`, `{channel}` | | `crossServerGlobalChatFormat` | `§7[§6{server}§7] §e{sender}: §f{message}` | `{sender}`, `{message}`, `{server}` | +## Data Files + +Everything the plugin writes lives under `plugins/LunaticChat/`. + +| File | Written when | Notes | +|------|--------------|-------| +| `config.yml` | Generated on first startup | Never rewritten by the plugin | +| `player-settings.yaml` | A player changes a setting with `/lc settings` | Path configurable via `userSettingsFilePath`. If it cannot be read at startup, **every player's settings fall back to their defaults** | +| `channels.json` | Channels or memberships change | Only when channel chat is enabled | +| `conversion_cache.json` | Periodically, per `cache.saveIntervalSeconds` | Only when Japanese conversion is enabled. Path configurable via `cache.filePath` | +| `logs/channelchat/` | Per channel message | Only when message logging is enabled. See [Message Logging](/docs/features/message-logging) | + +Saves are coalesced rather than written on every change, and every file is written atomically, so nothing ever reads a half-written file. All of them are also flushed when the server stops. + ## Default Configuration File [View on GitHub](https://github.com/m1sk9/LunaticChat/blob/main/platform-paper/src/main/resources/config.yml) diff --git a/website/src/docs/developers/architecture.md b/website/src/docs/developers/architecture.md index 4ba88d1..0532594 100644 --- a/website/src/docs/developers/architecture.md +++ b/website/src/docs/developers/architecture.md @@ -34,15 +34,11 @@ Things that break unless Paper and Velocity share the exact same definition. - `exception` — the shared vocabulary of domain errors - `permission`, `command` — neutral abstractions for permission node strings and command results -#### (b) Platform-independent pure logic - -Logic that could live anywhere, but is pulled into the neutral core because it is pure and reusable. - -- `converter` — the pure romaji-conversion algorithm (Trie) plus an external API client +Everything in `engine` falls into this category. Logic that merely *could* live anywhere is not pulled in for that reason alone: romaji conversion used to sit here as "platform-independent pure logic", and moving it into `platform-paper` — where its only caller is — let `engine` shed its Ktor dependency, which the Velocity build had been paying for in JAR size for nothing. The primary goal of centralizing (a) in `engine` is to create a **single source of truth for the wire contract**. Paper and Velocity are two artifacts built, deployed, and versioned separately; duplicating the protocol in both modules would inevitably drift. With a single definition in `engine`, a contract mismatch surfaces early as a compile error or a snapshot-test failure rather than a runtime mismatch in production. -`engine` depends on no Bukkit / Velocity API, and borrows only the "meaning of types and values" from Adventure / Brigadier to avoid depending on their runtimes (`compileOnly` Adventure, and `toBrigadierResult()` returning an `Int` without depending on Brigadier itself). This lets `engine` be tested on a pure JVM without spinning up a Minecraft server, while platform concerns (the Folia scheduler, etc.) stay isolated in the platform modules. +`engine` depends on no Bukkit / Velocity / Adventure / Brigadier API at all — its single dependency is `kotlinx-serialization-json`. Rendering was pushed out to the platform modules (`CommandResult` carries a message key, and `toBrigadierResult()` returns an `Int` without depending on Brigadier), so `engine` borrows nothing from a platform runtime. This lets `engine` be tested on a pure JVM without spinning up a Minecraft server, while platform concerns (the Folia scheduler, HTTP, Adventure components) stay isolated in the platform modules. ## Compatibility via the protocol version @@ -77,7 +73,7 @@ For details, see [platform-paper - Paper / Folia Plugin](/docs/developers/platfo 4. **Annotation-driven commands** — `@Command` / `@Permission` / `@PlayerOnly` are read via Kotlin reflection and mapped onto the Brigadier tree. A command's definition and its metadata (permission, aliases) are declared together in one place. 5. **Folia compatibility** — asynchronous work runs on `asyncScheduler` and `PluginCoroutineScope` (SupervisorJob), and Bukkit API calls are moved back to the main thread via `scheduler.runTask`. Thread boundaries are handled explicitly so it also works on region-threaded Folia. 6. **Persistence chosen per purpose** — languages / player settings = KAML (YAML), channels / conversion cache = kotlinx.serialization JSON, channel logs = NDJSON. All follow the same pattern: in-memory cache + asynchronous save (debounce/queue) + synchronous save on shutdown. -7. **DM/channel = local, global = via the proxy** — routing differs by chat type; only global chat goes through Velocity. The relay prevents loops in two stages: "exclude the source server" + "deduplicate by messageId". +7. **Channel = local, global and DM = optionally via the proxy** — routing differs by chat type. Channel chat is always server-local; global chat crosses the proxy when `crossServerGlobalChat` is on, and direct messages do when `crossServerDirectMessage` is on. The relay prevents loops in two stages: "exclude the source server" + "deduplicate by messageId". ## Module details diff --git a/website/src/docs/developers/engine.md b/website/src/docs/developers/engine.md index ee3a2c1..a65a3f6 100644 --- a/website/src/docs/developers/engine.md +++ b/website/src/docs/developers/engine.md @@ -48,13 +48,11 @@ The compatibility check is "**MAJOR matches exactly, the remote MINOR is within As a consequence of this design, Paper and Velocity can be released independently. See [Build, Release & Versioning](/docs/developers/resource#independent-versioning). -## converter — Romaji-to-Japanese conversion +## Romaji conversion is no longer here -`converter` is not a Paper↔Velocity contract (Velocity does no romaji conversion); it lives in `engine` **because it is platform-independent pure logic**. It has three layers. +Romaji conversion used to live in `engine` on the grounds that it was platform-independent pure logic. It now lives in [platform-paper](/docs/developers/platform-paper), which is its only caller. -- `KanaConverter` (`object`) — converts romaji to hiragana with a **Trie**. An immutable structure of `sealed class TrieNode { Leaf, Branch }` covers mappings from 4 characters (`xtsu`→っ) down to 1 (`a`→あ). `isValidRomaji()` validates before conversion; `toHiragana()` is a pure algorithm using longest-match plus sokuon handling -- `GoogleIMEClient` — receives a Ktor `HttpClient` via DI and converts hiragana to kanji-kana via Google IME (`langpair=ja-Hira|ja`), concatenating the top candidate of each segment of the response -- `CacheData` (`@Serializable`) — the persistence schema for conversion results (`version` plus `entries: Map`). It is a container for caching the expensive IME conversions; the caching logic itself lives on the paper side +Being platform-independent turned out not to be reason enough: keeping it here made `engine` depend on Ktor, and because both platforms depend on `engine`, the **Velocity** artifact shipped an HTTP client it never called. Moving it out is what let `engine` narrow its dependencies to `kotlinx-serialization-json` alone. ## chat/channel — Channel domain model @@ -79,14 +77,14 @@ There are two UUID serializers because they serve different purposes. `UUIDSeria ## exception — Shared error vocabulary -So that Paper and Velocity can handle domain errors as the same types, exceptions are centralized in `engine`. There is no common sealed base — it is a flat structure (23 types) that directly extends `Exception`. They fall into existence/reference, state, limit, and permission/BAN/KICK categories, and many take `playerId` / `channelId` / `limit` in the constructor and build their own messages. Because there is no base type, callers are expected to catch each individually. +So that Paper and Velocity can handle domain errors as the same types, exceptions are centralized in `engine`. There is no common sealed base — it is a flat structure that directly extends `Exception`. They fall into existence/reference, state, limit, and permission/BAN/KICK categories, and many take `playerId` / `channelId` / `limit` in the constructor and build their own messages. Because there is no base type, callers are expected to catch each individually. ## permission / command — Neutral abstractions Permissions and command results are placed in `engine` as neutral representations that can be passed to either the Bukkit or Velocity API. - `LunaticChatPermissionNode` — permissions enumerated type-safely as `sealed class` + `object` subclasses. The string node can be passed to either platform's permission API, and `when` also gives exhaustiveness checking -- `CommandResult` — a `sealed class` (`Success` / `SuccessWithMessage` / `Failure` / `InvalidUsage`). The message is an Adventure `Component`, and `toBrigadierResult()` expresses only "the meaning of the return value" (success=1/failure=0) without depending on Brigadier itself +- `CommandResult` — a `sealed class` (`Success` / `SuccessWithMessage` / `Failure` / `InvalidUsage`). Messages travel as plain `String`, so no Adventure type reaches `engine`; turning them into styled output is the platform's job. `toBrigadierResult()` expresses only "the meaning of the return value" (success=1/failure=0) without depending on Brigadier itself ## Related diff --git a/website/src/docs/developers/platform-paper.md b/website/src/docs/developers/platform-paper.md index 19437c1..dbba4f1 100644 --- a/website/src/docs/developers/platform-paper.md +++ b/website/src/docs/developers/platform-paper.md @@ -121,7 +121,7 @@ Branches: ### Direct messages (DirectMessageHandler) -Manages `/tell`・`/reply` state. Two `ConcurrentHashMap`s, `lastMessager` / `lastRecipient`, track reply targets, and `getReplyTarget()` returns an online player in the order "whoever messaged me → whoever I messaged". +Manages `/tell`・`/reply` state. Two `ConcurrentHashMap`s, `lastMessager` / `lastRecipient`, track reply targets as a `sealed interface ReplyTarget` of `Local` (a UUID) or `Remote` (a player name plus server name). `getReplyTarget()` resolves in the order "whoever messaged me → whoever I messaged", validating as it goes: a `Local` target must be online, and a `Remote` target must still be reported on that server by `RemotePlayerRegistry`. `sendDirectMessage()` applies romaji conversion per the sender's settings → delivers a hover-annotated copy to spy players (excluding sender and recipient) → sends the formatted message to sender and recipient plus a notification sound (settings-dependent). The message carries a `ClickEvent.suggestCommand` that fills in `/tell <sender>`. @@ -144,27 +144,27 @@ Channel state itself is managed by the `chat/channel` package. ## config -- `ConfigManager` — reads the main `config.yml` from **Bukkit's `FileConfiguration`** by dotted keys and hand-assembles `LunaticChatConfiguration` (note: this path is not KAML) +- `ConfigManager` — deserializes `config.yml` into `LunaticChatConfiguration` with **KAML**, so each default lives in exactly one place: on the data class. It replaced a hand-written dotted-key mapper that repeated every default a second time, and they had already drifted — `checkForUpdates` disagreed with both `config.yml` and the data class, and the whole `messageLogging` block was documented but never read +- Failure is handled per setting, not per file: on a `YamlException` the offending key is pruned from the document and decoding is retried, so one unreadable value costs only itself. Only a document that is not YAML at all falls back to defaults wholesale, and neither case is allowed to throw out of `onEnable` +- `LenientBoolean` — a `Boolean` typealias with a serializer that still accepts `yes` / `no` / `on` / `off`. Bukkit read `config.yml` as YAML 1.1, where those are booleans; kaml reads YAML 1.2, where they are plain strings, and silently resetting them would have flipped `checkForUpdates: no` to its opposite default - Feature defaults: `quickReplies=true`, `japaneseConversion=false`, `channelChat=false`, `velocityIntegration=false` - Under `config/key`: `FeaturesConfig` / `ChannelChatFeatureConfig` / `JapaneseConversionFeatureConfig` / `VelocityIntegrationConfig` / `QuickRepliesFeatureConfig` / `MessageFormatConfig` / `ChannelMessageLoggingConfig` -::: warning Implementation note -`ChannelChatFeatureConfig.messageLogging` is not loaded by `ConfigManager` and stays at its default values (enabled=true, retention=30, 100MB). Whether this is intentional needs confirmation — decide whether to fix it or document it as intended behavior. -::: - ## i18n - `Language` (enum) — `EN` / `JA`; unknown codes fall back to EN - `LanguageManager` — loads `resources/languages/` with KAML at startup and flattens the nested YAML into dotted keys (`toggle.on`, etc.). `getMessage(key, placeholders)` resolves with selected-language → EN fallback and substitutes `{placeholder}`, returning the key itself if not found. A missing EN is a fatal error - `MessageFormatter` (`object`) — produces an Adventure `Component` with a `[LC]` prefix and highlights `{braces}` placeholders detected by regex -## converter (paper side) — engine integration +## converter — Romaji-to-Japanese conversion -The paper side handles the platform concerns of "cache management, timeouts, Bukkit scheduling", and delegates the conversion algorithm and API calls to `engine`. +Romaji conversion lives here in full: the algorithm, the API client, the cache, and the platform concerns (timeouts, scheduling). It used to sit in `engine` as platform-independent pure logic, but `platform-paper` is its only caller, and keeping it in `engine` made the Velocity artifact carry Ktor for nothing. -- `RomanjiConverter` — the two-stage conversion orchestrator. Per word: cache lookup → engine `KanaConverter` for romaji→hiragana → engine `GoogleIMEClient` for hiragana→kanji. Falls back to hiragana on API failure -- `ConversionCache` — persists engine `CacheData` as JSON. In-memory cache plus debounced save (a FIXME notes that eviction on `maxEntries` overflow is effectively random due to `ConcurrentHashMap` ordering) -- `RomajiConversionHelper` — `convertWithRomaji()`. Calls synchronously via `runBlocking` + `withTimeoutOrNull` (default 1000ms), returning `"original §e(converted)"` on success and the original text on failure/timeout +- `KanaConverter` (`object`) — romaji to hiragana with a **Trie**. An immutable `sealed class TrieNode { Leaf, Branch }` covers mappings from 4 characters (`xtsu`→っ) down to 1 (`a`→あ). `isValidRomaji()` validates before conversion; `toHiragana()` is a pure longest-match algorithm with sokuon handling +- `GoogleIMEClient` — receives a Ktor `HttpClient` via DI and converts hiragana to kanji-kana via Google IME (`langpair=ja-Hira|ja`), concatenating the top candidate of each segment +- `RomanjiConverter` — the two-stage orchestrator. Per word: cache lookup → `KanaConverter` → `GoogleIMEClient`. Words are converted concurrently, and an API failure degrades to hiragana rather than failing the message +- `ConversionCache` — persists `CacheData` as JSON. In-memory cache plus debounced save (a FIXME notes that eviction on `maxEntries` overflow drops an arbitrary 10%, not the oldest, because `ConcurrentHashMap` is unordered) +- `RomajiConversionHelper` — `convertWithRomaji()` is `suspend` and bounded by `withTimeoutOrNull` (default 1000ms), returning `"original §e(converted)"` on success and the original text on failure or timeout. `convertWithRomajiBlocking()` wraps it in `runBlocking` for `AsyncChatEvent`, the one caller that must decide whether to cancel the event before returning; command handlers run on the tick thread and must use the suspending form ## Velocity integration (Paper side) @@ -177,7 +177,7 @@ Using the engine's protocol, it communicates with the proxy over Bukkit's Plugin ## settings / common - `PlayerSettingsManager` — manages three boolean settings in `ConcurrentHashMap`s. Uses the engine DTOs; unset values default to true -- `YamlPlayerSettingsStorage` — reads/writes `player-settings.yaml` with KAML. Recovers from a backup on load failure; debounced save (5s) +- `YamlPlayerSettingsStorage` — reads/writes `player-settings.yaml` with KAML; debounced save (5s). There is no backup file: a load failure is logged and falls back to **empty settings**, which means every player silently returns to defaults - `UpdateChecker` — hits the GitHub Releases API via Ktor and compares semver. The result is a sealed `UpdateCheckResult` - `SoundCollector` — Adventure `Sound` constants for notifications plus Player extension functions - `PermissionCollector` — a DSL that collects permissions via `@PermissionDsl` + the `+LunaticChatPermissionNode` operator. `requirePermission` throws the engine's `RequirePermissionException` diff --git a/website/src/docs/features/admin.md b/website/src/docs/features/admin.md index c3a3d2f..3f71dcc 100644 --- a/website/src/docs/features/admin.md +++ b/website/src/docs/features/admin.md @@ -24,9 +24,11 @@ Displayed information: ## Spy Mode -Players with the `lunaticchat.spy` permission (default: op) can view all direct messages sent and received on the server. +Players with the `lunaticchat.spy` permission (default: op) can view both the direct messages and the channel messages sent on the server. -- Spy players see the original message before romaji conversion +- Direct messages are delivered to spies, excluding the sender and the recipient +- Channel messages are delivered to spies, excluding the sender and the channel's own members +- For direct messages, spies see the original text before romaji conversion. Channel messages reach spies in the same converted form the members see - Hover text indicates the message is a spy message - Spy players themselves are not included in the normal sender/recipient list @@ -46,6 +48,15 @@ When `checkForUpdates` is `true` (default), the plugin checks for new versions a checkForUpdates: true ``` +## Nightly Builds + +Builds produced from the `main` branch outside of a release are marked as nightly, and the plugin says so rather than letting it go unnoticed. + +- Every player is warned on join that the build may be unstable, along with a pointer to GitHub Issues +- `/lc status` shows the same warning, and displays the release channel in yellow instead of green + +Nightly builds are not covered by the [security policy](https://github.com/m1sk9/LunaticChat/blob/main/.github/SECURITY.md); use a release build on a production server. + ## Debug Mode Setting `debug` to `true` enables verbose plugin logging. This is useful for troubleshooting issues or submitting bug reports. @@ -68,7 +79,7 @@ language: "ja" # "en" or "ja" | Permission | Default | Description | |-----------|---------|-------------| -| `lunaticchat.spy` | op | View all direct messages | +| `lunaticchat.spy` | op | View all direct and channel messages | | `lunaticchat.channelbypass` | op | Bypass channel restrictions | | `lunaticchat.noticeupdate` | op | Receive update notifications | | `lunaticchat.command.lcv.status` | op | Use the `/lcv status` command | diff --git a/website/src/docs/features/channel-chat.md b/website/src/docs/features/channel-chat.md index c06ea39..ff6bab0 100644 --- a/website/src/docs/features/channel-chat.md +++ b/website/src/docs/features/channel-chat.md @@ -37,6 +37,19 @@ Players can join multiple channels, but only one channel can be active at a time /lc channel status # Display the current active channel and list of joined channels ``` +## Sending to Global Chat (`!` Prefix) + +While a channel is active, your chat goes to that channel. Prefixing a message with `!` sends that one message to global chat instead, without leaving or switching the channel. + +``` +!Hello everyone # Goes to global chat even while a channel is active +``` + +The `!` and any space following it are stripped before the message is sent, so it never appears in the message itself. A message consisting of only `!` is discarded and nothing is sent. + +> [!NOTE] +> The prefix is handled by the chat listener, which is only registered when channel chat, cross-server global chat, or Japanese conversion is enabled. If all three are disabled, a leading `!` stays in the message exactly as typed. + ## Roles and Permissions Channels have three roles. @@ -87,6 +100,10 @@ See the `features.channelChat.messageLogging` section on the [Configuration page Players with the `lunaticchat.channelbypass` permission (default: op) are protected from kicks and bans, and can force-delete channels. +## Spy Mode + +Channel messages are not private from administrators. Players with the `lunaticchat.spy` permission (default: op) also receive channel messages, excluding the sender and the channel's own members. See [Spy Mode](/docs/features/admin#spy-mode) for details. + ## Message Format The display format for channel messages can be customized via `messageFormat.channelMessageFormat` in `config.yml`. See [Message Format](/docs/reference/message-format) for details. diff --git a/website/src/docs/features/direct-message.md b/website/src/docs/features/direct-message.md index 07f6cdb..ec0f24e 100644 --- a/website/src/docs/features/direct-message.md +++ b/website/src/docs/features/direct-message.md @@ -42,6 +42,17 @@ To message a player on another server, specify the player argument as `playerNam /tell <player>@<server> <message> ``` +`serverName` is the name of the destination server **as registered in your Velocity configuration** (`velocity.toml`), which is what the proxy resolves the target against. Tab completion offers the names and players it currently knows about. + +Set `features.velocityIntegration.serverName` on each backend to that same name. It does not affect routing, but it fills `{server}` in cross-server chat and is how a server recognises which players are its own — if it disagrees with the Velocity name, local players are treated as remote in tab completion. + +Delivery can fail in two ways, and the sender is told which: + +| Reason | Meaning | +|--------|---------| +| `SERVER_NOT_FOUND` | No server registered on the proxy has that name | +| `TARGET_OFFLINE` | The server exists, but that player is not on it — including when they are online on a different server | + ## Notification Settings Players can individually control the sound notification when receiving direct messages. diff --git a/website/src/docs/features/japanese-conversion.md b/website/src/docs/features/japanese-conversion.md index ec1df1b..d98ac87 100644 --- a/website/src/docs/features/japanese-conversion.md +++ b/website/src/docs/features/japanese-conversion.md @@ -19,8 +19,11 @@ Conversion is performed in two stages. Input: konnichiha sekai Stage 1: こんにちは せかい Stage 2: こんにちは 世界 +Sent: konnichiha sekai §e(こんにちは 世界) ``` +The original text is **not** replaced. What you typed is kept, and the conversion result is appended in parentheses, so both are visible to everyone who receives the message. + ## Conversion Targets - Normal chat @@ -48,7 +51,7 @@ Conversion results are cached per word. When the same word is converted again, t | `cache.saveIntervalSeconds` | `300` | Interval for saving to disk (seconds) | | `cache.filePath` | `"conversion_cache.json"` | Path to the cache file | -When the cache reaches its limit, the oldest 10% of entries are automatically removed. +When the cache reaches its limit, 10% of the entries are removed. Which entries are dropped is not defined: the in-memory cache is unordered, so eviction is effectively arbitrary rather than oldest-first. ## API Settings @@ -56,6 +59,11 @@ Settings related to the connection to the Google IME API. | Setting Key | Default | Description | |-------------|---------|-------------| -| `api.timeout` | `3000` | Request timeout (milliseconds) | +| `api.timeout` | `3000` | Timeout for a single API request (milliseconds) | If the API times out or fails, the message is sent in hiragana as-is. + +> [!WARNING] +> Independently of `api.timeout`, converting one message is given an overall budget of **1000 ms**. When that budget runs out the conversion is abandoned and the message is sent exactly as typed, with nothing appended. +> +> Because the overall budget is shorter than the default `api.timeout`, raising `api.timeout` above `1000` has no practical effect. diff --git a/website/src/docs/features/velocity.md b/website/src/docs/features/velocity.md index 8bc1b70..7842a24 100644 --- a/website/src/docs/features/velocity.md +++ b/website/src/docs/features/velocity.md @@ -51,6 +51,8 @@ When `crossServerGlobalChat` is set to `true`, player chat messages are relayed Each message is assigned a unique ID, and a cache prevents the same message from being displayed more than once. The cache size can be configured with `messageDeduplicationCacheSize` (default: `100`). +Entries expire 60 seconds after they are recorded. If the cache is still over its configured size after expired entries are cleared, the oldest remaining entries are dropped. + ## Cross-Server Direct Messages <Badge type="tip" text="v1.3.0~" /> Setting `crossServerDirectMessage` to `true` lets players exchange direct messages with players on other servers connected to the same proxy. @@ -80,7 +82,8 @@ The handshake timeout is 5 seconds. If the handshake times out, the state become |-------------|---------|-------------| | `enabled` | `false` | Enable Velocity integration | | `crossServerGlobalChat` | `false` | Enable cross-server global chat | -| `serverName` | `"Unknown"` | Server name displayed in cross-server chat | +| `crossServerDirectMessage` | `false` | Enable cross-server direct messages | +| `serverName` | `"Unknown"` | This server's own name. Fills `{server}` in cross-server chat and is how the server recognises its own players. Set it to the name registered in `velocity.toml` | | `messageDeduplicationCacheSize` | `100` | Size of the message deduplication cache | ## Message Format diff --git a/website/src/docs/permissions.md b/website/src/docs/permissions.md index d059bba..caada8b 100644 --- a/website/src/docs/permissions.md +++ b/website/src/docs/permissions.md @@ -44,7 +44,7 @@ The following permissions are granted to OPs only by default. | Permission | Default | Description | |------------|---------|-------------| -| `lunaticchat.spy` | op | View all direct messages on the server | +| `lunaticchat.spy` | op | View all direct and channel messages on the server | | `lunaticchat.noticeupdate` | op | Receive update notifications | | `lunaticchat.channelbypass` | op | Bypass channel restrictions (kick/ban protection, force deletion) | | `lunaticchat.command.lcv.status` | op | Use the `/lcv status` command | diff --git a/website/src/docs/reference/commands.md b/website/src/docs/reference/commands.md index a039f7a..28627bc 100644 --- a/website/src/docs/reference/commands.md +++ b/website/src/docs/reference/commands.md @@ -58,7 +58,8 @@ Creates a new channel. The creator becomes the owner. - **Aliases**: `new` - **Permission**: `lunaticchat.command.lc.channel.create` -- `channelId`: Only alphanumeric characters, underscores, and hyphens are allowed +- `channelId`: 3-30 characters; only alphanumeric characters, underscores, and hyphens are allowed +- `name`: Cannot be blank - `isPrivate`: `true` / `false` (default: `false`) #### `/lc channel list [page]` diff --git a/website/src/docs/reference/compatibility.md b/website/src/docs/reference/compatibility.md index e0e341c..23274ab 100644 --- a/website/src/docs/reference/compatibility.md +++ b/website/src/docs/reference/compatibility.md @@ -57,9 +57,11 @@ The rules (from Velocity's perspective) are: Compatibility is checked at connection time: -1. The Paper server sends a handshake to Velocity at startup +1. The Paper server sends a handshake to Velocity one second after the **first player joins** 2. Velocity validates Paper's protocol version against its own 3. On mismatch, Velocity rejects the connection and Paper's state becomes `FAILED` 4. The handshake timeout is 5 seconds +The handshake is sent once per server start, and it is triggered by a player joining rather than by startup itself — the plugin messaging channel needs a player connection to send on. Until the first player joins, `/lcv status` reports `DISCONNECTED`, which is normal and not a sign of a problem. + Live connection state is available via `/lcv status`. See [Velocity Integration](/docs/features/velocity#connection-states) for details. diff --git a/website/src/ja/changelog/overview.md b/website/src/ja/changelog/overview.md new file mode 100644 index 0000000..3b03477 --- /dev/null +++ b/website/src/ja/changelog/overview.md @@ -0,0 +1,55 @@ +--- +layout: doc +--- + +# 更新履歴 + +LunaticChat に関する更新履歴です. + +## バージョニング + +::: tip + +LunaticChat における独立バージョニングの詳細は [ビルド・リリース・バージョニング](../docs/developers/resource.md) を参照してください. + +::: + +LunaticChat ではセマンティックバージョニングを採用しています.バージョン番号は `MAJOR.MINOR.PATCH` の 3 つの数字で構成され,それぞれ次の基準で上がります. + +| 位置 | 上がる条件 | 例 | +|------|-----------|-----| +| **MAJOR** | プレリリース (v0 系) から最初の安定版へ移行する際に使用しました.安定版に入って以降は使用していません | `v0.11.0` → `v1.0.0` | +| **MINOR** | 機能の追加・変更,および**サポート対象プラットフォームの打ち切り** | `v1.2.2` → `v1.3.0` (サーバー間 DM の追加,Paper 26.1 の打ち切り) | +| **PATCH** | 不具合修正と依存関係の更新のみ.機能の変更を含みません | `v1.2.0` → `v1.2.1` (依存更新のみ) | + +::: warning サポート打ち切りは MINOR で行います + +「Paper 26.1 のサポートを終了」「Velocity 3.5.x のサポートを終了」のような**動作環境の切り捨ては MINOR バンプで行います**.MAJOR は上がらないため,マイナーバージョンの更新であっても動作しなくなる組み合わせがありえます.更新の前に対象バージョンの更新履歴を確認してください. + +::: + +### Paper 版と Velocity 版は別のバージョン番号を持ちます + +Paper / Folia 向けプラグインと Velocity 向けプラグインは独立してリリースされるため,バージョン番号も別々に進みます.番号が揃っていないのは正常な状態です. + +リリースは対象を示すタグで行います. + +| タグ | リリース対象 | +|------|-------------| +| `paper/vX.Y.Z` | Paper / Folia 版のみ | +| `velocity/vX.Y.Z` | Velocity 版のみ | +| `vX.Y.Z` | 両方同時.共有モジュール `engine` の変更など,双方に影響する変更で使用します | + +### プラグインバージョンは互換性を表しません + +Paper 版と Velocity 版が通信できるかどうかは,プラグインバージョンではなく両者に埋め込まれた**プロトコルバージョン**で決まります.バージョン番号が離れていても組み合わせられる場合があり,逆に近い番号同士でも組み合わせられない場合があります. + +詳しくは [Paper / Velocity 互換性](/ja/docs/reference/compatibility) を参照してください. + +### サポート対象 + +サポート対象は**各プラットフォームの最新リリースのみ**です.古いバージョンへの修正の backport は,どうしても必要な場合を除いて行いません. + +nightly ビルドは開発中のコミットから自動生成されるビルドであり,正式なリリースではないためサポート対象外です.nightly を使用している場合は,ログイン時と `/lc status` の実行時に警告が表示されます. + + diff --git a/website/src/ja/changelog/paper/v1.3.0.md b/website/src/ja/changelog/paper/v1.3.0.md new file mode 100644 index 0000000..78e241a --- /dev/null +++ b/website/src/ja/changelog/paper/v1.3.0.md @@ -0,0 +1,60 @@ +--- +layout: doc +--- + +# v1.3.0 + +- Download: + - [GitHub](https://github.com/m1sk9/LunaticChat/releases/tag/paper/v1.3.0) + - [Modrinth](https://modrinth.com/plugin/lunaticchat/version/1.3.0) + +## New Features + +- Paper 26.2 (Minecraft 26.2) をサポートしました + - 同時にPaper 26.1 のサポートは終了しました. + - Paper 26.2 では Adventure 5.2.0 を同梱しているため,26.1 とはバイナリ互換性が失われています. + - そのため, v1.3.0 以降のビルドは Paper 26.1 では使用できなくなりました. +- `/tell <player>@<server>` で Velocity 間で接続しているサーバのプレイヤーにメッセージを送信できるように + - デフォルトでは無効化されています. + - `features.velocityIntegration.crossServerDirectMessage` で有効化できます. +- プロトコルバージョンを `1.0.1` に引き上げました. + - Paper 側と Velocity 側はどちらを先に更新しても問題ありません. + - ただし,サーバー間ダイレクトメッセージ機能を使うには両側の更新が必要です. + +## Improvements + +- プレイヤーデータの保存に失敗しても,残りのシャットダウン処理が中断されなくなり,チャンネルログと Velocity 接続は必ずクローズされるようになりました. +- ファイルの永続化処理が単一のストレージレイヤーの背後に統合されました. + - これにより,クラッシュや同時保存によってファイルが中途半端な状態のまま残ることがなくなりました. +- 各プレイヤーのメッセージは送信順に配信されるようになりました. + - 切断時にまだキューに残っている作業は,既にログアウトしたプレイヤーに配信されるのではなく破棄されます. +- ダイレクトメッセージ配信とチャンネルメッセージのロギングが,tick スレッド上で実行されなくなりました. +- キャッシュ済みのローマ字変換結果が,共有 API 同時実行数リミッターの許可待ちをしなくなりました. + - これまでは,全ての単語がキャッシュ済みであるメッセージでも,不要な permit の取得待ちで変換予算を使い切ってしまい,未変換のまま送信されることがありました. +- メッセージ中の単語の変換が並行して行われるようになりました. +- プレイヤー設定は,変更があった場合のみ書き直されるようになりました. + - これまでは,プレイヤーが退出するたびに,同一バイト列であっても保存済みの全プレイヤーが再シリアライズされていました. +- プレイヤーがアクティブなチャンネルを持たない状態で退出した際,そのプレイヤーのアクティブチャンネルをクリアする処理でチャンネルキャッシュのスナップショットが取られなくなりました. + - これまでは,大量切断時に 1 tick 内で切断人数分のフルスナップショットが発生していました. +- ダイレクトメッセージおよびチャンネルメッセージのスパイ通知は,対象者がオンラインでない場合,翻訳ルックアップとメンバーセットの構築を行わなくなりました. +- チャンネルデータの書き込みは,変更のたびにファイル全体を書き直すのではなく,まとめて行われるようになりました. +- 内部的なクリーンアップを行いました. + +## Changes + +- 設定ファイルの読み込みに KAML を使用するようになりました.これにより壊れたファイルの扱いが変わります. + - 読み込み不能な設定ファイルがあっても,プラグインは無効化されず,デフォルト設定で起動するようになりました. + - 単一の値が読み込めない場合も,ファイル内の他の設定が全て破棄されることはなくなり,警告付きでその値のみデフォルトにフォールバックするようになりました. +- プレイヤーごとの配信キューに上限が設けられました. + - 配信のドレイン速度を超えて送信するプレイヤーは,無制限にバックログを溜め込んで数分後にまとめて届くのではなく,警告と共に拒否されるようになりました. + +## Bug Fixes + +- `/tell` のプレイヤー引数が部分一致になっていた不具合を修正 + - 旧 Bukkit API からの切り替えによるものです. +- `features.channelChat.messageLogging` が実際には読み込まれず,デフォルト値としてフォールバックされる不具合を修正 + - これまではドキュメントに記載があるにもかかわらず読み込み処理が実装されておらず,ファイルの記述に関わらず常にデフォルト値のままでした. +- Google IME の応答が遅い場合に,そのプレイヤーのセッション中のメッセージ配信が以降すべて無言で止まってしまう不具合を修正 +- 一度の変換タイムアウトが原因で,ある単語が恒久的にひらがな表示に固定されてしまう不具合を修正 +- `/reply` が記録されたばかりの返信先を認識できない不具合を修正 +- `/reply` の対象者がログアウトした際,クリアされたはずのエントリが重複してチャットに挿入されてしまう不具合を修正 diff --git a/website/src/ja/changelog/velocity/v1.2.0.md b/website/src/ja/changelog/velocity/v1.2.0.md new file mode 100644 index 0000000..18249c9 --- /dev/null +++ b/website/src/ja/changelog/velocity/v1.2.0.md @@ -0,0 +1,23 @@ +--- +layout: doc +--- + +# v1.2.0 + +- Download: + - [GitHub](https://github.com/m1sk9/LunaticChat/releases/tag/velocity/v1.2.0) + - [Modrinth (Velocity)](https://modrinth.com/plugin/lunaticchat/version/1.2.0-velocity) + +## New Features + +- Velocity 4.0.0 をサポートしました + - Velocity 3.5.x 系統のサポートは終了しました. +- サーバー間ダイレクトメッセージのリレー機能とプレイヤーのプレゼンス追跡機能を追加しました. +- プロトコルバージョンを `1.0.1` に引き上げました. + - Paper 側と Velocity 側はどちらを先に更新しても問題ありません. + - ただし,サーバー間ダイレクトメッセージ機能を使うには両側の更新が必要です. + +## Improvements + +- Velocity 用 JAR のサイズが,約 8.2 MiB から約 2.6 MiB に縮小されました. + - Paper 専用の依存関係(Ktor とローマ字変換モジュール)が共有モジュールから分離され,Velocity ビルドに同梱されなくなったためです. diff --git a/website/src/ja/docs/configuration.md b/website/src/ja/docs/configuration.md index c2c8c12..3324940 100644 --- a/website/src/ja/docs/configuration.md +++ b/website/src/ja/docs/configuration.md @@ -6,6 +6,22 @@ layout: doc LunaticChat の設定は `plugins/LunaticChat/config.yml` で管理されます.サーバーの初回起動時にデフォルトの設定ファイルが生成されます. +## 設定の反映 + +リロードコマンドはありません.`config.yml` を編集したら**サーバーを再起動**してください. + +真偽値の設定は `true` / `false` のほか,Bukkit が従来受け付けていた `yes` / `no` / `on` / `off` の表記も使用できます.古いリリース向けに書かれたファイルもそのまま動作します. + +## 不正なファイルからの復帰 <Badge type="tip" text="v1.3.0~" /> + +`config.yml` が使用できない状態であっても,プラグインの起動が止まることはありません. + +- **1つの値**が読めない場合,その設定だけがデフォルトにフォールバックし,該当キーを示す警告がログに出力されます.ファイル内のほかの設定はそのまま反映されます +- ファイルが **YAML として不正**な場合やディスクから読み取れない場合は,すべての設定がデフォルトにフォールバックし,エラーがログに出力されます +- コメントのみのファイルは「デフォルトを使う」という有効な指定として扱われ,問題として報告されません + +`config.yml` を編集したあとはサーバーログを確認してください.デフォルトに戻された設定があれば,そこに報告されています. + ## グローバル設定 | キー | 型 | デフォルト | 説明 | @@ -68,6 +84,20 @@ LunaticChat の設定は `plugins/LunaticChat/config.yml` で管理されます | `channelMessageFormat` | `§7[§b#{channel}§7] §e{sender}: §f{message}` | `{sender}`, `{message}`, `{channel}` | | `crossServerGlobalChatFormat` | `§7[§6{server}§7] §e{sender}: §f{message}` | `{sender}`, `{message}`, `{server}` | +## データファイル + +プラグインが書き込むファイルはすべて `plugins/LunaticChat/` 配下に置かれます. + +| ファイル | 書き込まれるタイミング | 備考 | +|----------|----------------------|------| +| `config.yml` | 初回起動時に生成 | プラグインが書き換えることはない | +| `player-settings.yaml` | プレイヤーが `/lc settings` で設定を変更したとき | パスは `userSettingsFilePath` で変更可能.起動時に読み取れなかった場合,**全プレイヤーの設定がデフォルトに戻る** | +| `channels.json` | チャンネルまたはメンバーシップが変化したとき | チャンネルチャットが有効な場合のみ | +| `conversion_cache.json` | `cache.saveIntervalSeconds` ごとに定期保存 | ローマ字変換が有効な場合のみ.パスは `cache.filePath` で変更可能 | +| `logs/channelchat/` | チャンネルメッセージごと | メッセージログが有効な場合のみ.[メッセージログ](/ja/docs/features/message-logging)を参照 | + +保存は変更ごとではなくまとめて行われ,またすべてのファイルはアトミックに書き込まれるため,書き込み途中のファイルが読まれることはありません.いずれもサーバー停止時にも書き出されます. + ## デフォルト設定ファイル [GitHub で確認する](https://github.com/m1sk9/LunaticChat/blob/main/platform-paper/src/main/resources/config.yml) diff --git a/website/src/ja/docs/developers/architecture.md b/website/src/ja/docs/developers/architecture.md index cd3965a..5e3c513 100644 --- a/website/src/ja/docs/developers/architecture.md +++ b/website/src/ja/docs/developers/architecture.md @@ -36,13 +36,11 @@ Paper と Velocity が同一定義でないと壊れるもの. #### (b) プラットフォーム非依存の純ロジック -どこに置いてもよいが,純粋な再利用可能のロジックを中立コアに寄せたもの. - -- `converter` — ローマ字変換の純アルゴリズム (Trie) +外部 API クライアント +engine の中身はすべて (a) に該当します.「どこに置いてもよい純ロジック」であることは,それだけでは engine に置く理由になりません.ローマ字変換はかつて「プラットフォーム非依存の純ロジック」としてここにありましたが,唯一の呼び出し元である `platform-paper` へ移したことで engine は Ktor 依存を落とせました.Velocity 側のビルドはそれまで,呼ばない HTTP クライアントの分だけ JAR サイズを払っていたためです. (a) を engine に一元化する最大の狙いは,**ワイヤ契約の「単一の真実源」を作ること**です.Paper と Velocity は別々にビルド・デプロイ・バージョニングされる 2 つの成果物であり,protocol を両モジュールに複製すれば必ず drift します.engine に 1 つだけ置けば,契約の不一致が「本番での実行時ミスマッチ」ではなく「コンパイルエラー / スナップショットテスト失敗」として早期に顕在化します. -engine は Bukkit / Velocity API に依存せず,Adventure / Brigadier も「型・値の意味」だけを借りて本体依存を避けています (`compileOnly` の Adventure,Brigadier に依存せず `Int` を返す `toBrigadierResult()`).これにより engine は Minecraft サーバーを立てずに pure-JVM でテストでき,プラットフォーム都合 (Folia のスケジューラ等) は platform 側に隔離されます. +engine は Bukkit / Velocity / Adventure / Brigadier のいずれの API にも依存せず,唯一の依存は `kotlinx-serialization-json` です.描画は platform 側へ押し出されており (`CommandResult` はメッセージを文字列で運び,`toBrigadierResult()` は Brigadier に依存せず `Int` を返す),プラットフォームのランタイムから何も借りていません.これにより engine は Minecraft サーバーを立てずに pure-JVM でテストでき,プラットフォーム都合 (Folia のスケジューラ,HTTP,Adventure コンポーネント等) は platform 側に隔離されます. ## プロトコルバージョンによる互換管理 @@ -77,7 +75,7 @@ Paper–Velocity 間の互換性は,プラグインのバージョンではな 4. **アノテーション駆動コマンド** — `@Command` / `@Permission` / `@PlayerOnly` を Kotlin リフレクションで読み Brigadier ツリーへマッピングします.コマンドの定義とメタデータ (権限・エイリアス) が同じ場所に宣言的に並びます. 5. **Folia 互換性** — 非同期処理は `asyncScheduler` と `PluginCoroutineScope` (SupervisorJob) で行い,Bukkit API 呼び出しは `scheduler.runTask` でメインスレッドへ戻します.リージョンスレッド化された Folia でも壊れないよう,スレッド境界を明示的に扱います. 6. **永続化の使い分け** — 言語/プレイヤー設定=KAML(YAML),チャンネル/変換キャッシュ=kotlinx.serialization JSON,チャンネルログ=NDJSON.いずれも「メモリキャッシュ+非同期保存 (デバウンス/キュー)+shutdown 同期保存」の共通パターンに従います. -7. **DM/チャンネル=ローカル,グローバル=プロキシ経由** — チャットの種類でルーティングが分かれ,グローバルチャットだけが Velocity を経由します.中継は「送信元サーバー除外」+「messageId による重複排除」の二段でループを防ぎます. +7. **チャンネル=ローカル,グローバルと DM=任意でプロキシ経由** — チャットの種類でルーティングが分かれます.チャンネルチャットは常にサーバーローカルで,グローバルチャットは `crossServerGlobalChat` が有効なとき,ダイレクトメッセージは `crossServerDirectMessage` が有効なときにプロキシを経由します.中継は「送信元サーバー除外」+「messageId による重複排除」の二段でループを防ぎます. ## 各モジュールの詳細についてはこちら diff --git a/website/src/ja/docs/developers/engine.md b/website/src/ja/docs/developers/engine.md index fb9ee9e..f798335 100644 --- a/website/src/ja/docs/developers/engine.md +++ b/website/src/ja/docs/developers/engine.md @@ -48,13 +48,11 @@ Paper–Velocity の互換性は,プラグインバージョンではなく `P この設計の帰結として Paper と Velocity を独立にリリースできます.詳しくは [ビルド・リリース・バージョニング](/ja/docs/developers/resource#独立バージョニング) を参照してください. -## converter — ローマ字→日本語変換 +## ローマ字変換はここにはない -converter は Paper↔Velocity の契約ではなく (Velocity はローマ字変換をしない),**プラットフォームに依存しない純ロジックだから** engine に置かれています.3 段構成です. +ローマ字変換は「プラットフォームに依存しない純ロジックだから」という理由で engine に置かれていましたが,現在は唯一の呼び出し元である [platform-paper](/ja/docs/developers/platform-paper) にあります. -- `KanaConverter` (`object`) — **Trie** でローマ字→ひらがなに変換.`sealed class TrieNode { Leaf, Branch }` の不変構造で,4 文字 (`xtsu`→っ) 〜1 文字 (`a`→あ) を網羅.`isValidRomaji()` で変換前検証,`toHiragana()` は最長一致+促音処理を行う純アルゴリズム -- `GoogleIMEClient` — Ktor `HttpClient` を DI で受け取り,Google IME (`langpair=ja-Hira|ja`) でひらがな→漢字仮名交じりに変換.レスポンスの各セグメント第 1 候補を連結する -- `CacheData` (`@Serializable`) — 変換結果 (`version` + `entries: Map`) の永続化スキーマ.コストの高い IME 変換をキャッシュするための器で,キャッシュ本体のロジックは paper 側にある +プラットフォーム非依存であることは,engine に置く理由としては不十分でした.ここに置くと engine が Ktor に依存し,両プラットフォームが engine に依存する構図上,**Velocity** の成果物が呼ばれることのない HTTP クライアントを同梱してしまうためです.これを外したことで engine の依存を `kotlinx-serialization-json` だけに絞れました. ## chat/channel — チャンネルのドメインモデル @@ -81,14 +79,14 @@ UUID シリアライザが 2 つあるのは用途が違うためです. ## exception — 共通の例外語彙 -ドメインエラーを Paper / Velocity 双方で同じ型として扱えるよう,例外を engine に集約しています.共通の封印基底は持たず,`Exception` を直接継承するフラット構造 (23 種) です.存在/参照系・状態系・制限系・権限/BAN・KICK 系に分類でき,多くが `playerId` / `channelId` / `limit` をコンストラクタで受けてメッセージを自前生成します.基底を持たないため,呼び出し側は個別に catch する前提です. +ドメインエラーを Paper / Velocity 双方で同じ型として扱えるよう,例外を engine に集約しています.共通の封印基底は持たず,`Exception` を直接継承するフラット構造です.存在/参照系・状態系・制限系・権限/BAN・KICK 系に分類でき,多くが `playerId` / `channelId` / `limit` をコンストラクタで受けてメッセージを自前生成します.基底を持たないため,呼び出し側は個別に catch する前提です. ## permission / command — 中立抽象 Bukkit / Velocity どちらの API にも渡せる中立表現として,権限とコマンド結果を engine に置いています. - `LunaticChatPermissionNode` — `sealed class` + `object` サブクラスで権限を型安全に列挙.文字列ノードは両プラットフォームの permission API に渡せ,`when` で網羅性チェックも効く -- `CommandResult` — `sealed class` (`Success` / `SuccessWithMessage` / `Failure` / `InvalidUsage`).メッセージは Adventure `Component`,`toBrigadierResult()` は Brigadier 本体に依存せず成功=1/失敗=0 という「戻り値の意味」だけを表現する +- `CommandResult` — `sealed class` (`Success` / `SuccessWithMessage` / `Failure` / `InvalidUsage`).メッセージは平文の `String` として運ばれ,engine に Adventure の型は入らない (装飾は platform 側の責務).`toBrigadierResult()` は Brigadier 本体に依存せず成功=1/失敗=0 という「戻り値の意味」だけを表現する ## 関連 diff --git a/website/src/ja/docs/developers/platform-paper.md b/website/src/ja/docs/developers/platform-paper.md index bbbeb0c..26a9ad7 100644 --- a/website/src/ja/docs/developers/platform-paper.md +++ b/website/src/ja/docs/developers/platform-paper.md @@ -121,7 +121,7 @@ config フラグ ### ダイレクトメッセージ (DirectMessageHandler) -`/tell`・`/reply` の状態を管理します.`lastMessager` / `lastRecipient` の 2 つの `ConcurrentHashMap` で返信先を追跡し,`getReplyTarget()` は「自分に送ってきた人 → 自分が送った人」の優先順でオンラインのプレイヤーを返します. +`/tell`・`/reply` の状態を管理します.`lastMessager` / `lastRecipient` の 2 つの `ConcurrentHashMap` が返信先を `sealed interface ReplyTarget` (`Local` = UUID / `Remote` = プレイヤー名+サーバー名) として追跡し,`getReplyTarget()` は「自分に送ってきた人 → 自分が送った人」の優先順で解決しながら検証します.`Local` はオンラインであること,`Remote` は `RemotePlayerRegistry` がそのサーバーに在席を報告していることが条件です. `sendDirectMessage()` は,送信者設定に応じたローマ字変換 → spy プレイヤーへの hover 付き配信 (送受信者は除外) → 送受信者への整形メッセージ送信+通知音 (設定依存) を行います.メッセージには `/tell <sender>` を補完する `ClickEvent.suggestCommand` が付きます. @@ -144,27 +144,27 @@ config フラグ ## config -- `ConfigManager` — メイン `config.yml` を **Bukkit の `FileConfiguration`** からドット記法で読み,`LunaticChatConfiguration` を手組みする (この経路は KAML ではない点に注意) +- `ConfigManager` — `config.yml` を **KAML** で `LunaticChatConfiguration` にデシリアライズする.デフォルト値の定義箇所をデータクラス 1 箇所に限定するためで,以前のドット記法の手組みマッパーは同じデフォルトを二重に持っており,実際に乖離していた (`checkForUpdates` が `config.yml` とデータクラスの双方と食い違い,`messageLogging` ブロックはドキュメント化されていながら一度も読まれていなかった) +- 失敗はファイル単位ではなく設定単位で処理する.`YamlException` が出た場合は該当キーをドキュメントから取り除いてデコードを再試行するため,読めない値 1 つの影響はその値だけに留まる.全体をデフォルトに落とすのは「YAML として成立していない」場合だけで,いずれのケースも `onEnable` の外へ例外を投げない +- `LenientBoolean` — `yes` / `no` / `on` / `off` も受け付けるシリアライザ付きの `Boolean` typealias.Bukkit は `config.yml` を YAML 1.1 として読んでいたためこれらは真偽値だったが,kaml が読む YAML 1.2 では単なる文字列であり,黙ってリセットすると `checkForUpdates: no` がデフォルトの逆の値に反転してしまう - 機能デフォルト: `quickReplies=true`, `japaneseConversion=false`, `channelChat=false`, `velocityIntegration=false` - `config/key` 以下に `FeaturesConfig` / `ChannelChatFeatureConfig` / `JapaneseConversionFeatureConfig` / `VelocityIntegrationConfig` / `QuickRepliesFeatureConfig` / `MessageFormatConfig` / `ChannelMessageLoggingConfig` -::: warning 実装ノート -`ChannelChatFeatureConfig.messageLogging` は `ConfigManager` でロードされず,デフォルト値 (enabled=true, retention=30, 100MB) 固定になっています.意図的な仕様か要確認 — 修正するか,仕様として明記するかを決める必要があります. -::: - ## i18n - `Language` (enum) — `EN` / `JA`.未知コードは EN にフォールバック - `LanguageManager` — 起動時に `resources/languages/` を KAML でロードし,ネストした YAML をドット記法 (`toggle.on` 等) にフラット化する.`getMessage(key, placeholders)` は 選択言語 → EN フォールバック で解決し `{placeholder}` を置換,未発見はキー自身を返す.EN が無ければ致命エラー - `MessageFormatter` (`object`) — `[LC]` プレフィックス付きの Adventure `Component` を生成し,`{braces}` プレースホルダを正規表現で検出して色分けする -## converter (paper 側) — engine 連携 +## converter — ローマ字→日本語変換 -paper 側は「キャッシュ管理・タイムアウト・Bukkit スケジューリング」というプラットフォーム都合を担い,変換アルゴリズムと API 通信は engine に委譲します. +ローマ字変換はアルゴリズム・API クライアント・キャッシュ・プラットフォーム都合 (タイムアウト,スケジューリング) のすべてがここにあります.かつては「プラットフォーム非依存の純ロジック」として engine にありましたが,呼び出し元は `platform-paper` だけであり,engine に置いたままでは Velocity の成果物が使わない Ktor を同梱することになるため移されました. -- `RomanjiConverter` — 2 段変換のオーケストレータ.単語ごとに キャッシュ確認 → engine `KanaConverter` でローマ字→ひらがな → engine `GoogleIMEClient` でひらがな→漢字.API 失敗時はひらがなにフォールバック -- `ConversionCache` — engine `CacheData` を JSON 永続化.メモリキャッシュ+デバウンス保存 (`maxEntries` 超過時の退避は ConcurrentHashMap の順不同により実質ランダム,との FIXME あり) -- `RomajiConversionHelper` — `convertWithRomaji()`.`runBlocking` + `withTimeoutOrNull` (既定 1000ms) で同期呼び出しし,成功時 `"元文 §e(変換)"`,失敗/タイムアウト時は原文を返す +- `KanaConverter` (`object`) — **Trie** でローマ字→ひらがなに変換.`sealed class TrieNode { Leaf, Branch }` の不変構造で,4 文字 (`xtsu`→っ) 〜1 文字 (`a`→あ) を網羅.`isValidRomaji()` で変換前検証,`toHiragana()` は最長一致+促音処理を行う純アルゴリズム +- `GoogleIMEClient` — Ktor `HttpClient` を DI で受け取り,Google IME (`langpair=ja-Hira|ja`) でひらがな→漢字仮名交じりに変換.レスポンスの各セグメント第 1 候補を連結する +- `RomanjiConverter` — 2 段変換のオーケストレータ.単語ごとに キャッシュ確認 → `KanaConverter` → `GoogleIMEClient`.単語は並行して変換され,API 失敗時はメッセージ全体を失敗させずひらがなにフォールバックする +- `ConversionCache` — `CacheData` を JSON 永続化.メモリキャッシュ+デバウンス保存 (`maxEntries` 超過時に削除されるのは古い順ではなく任意の 10%,ConcurrentHashMap が順不同であるため,との FIXME あり) +- `RomajiConversionHelper` — `convertWithRomaji()` は `suspend` 関数で `withTimeoutOrNull` (既定 1000ms) により上限が設けられ,成功時 `"元文 §e(変換)"`,失敗/タイムアウト時は原文を返す.`convertWithRomajiBlocking()` はこれを `runBlocking` で包んだもので,イベントをキャンセルするか否かを return 前に決めなければならない `AsyncChatEvent` だけが使う.コマンドハンドラは tick スレッドで動くため suspend 版を使う必要がある ## velocity 連携 (Paper 側視点) @@ -177,7 +177,7 @@ engine の protocol を使い,Bukkit の Plugin Messaging Channel (`lunaticcha ## settings / common - `PlayerSettingsManager` — 3 種のブール設定を `ConcurrentHashMap` で管理.engine の DTO を使い,未設定はデフォルト true -- `YamlPlayerSettingsStorage` — KAML で `player-settings.yaml` を read/write.読み込み失敗時はバックアップから復旧,5 秒デバウンス保存 +- `YamlPlayerSettingsStorage` — KAML で `player-settings.yaml` を read/write.5 秒デバウンス保存.バックアップファイルは存在せず,読み込み失敗時はログを出して**空の設定**にフォールバックするため,全プレイヤーの設定が黙ってデフォルトに戻る - `UpdateChecker` — GitHub Releases API を Ktor で叩き semver 比較.結果は sealed `UpdateCheckResult` - `SoundCollector` — 通知音の Adventure `Sound` 定数と Player 拡張関数 - `PermissionCollector` — `@PermissionDsl` + `+LunaticChatPermissionNode` 演算子で権限を集める DSL.`requirePermission` は engine の `RequirePermissionException` を投げる diff --git a/website/src/ja/docs/features/admin.md b/website/src/ja/docs/features/admin.md index 57d38c0..a29be09 100644 --- a/website/src/ja/docs/features/admin.md +++ b/website/src/ja/docs/features/admin.md @@ -24,9 +24,11 @@ layout: doc ## スパイモード -`lunaticchat.spy` パーミッション (デフォルト: op) を持つプレイヤーは,サーバー上で送受信されるすべてのダイレクトメッセージを閲覧できます. +`lunaticchat.spy` パーミッション (デフォルト: op) を持つプレイヤーは,サーバー上で送信されるダイレクトメッセージとチャンネルメッセージの両方を閲覧できます. -- スパイプレイヤーにはローマ字変換前の元のメッセージが表示されます +- ダイレクトメッセージは,送信者と受信者を除いてスパイプレイヤーに配信されます +- チャンネルメッセージは,送信者とそのチャンネルのメンバーを除いてスパイプレイヤーに配信されます +- ダイレクトメッセージについては,ローマ字変換前の元のテキストがスパイプレイヤーに表示されます.チャンネルメッセージはメンバーと同じ変換後の形で届きます - ホバーテキストでスパイメッセージであることが示されます - スパイプレイヤー自身は通常の送受信者リストには含まれません @@ -46,6 +48,15 @@ layout: doc checkForUpdates: true ``` +## Nightly ビルド + +リリース以外で `main` ブランチから作られたビルドは nightly として扱われ,プラグインがその旨を明示します. + +- すべてのプレイヤーに対して,ログイン時に不安定な可能性があることと GitHub Issues への案内が警告として表示されます +- `/lc status` にも同じ警告が表示され,リリースチャンネルが緑ではなく黄色で表示されます + +nightly ビルドは[セキュリティポリシー](https://github.com/m1sk9/LunaticChat/blob/main/.github/SECURITY.md)のサポート対象外です.本番サーバーではリリースビルドを使用してください. + ## デバッグモード `debug` を `true` にすると,プラグインの詳細なログが出力されます.問題の調査やバグ報告時に有用です. @@ -68,7 +79,7 @@ language: "ja" # "en" または "ja" | パーミッション | デフォルト | 説明 | |---------------|-----------|------| -| `lunaticchat.spy` | op | 全ダイレクトメッセージの閲覧 | +| `lunaticchat.spy` | op | 全ダイレクトメッセージ・チャンネルメッセージの閲覧 | | `lunaticchat.channelbypass` | op | チャンネル制限のバイパス | | `lunaticchat.noticeupdate` | op | アップデート通知の受信 | | `lunaticchat.command.lcv.status` | op | `/lcv status` コマンドの使用 | diff --git a/website/src/ja/docs/features/channel-chat.md b/website/src/ja/docs/features/channel-chat.md index dce3c85..b9723ee 100644 --- a/website/src/ja/docs/features/channel-chat.md +++ b/website/src/ja/docs/features/channel-chat.md @@ -37,6 +37,19 @@ layout: doc /lc channel status # 現在のアクティブチャンネルと参加チャンネル一覧を表示 ``` +## グローバルチャットへの送信 (`!` プレフィックス) + +アクティブチャンネルがある間,チャットはそのチャンネルに送信されます.メッセージの先頭に `!` を付けると,チャンネルから退出したり切り替えたりせずに,そのメッセージだけをグローバルチャットへ送信できます. + +``` +!みんなこんにちは # アクティブチャンネルがあってもグローバルチャットへ送信される +``` + +`!` とその直後の空白は送信前に除去されるため,メッセージ本文には残りません.`!` のみのメッセージは破棄され,何も送信されません. + +> [!NOTE] +> このプレフィックスはチャットリスナーが処理します.リスナーはチャンネルチャット・クロスサーバーグローバルチャット・ローマ字変換のいずれかが有効なときのみ登録されるため,3つすべてが無効な場合は先頭の `!` は入力したまま残ります. + ## ロールと権限 チャンネルには3つのロールがあります. @@ -87,6 +100,10 @@ layout: doc `lunaticchat.channelbypass` パーミッション (デフォルト: op) を持つプレイヤーは,キック・BAN の保護やチャンネルの強制削除が可能です. +## スパイモード + +チャンネルメッセージは管理者に対して非公開ではありません.`lunaticchat.spy` パーミッション (デフォルト: op) を持つプレイヤーには,送信者とそのチャンネルのメンバーを除いてチャンネルメッセージが配信されます.詳細は[スパイモード](/ja/docs/features/admin#スパイモード)を参照してください. + ## メッセージフォーマット チャンネルメッセージの表示形式は `config.yml` の `messageFormat.channelMessageFormat` でカスタマイズできます.詳細は[メッセージフォーマット](/ja/docs/reference/message-format)を参照してください. diff --git a/website/src/ja/docs/features/direct-message.md b/website/src/ja/docs/features/direct-message.md index 945a2db..736a257 100644 --- a/website/src/ja/docs/features/direct-message.md +++ b/website/src/ja/docs/features/direct-message.md @@ -42,6 +42,17 @@ layout: doc /tell <player>@<server> <message> ``` +ここでのサーバー名は,**Velocity の設定 (`velocity.toml`) に登録されている**宛先サーバーの名前です.プロキシはこの名前で宛先を解決します.タブ補完では,その時点で判明しているサーバー名とプレイヤーが提示されます. + +各バックエンドの `features.velocityIntegration.serverName` にも同じ名前を設定してください.この設定値はルーティングには使われませんが,クロスサーバーチャットの `{server}` に入る値であり,またサーバーが自分のプレイヤーを識別するために使われます.Velocity 側の名前と食い違っていると,ローカルのプレイヤーがタブ補完でリモート扱いになります. + +送信が失敗する場合は次の2種類があり,送信者にどちらであるかが通知されます. + +| 理由 | 意味 | +|------|------| +| `SERVER_NOT_FOUND` | その名前で登録されているサーバーがプロキシ上に存在しない | +| `TARGET_OFFLINE` | サーバーは存在するが,そのプレイヤーがそこにいない (別のサーバーでオンラインである場合も含む) | + ## 通知設定 プレイヤーはダイレクトメッセージ受信時のサウンド通知を個別に制御できます. diff --git a/website/src/ja/docs/features/japanese-conversion.md b/website/src/ja/docs/features/japanese-conversion.md index b94542e..1cc7d6b 100644 --- a/website/src/ja/docs/features/japanese-conversion.md +++ b/website/src/ja/docs/features/japanese-conversion.md @@ -19,8 +19,11 @@ layout: doc 入力: konnichiha sekai 変換1: こんにちは せかい 変換2: こんにちは 世界 +送信: konnichiha sekai §e(こんにちは 世界) ``` +入力した文字列は**置き換えられません**.入力したそのままの文字列を残し,その後ろに変換結果を括弧付きで併記するため,受信側には両方が表示されます. + ## 変換対象 - 通常チャット @@ -48,7 +51,7 @@ layout: doc | `cache.saveIntervalSeconds` | `300` | ディスク保存の間隔 (秒) | | `cache.filePath` | `"conversion_cache.json"` | キャッシュファイルのパス | -キャッシュが上限に達すると,古いエントリの10%が自動的に削除されます. +キャッシュが上限に達すると,エントリの10%が削除されます.どのエントリが削除されるかは規定されていません.メモリ上のキャッシュは順序を持たないため,古い順ではなく実質的に任意のエントリが対象になります. ## API 設定 @@ -56,6 +59,11 @@ Google IME API への接続に関する設定です. | 設定キー | デフォルト | 説明 | |----------|-----------|------| -| `api.timeout` | `3000` | リクエストタイムアウト (ミリ秒) | +| `api.timeout` | `3000` | API リクエスト1回あたりのタイムアウト (ミリ秒) | API がタイムアウトまたは失敗した場合,ひらがなのまま送信されます. + +> [!WARNING] +> `api.timeout` とは別に,1メッセージの変換処理全体に **1000ミリ秒**の上限があります.この上限に達すると変換は中断され,入力したそのままの文字列が (何も併記されずに) 送信されます. +> +> 変換全体の上限が `api.timeout` のデフォルト値より短いため,`api.timeout` を `1000` より大きくしても実質的な効果はありません. diff --git a/website/src/ja/docs/features/velocity.md b/website/src/ja/docs/features/velocity.md index 55ee653..9020ea1 100644 --- a/website/src/ja/docs/features/velocity.md +++ b/website/src/ja/docs/features/velocity.md @@ -51,6 +51,8 @@ features: 各メッセージに一意な ID が付与され,キャッシュにより同じメッセージが重複して表示されることを防ぎます.キャッシュサイズは `messageDeduplicationCacheSize` (デフォルト: `100`) で設定できます. +エントリは記録から 60 秒で期限切れになります.期限切れエントリを削除してもなお設定サイズを超えている場合は,残りのうち古いものから削除されます. + ## クロスサーバーダイレクトメッセージ <Badge type="tip" text="v1.3.0~" /> `crossServerDirectMessage` を `true` にすると,同プロキシ内で接続しているサーバーのプレイヤー同士でメッセージのやり取りができるようになります. @@ -80,7 +82,8 @@ features: |----------|-----------|------| | `enabled` | `false` | Velocity 連携を有効にする | | `crossServerGlobalChat` | `false` | クロスサーバーグローバルチャットを有効にする | -| `serverName` | `"Unknown"` | クロスサーバーチャットで表示されるサーバー名 | +| `crossServerDirectMessage` | `false` | クロスサーバーダイレクトメッセージを有効にする | +| `serverName` | `"Unknown"` | このサーバー自身の名前.クロスサーバーチャットの `{server}` に入り,自分のプレイヤーを識別するために使われる.`velocity.toml` に登録した名前を設定する | | `messageDeduplicationCacheSize` | `100` | メッセージ重複排除キャッシュのサイズ | ## メッセージフォーマット diff --git a/website/src/ja/docs/permissions.md b/website/src/ja/docs/permissions.md index 3fb20f5..b2726dd 100644 --- a/website/src/ja/docs/permissions.md +++ b/website/src/ja/docs/permissions.md @@ -44,7 +44,7 @@ LunaticChat のすべてのパーミッションノードの一覧です. | パーミッション | デフォルト | 説明 | |---------------|-----------|------| -| `lunaticchat.spy` | op | サーバー上の全ダイレクトメッセージを閲覧 | +| `lunaticchat.spy` | op | サーバー上の全ダイレクトメッセージ・チャンネルメッセージを閲覧 | | `lunaticchat.noticeupdate` | op | アップデート通知の受信 | | `lunaticchat.channelbypass` | op | チャンネル制限のバイパス(キック・BAN 保護,強制削除) | | `lunaticchat.command.lcv.status` | op | `/lcv status` コマンドの使用 | diff --git a/website/src/ja/docs/reference/commands.md b/website/src/ja/docs/reference/commands.md index 7250ddd..8102a75 100644 --- a/website/src/ja/docs/reference/commands.md +++ b/website/src/ja/docs/reference/commands.md @@ -58,7 +58,8 @@ LunaticChat で使用できるすべてのコマンドのリファレンスで - **エイリアス**: `new` - **パーミッション**: `lunaticchat.command.lc.channel.create` -- `channelId`: 英数字,アンダースコア,ハイフンのみ使用可能 +- `channelId`: 3〜30文字.英数字,アンダースコア,ハイフンのみ使用可能 +- `name`: 空文字は不可 - `isPrivate`: `true` / `false`(デフォルト: `false`) #### `/lc channel list [page]` diff --git a/website/src/ja/docs/reference/compatibility.md b/website/src/ja/docs/reference/compatibility.md index 587261e..781dbf6 100644 --- a/website/src/ja/docs/reference/compatibility.md +++ b/website/src/ja/docs/reference/compatibility.md @@ -57,9 +57,11 @@ Paper / Velocity 間の通信は LunaticChat 独自のプラグインメッセ 接続時は以下の流れで互換性が確認されます: -1. Paper サーバー起動時に Velocity に対してハンドシェイクを送信 +1. **最初のプレイヤーがログインした 1 秒後**に,Paper サーバーが Velocity に対してハンドシェイクを送信 2. Velocity が Paper のプロトコルバージョンを自身のものと照合 3. 不一致の場合は Velocity が接続を拒否し,Paper 側の状態が `FAILED` になる 4. ハンドシェイクのタイムアウトは 5 秒 +ハンドシェイクはサーバー起動ごとに1回だけ送信されます.起動時ではなくプレイヤーのログインが契機になるのは,送信に使う plugin messaging チャネルがプレイヤーの接続を必要とするためです.最初のプレイヤーがログインするまで `/lcv status` は `DISCONNECTED` を報告しますが,これは正常であり異常の兆候ではありません. + 接続状態は `/lcv status` で確認できます.詳細は [Velocity 連携](/ja/docs/features/velocity#接続状態) を参照してください. |
