<feed xmlns='http://www.w3.org/2005/Atom'>
<title>LunaticChat.git/website/src/ja/docs/developers, branch main</title>
<subtitle>A next-generation chat plugin for Paper, Folia and Velocity.
</subtitle>
<link rel='alternate' type='text/html' href='https://www.dangofactory.net/cgit/cgit.cgi/LunaticChat.git/'/>
<entry>
<title>fix(paper): splice display names as components and share role labels</title>
<updated>2026-09-28T14:43:13+00:00</updated>
<author>
<name>Sho Sakuma</name>
<email>me@m1sk9.dev</email>
</author>
<published>2026-09-28T14:43:13+00:00</published>
<link rel='alternate' type='text/html' href='https://www.dangofactory.net/cgit/cgit.cgi/LunaticChat.git/commit/?id=a22fce70db7b43aadd8baa42e87a4430a8b1e05f'/>
<id>a22fce70db7b43aadd8baa42e87a4430a8b1e05f</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
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 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>refactor(paper): render chat formats through one placeholder vocabulary</title>
<updated>2026-09-28T13:21:21+00:00</updated>
<author>
<name>Sho Sakuma</name>
<email>me@m1sk9.dev</email>
</author>
<published>2026-09-28T13:21:21+00:00</published>
<link rel='alternate' type='text/html' href='https://www.dangofactory.net/cgit/cgit.cgi/LunaticChat.git/commit/?id=9601f147dd38d5fc93d8fff118794990f44c3766'/>
<id>9601f147dd38d5fc93d8fff118794990f44c3766</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
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 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>docs: record the tag scheme change and the update checker fix in CHANGELOG</title>
<updated>2026-09-28T12:48:55+00:00</updated>
<author>
<name>Sho Sakuma</name>
<email>me@m1sk9.dev</email>
</author>
<published>2026-09-28T12:48:55+00:00</published>
<link rel='alternate' type='text/html' href='https://www.dangofactory.net/cgit/cgit.cgi/LunaticChat.git/commit/?id=5137d1ec51ba1ae609603a928d4a55ff3d499a0c'/>
<id>5137d1ec51ba1ae609603a928d4a55ff3d499a0c</id>
<content type='text'>
The fix is a bug fix only, so it goes under a v1.4.1 heading. The tag scheme
notes now say "up to v1.4.0" instead of naming v1.5.0, because the next Paper
release is a patch and the boundary would otherwise point at a version that
may never ship.

Co-Authored-By: Claude &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The fix is a bug fix only, so it goes under a v1.4.1 heading. The tag scheme
notes now say "up to v1.4.0" instead of naming v1.5.0, because the next Paper
release is a patch and the boundary would otherwise point at a version that
may never ship.

Co-Authored-By: Claude &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>ci: tag each release with exactly one platform</title>
<updated>2026-09-28T12:35:52+00:00</updated>
<author>
<name>Sho Sakuma</name>
<email>me@m1sk9.dev</email>
</author>
<published>2026-09-28T12:35:52+00:00</published>
<link rel='alternate' type='text/html' href='https://www.dangofactory.net/cgit/cgit.cgi/LunaticChat.git/commit/?id=9bb1623c32c80da097f423c1ae3c96aa11372a43'/>
<id>9bb1623c32c80da097f423c1ae3c96aa11372a43</id>
<content type='text'>
A vX.Y.Z tag used to ship both JARs under the Paper version number, so the
Velocity version never appeared in a tag. Changelog download links guessed
paper/ and velocity/ tags that did not exist and returned 404, and the same
JAR resurfaced under several releases.

Paper now releases on vX.Y.Z and Velocity on velocity/vX.Y.Z, which matches
the CHANGELOG.md headings one to one. Paper keeps the bare form rather than
moving to paper/vX.Y.Z because the update checker in already-deployed Paper
builds only understands that form. Both workflows call a shared reusable
workflow, since the two had drifted into near-identical copies.

Co-Authored-By: Claude &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
A vX.Y.Z tag used to ship both JARs under the Paper version number, so the
Velocity version never appeared in a tag. Changelog download links guessed
paper/ and velocity/ tags that did not exist and returned 404, and the same
JAR resurfaced under several releases.

Paper now releases on vX.Y.Z and Velocity on velocity/vX.Y.Z, which matches
the CHANGELOG.md headings one to one. Paper keeps the bare form rather than
moving to paper/vX.Y.Z because the update checker in already-deployed Paper
builds only understands that form. Both workflows call a shared reusable
workflow, since the two had drifted into near-identical copies.

