summaryrefslogtreecommitdiff
path: root/platform-paper
diff options
context:
space:
mode:
authorSho Sakuma <me@m1sk9.dev>2026-08-03 00:43:45 +0900
committerSho Sakuma <me@m1sk9.dev>2026-08-05 00:42:23 +0900
commit1ee7a7fb0b24a99daace60016468774a5bd6fbf4 (patch)
tree343191c5012dffab8e1ce227aff3ba687c893890 /platform-paper
parent8ef90fc0a3fad0e71af6c786c4d5739ac81fea71 (diff)
downloadLunaticChat-1ee7a7fb0b24a99daace60016468774a5bd6fbf4.tar.gz
LunaticChat-1ee7a7fb0b24a99daace60016468774a5bd6fbf4.tar.bz2
LunaticChat-1ee7a7fb0b24a99daace60016468774a5bd6fbf4.zip
perf: stop writing the whole settings file on every quit
PlayerQuitEvent called saveToDisk(), which KAML-serializes every player ever recorded and writes the file inline. That runs on the tick thread, costs time proportional to the total stored players rather than the online ones, and so grows for the life of the server. A logout wave stacks the writes into a visible stall. The comment above the call already said "async save"; the storage KDoc already said the synchronous path was for shutdown. Quitting changes no setting, so there is nothing new to persist - the value of saving here is flushing what an earlier toggle left pending. queueSave() does exactly that through the existing debounce, and saveToDisk() is now documented as the shutdown path and called from nowhere else. Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'platform-paper')
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerPresenceListener.kt2
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/settings/PlayerSettingsManager.kt14
-rw-r--r--platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/settings/PlayerSettingsManagerTest.kt11
3 files changed, 25 insertions, 2 deletions
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerPresenceListener.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerPresenceListener.kt
index fc79ffe..6418f6f 100644
--- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerPresenceListener.kt
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerPresenceListener.kt
@@ -95,6 +95,6 @@ class PlayerPresenceListener(
channelManager?.setPlayerChannel(playerId, null)
// 3. Trigger async save of player settings
- playerSettingsManager.saveToDisk()
+ playerSettingsManager.queueSave()
}
}
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/settings/PlayerSettingsManager.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/settings/PlayerSettingsManager.kt
index 9ee8d5a..110390d 100644
--- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/settings/PlayerSettingsManager.kt
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/settings/PlayerSettingsManager.kt
@@ -66,8 +66,20 @@ class PlayerSettingsManager(
}
/**
+ * Queues a debounced asynchronous save without changing any setting.
+ *
+ * Used where the caller wants what is already in memory flushed soon - a player leaving, say -
+ * rather than paying for a write it does not need.
+ */
+ fun queueSave() {
+ storage.queueAsyncSave(::snapshot)
+ }
+
+ /**
* Forces an immediate synchronous save of all settings to disk.
- * This should only be called during plugin shutdown.
+ *
+ * Serializes every stored player and writes the whole file inline, so this belongs on the
+ * shutdown path only; everywhere else should use [queueSave] or [updateSettings].
*/
fun saveToDisk() {
storage.saveToDisk(snapshot())
diff --git a/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/settings/PlayerSettingsManagerTest.kt b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/settings/PlayerSettingsManagerTest.kt
index b7e1385..3c34295 100644
--- a/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/settings/PlayerSettingsManagerTest.kt
+++ b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/settings/PlayerSettingsManagerTest.kt
@@ -154,6 +154,17 @@ class PlayerSettingsManagerTest {
}
@Test
+ fun `queueSave should not write on the calling thread`() {
+ val (manager, storage, _) = createManager()
+ manager.initialize()
+
+ manager.queueSave()
+
+ verify(exactly = 1) { storage.queueAsyncSave(any()) }
+ verify(exactly = 0) { storage.saveToDisk(any()) }
+ }
+
+ @Test
fun `multiple players should have independent settings`() {
val (manager, _, _) = createManager()
manager.initialize()