Vanished letters, lingering words

Two independent PvP bugs on the same day: Norwegian letters missing from what the opponent sees, and a crashed word that refused to leave the screen.

Two unrelated PvP bugs got fixed the same day. One was a letter that never showed up where it should have. The other was a word that stayed on screen after it should have gone.

The first came from a player report: type a word with æ, ø, or å in a duel, and the opponent’s live preview of that word just stopped at the Norwegian letter, as if it hadn’t been typed. The endpoint that broadcasts a player’s in-progress word to their opponent sanitizes the input before sending it, and the sanitizer’s character class was a-z, English-only, left over from before Norwegian was a first-class language in the game. Norwegian’s three extra vowels fell outside that range and were stripped along with genuine noise like punctuation and digits.

// before
body.word.toLowerCase().replace(/[^a-z]/g, '').slice(0, MAX_DRAFT_LENGTH)
// after
body.word.toLowerCase().replace(/[^a-zæøå]/g, '').slice(0, MAX_DRAFT_LENGTH)

The second bug took more than a one-line fix to place, even though the eventual diff was just as small. A crashed word is deliberately left on screen through the collision animation, an intentional choice from the lives system so a player can see what they were mid-word on when they hit the wall. The bug was that it stayed visible past that: once the turn passed back to the player who’d crashed, the same word reappeared, as if they’d typed it again on a turn that hadn’t happened yet. A small map with three research passes and a short grilling session settled where the fix belonged before any code changed. The engine’s own copy of the in-progress word was already cleared correctly the moment a turn ends; the stale copy lived one layer up, in the client store that mirrors it for the crash animation, and nothing there ever cleared it once the animation’s job was done.

if (prevMeta && prevMeta.currentTurn !== bundle.meta.currentTurn) {
	_playerAnimatingWord = '';
}

Both fixes landed within single digits of changed lines. The difference was in how each one got found: the missing letters were visible the moment someone typed them, but the lingering word only showed up once someone happened to crash, wait out the opponent’s turn, and notice the field wasn’t actually empty. Worth remembering next time the word panel misbehaves, that its state lives in more than one place and each place clears on its own schedule.