summaryrefslogtreecommitdiff
path: root/platform-paper
AgeCommit message (Collapse)Author
2026-10-11docs: name the anthy engine in the project descriptionHEADmainCharlie Root
The tagline and both plugin descriptors now say that romaji conversion can run inside the server through anthy, so the engine is visible wherever the project describes itself: the README, the website hero and meta description, the API documentation, and the Paper and Velocity plugin descriptors.
2026-10-11feat(conversion): run anthy in-process through WebAssemblyCharlie Root
Add a second engine for romaji-to-Japanese conversion that leaves the process alone: anthy, compiled to a WebAssembly module and executed in the JVM by Chicory's build-time AOT, so conversion needs no outbound request. anthy/ carries the module sources, the WASI shims, the reproducible build script, the committed AOT output and the dictionary (~23 MB, extracted into the plugin data folder on first start; an existing file is left alone). features.japaneseConversion.engine selects "google" or "anthy", and the anthy block adds dictionary, workDirectory and sentenceLevel. RomanjiConverter now sits on a KanjiConverter interface, so a whole message is handed to the engine at once - anthy segments and reorders a sentence instead of converting word by word - and the conversion cache is keyed by that sentence. The engine tests drive the real module, the dictionary tests cover the extraction, and everything else keeps running on the existing fakes.
2026-10-09fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.133-stable (#327) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-10-08fix(deps): update kotlin monorepo to v2.4.21 (#326)renovate[bot]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-10-07fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.132-stable (#323) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-10-05fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.130-stable (#322) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-28feat: carry the sender's world in cross-server direct messagesSho Sakuma
{world} rendered empty for a direct message from another server, which left formats like "({world})" showing a bare pair of brackets. The relay now carries the sender's world as an optional field, so this is a PATCH bump (protocol 1.0.2) and either side can be updated first; an older sender or proxy still yields an empty value. The world is read once per message, so the sender, the spies and the relayed copy can no longer disagree if the sender changes worlds mid-way. Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-28fix(paper): splice display names as components and share role labelsSho Sakuma
Serializing the display name into the legacy text let its color run into everything after it, and legacy text cannot hold hex colors or hover and click events, so names styled by other plugins came out wrong. The name is now spliced into the parsed component instead. {role} is resolved only when the format uses it, since the default formats do not and it costs a member scan and a lookup per message. /lc channel status now shows the same localized role names instead of a second, hard-coded [OWNER] / [MOD] vocabulary that only one of the two places translated. Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-28feat(paper): add world, display_name, channel_id and role chat placeholdersSho Sakuma
Operators asked for more of the sender's context in chat formats than the name alone. {world} covers direct messages and channel chat; the channel details and the sender's display name and role only exist for channel chat, so only that format accepts them. A direct message from another server renders {world} empty: the relay does not carry the sender's world, and adding it would need a protocol bump and a coordinated Velocity release for one placeholder. The renderer now accepts underscores so the snake_case names match as written. Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-28refactor(paper): render chat formats through one placeholder vocabularySho Sakuma
Placeholder names were spelled as string literals in three handlers, and each handler also chose its own way of turning the result into a Component. Adding a placeholder meant finding every call site by hand, with nothing to catch a format left without its value. ChatPlaceholder now owns the names, and each format has an entry point whose parameters are exactly the placeholders it accepts, so the compiler points at the callers when one is added. Substitution happens in a single pass, so a value that itself contains "{message}" is no longer replaced again by the next placeholder. All three formats now deserialize section signs into styles, which leaves what players see unchanged. Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-28fix(paper): find the newest Paper release instead of trusting releases/latestSho Sakuma
The update checker read releases/latest and stripped a leading "v", so any release whose tag was not a bare vX.Y.Z (paper/v1.2.x, or a Velocity release published last) parsed as 0.x and no update was ever reported. It now lists recent releases and picks the highest Paper tag, skipping drafts, prereleases and non-Paper tags, so a Velocity-only release can no longer hide a Paper update. Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-24fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.129-stable (#315) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-22fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.128-stable (#314) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-21fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.127-stable (#313) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-20fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.126-stable (#312) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-19fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.125-stable (#311) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-16fix(deps): update ktor monorepo to v3.6.0 (#310)renovate[bot]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-16fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.124-stable (#306) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-09fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.123-stable (#302) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-07fix(deps): update kotlin monorepo to v2.4.20 (#301)renovate[bot]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-30fix(paper): stop the command category logging on every tree buildSho Sakuma
Three findings from review of #296. Brigadier evaluates a `requires` predicate when it builds the command tree it sends to a player - on every join and every updateCommands() - not when someone is refused. The denial line sitting there put one entry per gated node per player in the log, burying the command results the category exists to show. It is dropped rather than moved: a hidden node is never reached by an execution, so there is no refusal path to log from. `all` was honoured by `debug: all` and `/lc debug all on` but not by `categories: [all]`, which resolved to an unknown name - leaving `enabled: true` with nothing switched on, reported as off by /lc status, explained only by a warning in the log. The word now lives in one place that all three read. Two dumps in the same second left one file, because the name is only accurate to the second and the write replaces. Reproducing a bug means dumping either side of the step that triggers it, so the second one now takes a suffix. Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-30refactor(paper): let /lc dump name the folder instead of the fileSho Sakuma
The reply carried the report's path so the operator could find the file. But `lunaticchat.command.lc.dump` can be granted to someone with no filesystem access at all, and to them a path is not something they can act on - it only says how the server is laid out. Relativising it against the server directory narrowed that without removing it. Naming the folder in the message says everything a reader can use, and leaves the exact path to the server log, which only someone with that access can read anyway. That also makes the reply a single message rather than a message plus a component the language files could not translate. Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-30Merge branch 'main' into improve/debug-featureSho Sakuma
2026-08-30feat(paper): make the debug switch categorised, live and dumpableSho Sakuma
config.yml now accepts `debug: velocity,protocol` and a block with `enabled` and `categories` beside the boolean it always took. Categories exist so that instrumenting the chat path does not bury the handshake an operator is chasing under one line per message. The setting is read by inspecting the YAML node by hand rather than through a data class, because one key has to accept both a scalar and a map and kaml picks the input to build from the descriptor kind alone - a class descriptor rejects `debug: true` before the serializer is ever called. An unknown category name is collected rather than thrown: throwing would make ConfigManager prune the whole `debug` key and switch off the categories the operator did spell correctly. `debug` moves from RESTART_REQUIRED to applied, since a reload only has to replace a DebugState. `/lc debug` switches a category for the running server and deliberately does not write config.yml back - re-emitting a commented YAML document from a parsed tree loses every comment and every bit of formatting the operator put there, and a restart or `/lc reload` putting the file back in charge is the safer default anyway. `/lc dump` writes the report a bug report needs: versions, enabled features, connection state and how full each store is. It carries no message text, player name or UUID, because it exists to be pasted into a public issue by someone who is not reading it first. Its reply names the file relative to the server directory - the command can be granted to someone with no filesystem access, to whom an absolute path discloses where the server is installed and nothing they can act on. The KtorSimpleLogger calls in ChannelMessageHandler and ChannelMessageLogger are folded into the same logger, so there is one place that decides whether a diagnostic line is printed. Refs #228 Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-30feat(engine): give both platforms one debug switch to readSho Sakuma
"Debug" had grown into three unconnected mechanisms: a boolean injected into RomanjiConverter, a one-off configuration dump at startup, and KtorSimpleLogger calls that Paper's stock log4j configuration never printed. Turning `debug: true` on told an operator nothing about the Velocity handshake, which is what #228 is actually about. The vocabulary goes in engine because it is a contract before it is an implementation: config.yml and the proxy's system property have to agree on what `velocity,protocol` means, and a category added on one side must exist on the other. Only the writing is left to the platforms. The message is a lambda so that the chat path - instrumented once per message - costs nothing while debugging is off. The `if (debugMode) logger.info("... $word")` guards this replaces built their strings either way. Both platform loggers write at INFO rather than at the level the content deserves, because neither Paper's nor Velocity's stock logging configuration prints anything below it, and asking operators to edit log4j before they can answer a bug report is the friction this exists to remove. Refs #228 Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-30fix(build): give nightly builds a version of their ownSho Sakuma
A nightly JAR was named after the bare release version, so a build from main and the release of the same base version were indistinguishable in the JAR file name, /plugins and bug reports; only build-info.properties knew the difference, and Velocity does not even ship that file. The x script tried to express this with -Pversion, but each module assigns its own version from paperVersion/velocityVersion, so the override never took effect. Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-30docs(paper): say why the non-YamlException catch has no testSho Sakuma
It shows up as an uncovered line on every patch that touches parse(), and the next reader would otherwise have to rediscover that no config.yml can reach it. Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-30test(paper): cover the services reading the live message formatsSho Sakuma
Swapping LunaticChatConfiguration for MessageFormatHolder pulled ChannelMessageHandler and CrossServerChatManager into the patch as uncovered, because neither class had ever had a unit test: nothing guarded the claim that /lc reload reaches channel chat and cross-server chat without a restart. Both now assert that a format replaced at runtime applies to the next message, alongside the delivery rules they already relied on. The reloader gains the read failure that carries no message, so the reason it reports is known not to come out empty. Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-30chore: update japanese i18n settingsSho Sakuma
2026-08-30chore: fix `ParseOutcome` doc commentSho Sakuma
2026-08-30Merge branch 'main' into feat/config-reloadSho Sakuma
2026-08-29fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.121-stable (#292) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-28fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.120-stable (#291) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-25fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.119-stable (#287) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-23fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.116-stable (#286) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-22fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.115-stable (#285) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-11fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.112-stable (#280) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-10refactor(paper): reword the /lc reload messages and mark the untranslated linesSho Sakuma
The Japanese wording was revised in place: the messages now say "the configuration file" rather than naming config.yml at the operator, and they follow the punctuation this file already used for in-game text. English is brought in line with that wording, keeping its own trailing periods, which the rest of en.yml uses on every full sentence. The three failure headings now carry a comment saying what is printed beneath them and why those lines are never translated. A translator has no way to see that from the file alone, and the two reasons it cannot change are both easy to undo by accident: the text comes from the YAML parser, so a parser update would silently age any translation kept here, and it cannot be passed as a placeholder because a reason holding braces of its own is broken by the next substitution and then coloured as if the braces were ours.
2026-08-10feat(paper): report /lc reload as a single-line resultSho Sakuma
The reply listed every setting that had moved and every setting that still needed a restart, as dotted keys. That buried the one bit that changes what the operator does next - whether they have to restart - under names they had just typed themselves and did not need read back to them. The detail is not lost: which settings moved was already written to the server log on every reload, which is the better home for it anyway, since a format change can be dated there long after the chat line has scrolled away. A pending restart now outranks "nothing changed" even when nothing could be applied. Reporting no change to someone who did edit the file would send them hunting for a typo they had not made. The failure path still names each unreadable setting and the reason it was rejected, because that is a list the operator cannot reconstruct and needs in order to fix the file.
2026-08-10feat(paper): add /lc reload to apply message formats without a restartSho Sakuma
Changing a message format meant taking the server down, because config.yml was read once in onEnable and every consumer held the value it was built with. Operators tune these formats far more often than anything else in the file, and a restart is a disproportionate price for a colour code. Only messageFormat is applied. The rest of config.yml settles things once, at startup - which services exist, which commands and listeners are registered, which files are opened - and none of that can be redone while the server runs: Paper registers commands only through its COMMANDS lifecycle event, Bukkit cannot unregister a listener, and the conversion cache saver keeps no task handle to cancel. Rebuilding the services would leave the old ones still wired up, so a changed setting the server cannot pick up is reported back as needing a restart rather than silently ignored. MessageFormatHolder is deliberately narrower than LunaticChatConfiguration: taking it says the value is live, taking the configuration says it was frozen at startup. A holder over the whole tree would hold a configuration matching no version of the file, since only messageFormat is ever swapped. Reload reads config.yml more strictly than startup does, because it has an option startup does not - leaving the running configuration alone. Startup falls a single unreadable setting back to its default so the server can come up; reload refuses the file, names every unreadable setting at once, and keeps what the server already had. A document holding no settings is refused for the same reason: it cannot be told apart from a config.yml caught halfway through being written, and resetting every format over that is worse than doing nothing. Closes #266 Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-07fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.111-stable (#277) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-07fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.107-stable (#275) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-07fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.105-stable (#274) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-07fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.100-stable (#272) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-05fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.98-stable (#271) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-05fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.96-stable (#270) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-05fix(deps): update dependency io.papermc.paper:paper-api to ↵renovate[bot]
v26.2.build.93-stable (#269) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-05test: cover what the player settings store promisesSho Sakuma
It was the one file store without a test, so the round trip it exists for - and its two fallbacks, an absent file and an unparseable one - were only asserted through PlayerSettingsManager. The write failure matters most: loading a torn file discards every player's settings, so a failed save must leave the previous file untouched. Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05fix: stop a slow reply from pinning a word to hiraganaSho Sakuma
Making a conversion timeout an ordinary exception put it in the same arm as a hard API failure, where caching the hiragana fallback is deliberate - so one slow reply recorded the unconverted form and that word rendered as hiragana for the life of the cache. A timeout says the request was slow, not that the word has no conversion, so it now returns the fallback without caching it and the next message asks again. The retry test builds its own remembering cache: the shared fixture's get() always returns null, so against it the API is called every time and the test would have passed even with the timeout cached. Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05refactor: drop what no longer carries its weightSho Sakuma
- getDirectMessageSpyPlayers had no callers left; handing out a copy of the spy map is the shape notifySpies was introduced to replace. - pluginScope was widened to public for "commands that must not block the tick thread", but commands take the delivery queue and only LunaticChat reads it. - getPlayerChannels returned a Result that cannot fail, so three callers carried unreachable error paths and channel.status.error could never be shown. - The cross-server managers were gated on velocityIntegration.enabled as well as on a manager that is non-null only when it is enabled, letting the two conditions disagree. - LenientBoolean's non-YamlInput fallback was observationally identical to the cast failing, since both land in ConfigManager's catch-all. - sendCrossServerMessage caught its own failures underneath the delivery queue, which already reports them without stopping the sender's later messages. The second boundary is what forced a CancellationException clause here. - handleOutgoingCrossServerMessage still recorded the reply target after the commands took that over, so remote targets were recorded twice - re-inserting entries clearPlayer had swept, which is the bug the local path was fixed for. - The reason delivery is queued was written out in both command constructors and twice more in KDoc; it now lives where the queueing happens. Spy notification also defers its notice lookup and member set until a spy is actually online, which is not the normal case. Co-Authored-By: Claude <noreply@anthropic.com>