summaryrefslogtreecommitdiff
path: root/engine
AgeCommit message (Collapse)Author
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-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-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-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-05refactor: keep rendering out of CommandResultSho Sakuma
CommandResult carried Adventure Components, which was engine's last Minecraft dependency and the reason CLAUDE.md's "engine has no Minecraft platform dependencies" was not quite true. It also meant a command could not report a result without having already decided how it looks: every site had to pick formatError versus format before it could return. Results now carry text, and LunaticCommandBase.handleResult is the single place that styles it - error red for Failure, normal for SuccessWithMessage. The fail()/ok() helpers from #260 already funnelled every call site through two functions, so this is a change to those two plus the one command that composes its own success text. engine's dependency list is down to kotlinx-serialization, and nothing under engine/src references net.kyori, org.bukkit, com.velocitypowered or io.papermc. Not done: the review also proposed collapsing the per-command `when (error)` blocks into one exception-to-key table. Those blocks pick wording, not just a key - "only owners can delete this channel" reads differently in the ban command than the delete command - so a shared table would hand every caller the same sentence and need per-command overrides on top. Left alone deliberately. Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05refactor: make the direct message failure reason an enumSho Sakuma
DirectMessageError.reason was a String backed by two constants, so the receiving side matched one case and let everything else fall through to "the target is offline". Adding a third reason on the proxy would have shipped it to Paper servers that silently reported the wrong thing - the one string-keyed dispatch sitting next to a protocol layer whose messages are otherwise a sealed hierarchy with exhaustiveness checking. As an enum, the reader must decide what to show for each case, and CrossServerDirectMessageManager's when no longer needs an else. The wire format is unchanged: kotlinx serializes an enum as its name, so the existing snapshots still decode. What did need care is the reverse direction - a reason from a newer proxy would now fail to parse, where the String version degraded. The property has a default and the codec enables coerceInputValues, so an unknown reason lands on TARGET_OFFLINE, exactly the old else branch. There is a compatibility test for that case. Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05refactor: move romaji conversion out of engineSho Sakuma
Closes #259. engine exposed ktor through api(), so both platforms inherited the client and its CIO engine. The only thing in engine that used ktor was GoogleIMEClient, and the only module that used GoogleIMEClient was platform-paper - Velocity was shipping roughly six megabytes of HTTP client to support a Paper-only feature. Same story for kotlinx-coroutines-core, which Velocity does not use at all. Romaji conversion is a Paper feature, so the converter package now lives in platform-paper alongside the ConversionCache and RomanjiConverter that were already there. engine keeps kotlinx-serialization on api(), which is genuine shared surface: the plugin messaging protocol is built on it. The velocity shadow jar goes from 7,618,405 to 2,769,395 bytes, and no longer contains io/ktor at all. CacheData's tests were sitting inside engine's SettingsDataClassesTest, which is unrelated to settings; they move with the class. Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-02refactor: fold the remaining small duplicationsSho Sakuma
- MessageFormatter built the same prefix component in three functions. - LanguageManager copied kaml's YamlNode into a private YamlValue tree before flattening it, so the map case was written twice and the list-of-maps case rendered a Kotlin data class toString into a player facing string. It now folds YamlNode directly. - StatusCommand inlined `if (enabled) "toggle.on" else "toggle.off"`, which is the body of LanguageManager.getToggleText. - The three chat formats each spelled out their own chain of String.replace, with the valid placeholder names documented only in a config.yml comment. - ChannelContext carried a channelId that both construction sites filled with channel.id; it is now derived, so the two cannot disagree. - ChannelInfo and ChannelStatus each declared MAX_MEMBERS_DISPLAY = 10 and built the same truncated member line, differing only in indent. A divergence between the two constants would have been invisible. Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-02refactor: single-source the plugin messaging channel and dedup cacheSho Sakuma
The channel Paper and Velocity talk over was declared in seven places, in two spellings ("lunaticchat:main" and the namespace/name pair), one of them an inline literal in CrossServerChatManager that bypassed even its own file's constant. Renaming it meant finding all seven; missing one leaves both sides compiling and starting, just not talking. It now lives next to the codec that defines the wire format. The echo-suppression cache was likewise written twice, and the copies had already drifted in style - one hand-rolled the expiry sweep, the other used filter/map - while staying semantically identical. Any future change to eviction would have had to land in both, and CrossServerChatManager's copy carried a comment claiming ConcurrentHashMap iterators cannot remove(), which they can. MessageDeduplicationCache documents the one property that surprised the tests written against it: eviction orders by millisecond timestamp, so a burst inside a single millisecond evicts arbitrarily among its members. Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-02refactor: remove code that no production path reachesSho Sakuma
These were all scaffolding that drifted out of use, and each one costs a reader time before they discover it does nothing: - UUIDASStringSerializer duplicated UUIDSerializer byte for byte; the differing descriptor name never reaches the JSON/YAML wire format, so the choice between them was a coin flip for contributors. - Velocity's BuildInfo was never referenced (the plugin reads its version from PluginContainer) and read a "commit" property the build never wrote, so it would have reported "unknown" had anyone called it. - KanaConverter.TrieNode.Leaf is never constructed: buildTrie starts from a Branch and insert only ever returns Branch. Six branches guarded against a state the type system allowed but the code could not produce. With those gone, isValidRomaji and toHiragana were visibly the same trie walk, so they now share one longestMatch. - @Deprecated command handling had no annotated command to act on. - The settings backup restore looked for *.backup.* files that nothing in the repository writes, so it always fell through to empty settings. Also drops CommandContext.replyWithEvent/replyPlain, PluginCoroutineScope's unused plugin parameter, GitHubRelease fields no caller reads, and four language keys with no lookup site. Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-02feat(velocity)!: build against Velocity API 4.0.0 and drop Velocity 3.5.x ↵Sho Sakuma
support Velocity moved to the 4.x generation while platform-velocity still compiled against 3.5.1, so the debug environment had to pin an older proxy than the one most users now run. The migration needed no source changes. Comparing every one of the 213 classes in the API jar with javap shows 4.0.0 is identical to 3.5.1 in its public signatures; the supported protocol range is unchanged too. The one real difference is the POM, which moves adventure-bom from 4.26.1 to 5.2.0. Support for 3.5.x is dropped even though the JAR would still load there, because keeping it meant Adventure 4.26.1 could be the runtime and engine had to stay inside the API surface both Adventure majors share -- a constraint no build step could check. Narrowing to 4.x makes every supported runtime ship Adventure 5.2.0, matching what engine already compiles against, so the constraint is gone rather than merely documented. Nothing enforces the requirement in code, matching how dropping 3.4.0 was handled in v1.1.0. Also drops two dead dependencies: kaml, declared but never imported, worth about 1 MB of shaded JAR, and the velocity-api annotationProcessor, which does nothing without Java sources or kapt.
2026-08-02feat: support Paper 26.2 (Minecraft 26.2)Sho Sakuma
Paper 26.2 bundles Adventure 5.2.0, a major bump from the 4.26.1 shipped by 26.1. Adventure 5 makes ClickEvent generic, so the raw type in CommandContext.replyWithEvent no longer compiled. Auditing the rest of the Adventure 5 removals found no other affected usage. api-version is raised to 26.2, dropping 26.1 support: the two Adventure majors are not binary compatible, so claiming 26.1 compatibility would be a lie. Also fixes two problems found while verifying on a real server: the Velocity healthcheck invoked mc-health, which does not exist in the mc-proxy image, so the container had never once passed its check; and the debug environments pinned no build at all, which meant the proxy came up as 4.1.0-SNAPSHOT while the plugin is compiled against velocity-api 3.5.1. Server versions are now derived by x from the Gradle coordinates so they cannot drift from what the plugin targets.
2026-07-31fix(deps): update ktor monorepo to v3.5.2 (#256)renovate[bot]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-25fix(deps): update ktor monorepo to v3.5.1 (#237)renovate[bot]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-17feat: add cross-server direct messaging via VelocitySho Sakuma
Allow /tell and /reply to reach players on other Paper servers behind a Velocity proxy using the "<player>@<server>" target syntax. Engine (protocol bumped 1.0.0 -> 1.0.1, optional sub-channels): - Add DirectMessageRelay, DirectMessageError, PresenceSnapshot/PresenceEntry and PresenceRequest messages plus codec branches. Velocity: - CrossServerDirectMessageRelay routes a DM to the target server (or returns a delivery error to the source). - PresenceTracker broadcasts proxy-wide presence snapshots on join/quit/switch and on request. Paper: - RemotePlayerRegistry caches proxy presence for completion and remote target resolution. - CrossServerDirectMessageManager handles send/receive/error and dedup. - DirectMessageHandler reply state generalized to ReplyTarget (Local/Remote) so /reply works across servers. - TellCommand parses "name@server", completes local names and remote name@server targets, and uses exact local name matching. - New crossServerDirectMessage config flag and i18n keys (en/ja). Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-14fix(deps): update ktor monorepo to v3.5.0 (#207)renovate[bot]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-08fix(deps): update kotlinx-coroutines monorepo to v1.11.0 (#200)renovate[bot]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-22fix(deps): update ktor monorepo to v3.4.3 (#189)renovate[bot]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-09fix(deps): update dependency ↵renovate[bot]
org.jetbrains.kotlinx:kotlinx-serialization-json to v1.11.0 (#184) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-04test: Add protocol version test-caseSho Sakuma
2026-04-04feat: Improving Velocity's Cycling CompatibilitySho Sakuma
2026-03-27fix(deps): update ktor monorepo to v3.4.2 (#157)renovate[bot]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-17feat!: Remove ChatMode feature, followback channel chat modeSho Sakuma
2026-03-15refactor: Optimization of internal logicSho Sakuma
2026-03-03fix(deps): update ktor monorepo to v3.4.1 (#129)renovate[bot]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-26test: expand test coverage across all modulesSho Sakuma
- Add 23 new test files covering exceptions, data classes, permissions, setting handlers, commands, and cross-server relay - Change command execute methods from private to internal visibility to enable unit testing (16 command files) - Engine coverage: 65.5% → 92.4% - Platform-paper coverage: 16.8% → 40.0% - Platform-velocity coverage: 62.4% → 76.8% Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25feat: add Codecov/Jacoco integration and expand test coverageSho Sakuma
- Add Jacoco plugin to all subprojects with XML report generation - Replace post-test-results.sh with Codecov upload in CI workflow - Add codecov.yml configuration - Add 140 new tests across engine, platform-paper, and platform-velocity Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07test: Add kana converter test-caseSho Sakuma
2026-02-07fix: Fix kana converter valid romajiSho Sakuma
2026-02-06feat: Add global chat protocolSho Sakuma
2026-02-01feat: Add Velocity protocolSho Sakuma
2026-01-31feat: Add channel loggingSho Sakuma
2026-01-27refactor: Rename exceptionSho Sakuma
2026-01-26feat: Add channel-chat configurationSho Sakuma
2026-01-26feat: Add ChangeChat message notificationSho Sakuma
2026-01-26feat: Add channel-chat handleSho Sakuma
2026-01-26feat: Add chatmode dataSho Sakuma
2026-01-26feat: Add channel commandSho Sakuma
2026-01-25feat: Add Channel CRUD operatorSho Sakuma
2026-01-25feat: Initialize Channel storage logicSho Sakuma
2026-01-24feat: Add status commandSho Sakuma
2026-01-24feat: Add new permissionSho Sakuma
2026-01-23refactor: Improving Module DependenciesSho Sakuma
2026-01-22fix(deps): update ktor monorepo to v3.4.0 (#53)renovate[bot]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-21fix(deps): update dependency ↵renovate[bot]
org.jetbrains.kotlinx:kotlinx-serialization-json to v1.10.0 (#52) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-20test: Add Kana Conversion TestSho Sakuma
2026-01-20fix: Fix Kana conversion with voiced consonantsSho Sakuma
2026-01-17feat: Add `directMessageNotice` settingsSho Sakuma
2026-01-17fix(deps): update dependency net.kyori:adventure-api to v4.26.1 (#45)renovate[bot]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>