[msc] make playersettings more efficient

This commit is contained in:
Devin Zuczek
2026-09-30 18:04:56 -04:00
parent 90c808c4a3
commit e00103c6b3
13 changed files with 178 additions and 37 deletions
+1
View File
@@ -17,6 +17,7 @@
},
"dependencies": {
"@2toad/profanity": "3.3.0",
"@repo/domain": "workspace:*",
"@repo/hono-helpers": "workspace:*",
"@repo/jwt": "workspace:*",
"@standard-community/standard-json": "0.3.5",
+5 -7
View File
@@ -2,6 +2,7 @@ import { Hono } from 'hono'
import { describeRoute, openAPIRouteHandler } from 'hono-openapi'
import { useWorkersLogger } from 'workers-tagged-logger'
import { mergePlayerSettings as mergeStoredPlayerSettings, readPlayerSettings } from '@repo/domain'
import { logger, withCleanSpec, withNotFound, withOnError } from '@repo/hono-helpers'
import { validateAndGetAccountId } from '@repo/jwt'
@@ -201,10 +202,7 @@ async function getPlayerSettings(
env: Env,
accountId: number
): Promise<Record<string, string> | null> {
return env.RECFLARE_PLAYER_SETTINGS.get<Record<string, string>>(
`player:${accountId}`,
'json'
).catch(() => null)
return readPlayerSettings(env.RECFLARE_PLAYER_SETTINGS, accountId).catch(() => null)
}
/**
@@ -251,15 +249,15 @@ async function writeChatPrivacy(
* Merge keys into the player's settings map, the way the `playersettings` worker's own PUT
* does. Never a whole-map write: the bag holds every setting the player has (OOBE state,
* tutorial mask, …) and storing one key on its own would wipe the rest. Values are strings,
* which is what that worker stores and what its GET serves back.
* which is what that worker stores and what its GET serves back. Nothing is written when
* the patch changes no value — the client re-posts its privacy settings freely.
*/
async function mergePlayerSettings(
env: Env,
accountId: number,
patch: Record<string, string>
): Promise<void> {
const merged: Record<string, string> = { ...(await getPlayerSettings(env, accountId)), ...patch }
await env.RECFLARE_PLAYER_SETTINGS.put(`player:${accountId}`, JSON.stringify(merged))
await mergeStoredPlayerSettings(env.RECFLARE_PLAYER_SETTINGS, accountId, patch)
}
/**
@@ -956,6 +956,17 @@ describe('PUT /thread/chatPrivacySetting', () => {
})
})
// Re-posting the stored value writes nothing to KV — writes are the cost, and the client
// posts these freely. The raw value is seeded with whitespace JSON.stringify never
// produces; surviving the PUT untouched means no write happened.
it('does not write KV when the value is already stored', async () => {
const padded = '{ "Recroom.OOBE": "77", "directMessagePrivacySetting": "Favorites" }'
await env.RECFLARE_PLAYER_SETTINGS.put('player:887010', padded)
const res = await put(887010, { directMessagePrivacySetting: 'Favorites' })
expect(await res.json()).toMatchObject({ directMessagePrivacySetting: 1 })
expect(await env.RECFLARE_PLAYER_SETTINGS.get('player:887010', 'text')).toBe(padded)
})
it('accepts the enum by ordinal too, and is case-insensitive about the name', async () => {
expect(await (await put(887005, { directMessagePrivacySetting: '2' })).json()).toMatchObject({
directMessagePrivacySetting: 2,