Co-Authored-By: Claude &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Merge pull request #297 from m1sk9/feat/release-v1.4.0</title>
<updated>2026-08-31T06:32:37+00:00</updated>
<author>
<name>Sho Sakuma</name>
<email>me@m1sk9.dev</email>
</author>
<published>2026-08-31T06:32:37+00:00</published>
<link rel='alternate' type='text/html' href='https://www.dangofactory.net/cgit/cgit.cgi/LunaticChat.git/commit/?id=0e24ee83bc05ec4761d46696d07d4a2a4f0b97b6'/>
<id>0e24ee83bc05ec4761d46696d07d4a2a4f0b97b6</id>
<content type='text'>
feat: Release LunaticChat v1.4.0</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
feat: Release LunaticChat v1.4.0</pre>
</div>
</content>
</entry>
<entry>
<title>docs: Add v1.4.0 doc</title>
<updated>2026-08-30T11:55:48+00:00</updated>
<author>
<name>Sho Sakuma</name>
<email>me@m1sk9.dev</email>
</author>
<published>2026-08-30T11:55:48+00:00</published>
<link rel='alternate' type='text/html' href='https://www.dangofactory.net/cgit/cgit.cgi/LunaticChat.git/commit/?id=6eadc66f4ac9bf626e3649da9a3402dec35c7ba6'/>
<id>6eadc66f4ac9bf626e3649da9a3402dec35c7ba6</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>fix(build): give nightly builds a version of their own</title>
<updated>2026-08-30T05:31:38+00:00</updated>
<author>
<name>Sho Sakuma</name>
<email>me@m1sk9.dev</email>
</author>
<published>2026-08-30T05:31:38+00:00</published>
<link rel='alternate' type='text/html' href='https://www.dangofactory.net/cgit/cgit.cgi/LunaticChat.git/commit/?id=48bd6845e76b479d7db8ad23820b988db19ad381'/>
<id>48bd6845e76b479d7db8ad23820b988db19ad381</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
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 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>feat(paper): add /lc reload to apply message formats without a restart</title>
<updated>2026-08-09T23:31:20+00:00</updated>
<author>
<name>Sho Sakuma</name>
<email>me@m1sk9.dev</email>
</author>
<published>2026-08-09T23:31:20+00:00</published>
<link rel='alternate' type='text/html' href='https://www.dangofactory.net/cgit/cgit.cgi/LunaticChat.git/commit/?id=f08d4d8f0557581d93063cd9f6e750ab3ac9cc17'/>
<id>f08d4d8f0557581d93063cd9f6e750ab3ac9cc17</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
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 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>docs: name both plugin versions for cross-server direct messages</title>
<updated>2026-08-05T10:46:16+00:00</updated>
<author>
<name>Sho Sakuma</name>
<email>me@m1sk9.dev</email>
</author>
<published>2026-08-05T10:46:16+00:00</published>
<link rel='alternate' type='text/html' href='https://www.dangofactory.net/cgit/cgit.cgi/LunaticChat.git/commit/?id=639173db48d037ffa5c084d089e5fc7d85d71d80'/>
<id>639173db48d037ffa5c084d089e5fc7d85d71d80</id>
<content type='text'>
The badge read `v1.3.0~`, pointing at a Velocity release that does not exist.
The feature needs both halves, and the Velocity half first shipped as 1.2.0
inside the unified v1.3.0 release, so a reader hunting for Velocity v1.3.0 finds
nothing and cannot tell which proxy build carries the relay.

Also refreshes the gradle.properties example, which still showed the versions
current when it was written.

Co-Authored-By: Claude &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The badge read `v1.3.0~`, pointing at a Velocity release that does not exist.
The feature needs both halves, and the Velocity half first shipped as 1.2.0
inside the unified v1.3.0 release, so a reader hunting for Velocity v1.3.0 finds
nothing and cannot tell which proxy build carries the relay.

Also refreshes the gradle.properties example, which still showed the versions
current when it was written.

Co-Authored-By: Claude &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>docs: correct the website where it had drifted from the implementation</title>
<updated>2026-08-04T19:36:50+00:00</updated>
<author>
<name>Sho Sakuma</name>
<email>me@m1sk9.dev</email>
</author>
<published>2026-08-04T19:36:50+00:00</published>
<link rel='alternate' type='text/html' href='https://www.dangofactory.net/cgit/cgit.cgi/LunaticChat.git/commit/?id=418be2d127ce2d6a3ed5e9c876deb9c2aafc089b'/>
<id>418be2d127ce2d6a3ed5e9c876deb9c2aafc089b</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
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 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
</feed>
