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 /website/src/docs | |
| 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
Diffstat (limited to 'website/src/docs')
| -rw-r--r-- | website/src/docs/configuration.md | 30 | ||||
| -rw-r--r-- | website/src/docs/developers/architecture.md | 10 | ||||
| -rw-r--r-- | website/src/docs/developers/engine.md | 12 | ||||
| -rw-r--r-- | website/src/docs/developers/platform-paper.md | 24 | ||||
| -rw-r--r-- | website/src/docs/features/admin.md | 17 | ||||
| -rw-r--r-- | website/src/docs/features/channel-chat.md | 17 | ||||
| -rw-r--r-- | website/src/docs/features/direct-message.md | 11 | ||||
| -rw-r--r-- | website/src/docs/features/japanese-conversion.md | 12 | ||||
| -rw-r--r-- | website/src/docs/features/velocity.md | 5 | ||||
| -rw-r--r-- | website/src/docs/permissions.md | 2 | ||||
| -rw-r--r-- | website/src/docs/reference/commands.md | 3 | ||||
| -rw-r--r-- | website/src/docs/reference/compatibility.md | 4 |
12 files changed, 112 insertions, 35 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..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. |
