diff options
| author | Sho Sakuma <me@m1sk9.dev> | 2026-08-05 04:36:50 +0900 |
|---|---|---|
| committer | Sho Sakuma <me@m1sk9.dev> | 2026-08-05 04:36:50 +0900 |
| commit | 418be2d127ce2d6a3ed5e9c876deb9c2aafc089b (patch) | |
| tree | db800a91ac8fb05ae870b50014dc7fe06f274c47 /website/src | |
| parent | 048932276d0c1e11d463f8b9aea872e498b04743 (diff) | |
| download | LunaticChat-418be2d127ce2d6a3ed5e9c876deb9c2aafc089b.tar.gz LunaticChat-418be2d127ce2d6a3ed5e9c876deb9c2aafc089b.tar.bz2 LunaticChat-418be2d127ce2d6a3ed5e9c876deb9c2aafc089b.zip | |
docs: correct the website where it had drifted from the implementation
Several statements were wrong rather than merely thin, and each would have set
the wrong expectation:
- the conversion example implied romaji replaces the input, when the result is
appended in parentheses and both are sent
- the handshake was described as happening at startup, when it waits for the
first player to join, so a DISCONNECTED status on an empty server read as a
fault
- cache eviction was called oldest-first, which the unordered in-memory map
cannot provide
- the Velocity settings table omitted crossServerDirectMessage
- ConfigManager was described as reading Bukkit's FileConfiguration, and the
settings storage as recovering from a backup that does not exist
It also documents behaviour that had no mention anywhere: the `!` force-global
prefix, that spy sees channel messages and not only DMs, the nightly build
warnings, the overall 1000ms conversion budget that makes api.timeout above it
ineffective, and the data files the plugin writes.
Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'website/src')
24 files changed, 218 insertions, 66 deletions
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..0c185fb 100644 --- a/website/src/docs/features/admin.md +++ b/website/src/docs/features/admin.md @@ -24,8 +24,10 @@ 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. +- 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 - Spy players see the original message before romaji conversion - 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..f3d39e4 100644 --- a/website/src/docs/features/direct-message.md +++ b/website/src/docs/features/direct-message.md @@ -42,6 +42,15 @@ To message a player on another server, specify the player argument as `playerNam /tell <player>@<server> <message> ``` +`serverName` is the `features.velocityIntegration.serverName` configured on the **destination** server, not the name Velocity uses internally. A server left at the default `"Unknown"` cannot be addressed meaningfully, so give every server a name before enabling this. + +Delivery can fail in two ways, and the sender is told which: + +| Reason | Meaning | +|--------|---------| +| `SERVER_NOT_FOUND` | No server behind the proxy reports that `serverName` | +| `TARGET_OFFLINE` | The server was found, but that player is not online on it | + ## 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..29b59f2 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"` | Server name displayed in cross-server chat, and the name `/tell <player>@<server>` matches against | | `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/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..7d3874a 100644 --- a/website/src/ja/docs/features/admin.md +++ b/website/src/ja/docs/features/admin.md @@ -24,8 +24,10 @@ 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..337759d 100644 --- a/website/src/ja/docs/features/direct-message.md +++ b/website/src/ja/docs/features/direct-message.md @@ -42,6 +42,15 @@ layout: doc /tell <player>@<server> <message> ``` +ここでのサーバー名は,**宛先サーバー側**の `features.velocityIntegration.serverName` に設定した値であり,Velocity が内部で使う名前ではありません.デフォルトの `"Unknown"` のままではサーバーを指定できないため,この機能を有効にする前に各サーバーへ名前を設定してください. + +送信が失敗する場合は次の2種類があり,送信者にどちらであるかが通知されます. + +| 理由 | 意味 | +|------|------| +| `SERVER_NOT_FOUND` | プロキシ配下にその `serverName` を報告しているサーバーが存在しない | +| `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..010165c 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"` | クロスサーバーチャットで表示されるサーバー名.`/tell <player>@<server>` の照合先にもなる | | `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#接続状態) を参照してください. |
