Sister Brawl — Technical Reference

Complete technical reference. Architecture → Architecture. Systems → Systems. Deployment → Deployment.


📁 Complete File Inventory

Frontend (games/sisterbrawl/src/)

FileLOCPurposeKey Exports
Game.svelte2,616Main component — dual 2D/3D renderer, solo practice, bot AI, arena themes, progression UI, VictoryScreen, TutorialOverlayisSoloMode, startSoloPractice(), draw2DFrame(), detectStateChanges(), platformBus handlers
stores/gameStore.svelte.ts891Reactive store — prediction, reconciliation, interpolation, hit-stop, progression, combo logicstate, applyInput(), reconcile(), interpolateRemotes(), hitStopTicks, progression
stores/spectatorStore.svelte.ts~200Spectator state managementspectatorState, camera, joinSpectator()
types/index.ts733Core types — Entity, CharacterDef, AbilityDef, MatchState, EntityState enum, input bitmasksEntity, CharacterDef, AbilityDef, MatchState, EntityState, BTN_*, InputState
lib/audio.ts440Web Audio API — 14+ SFX, BGM loop, master/SFX/music gainAudioEngine, playSFX(), playMusic(), setVolume()
lib/particleSystem.ts84Data-only particle pool (200 max) — sparks, bursts, dust, trails, specialsParticleSystem, emit(), update(), render()
lib/screenShake.ts~150Stackable screen shake — per-attack profiles, quadratic decay, directionalScreenShake, addShake(), update(), getOffset()
lib/arena-themes.ts~1203 deterministic themes + hazards (lava, ice, trees, moving platforms)ArenaTheme, selectArenaTheme(), HazardDef
components/CharacterSelect.svelte~350Character grid, ability tooltips, ready up, arena theme selectoronCharacterSelect, onReadyUp, onArenaThemeChange
components/GameHUD.svelte~280HP bars, combo counter, damage numbers (throttled), super meter, timercombo, damageNumbers, superMeter
components/CountdownOverlay.svelte~120Match start countdown 3-2-1-FIGHTcountdown, onCountdownComplete
components/ResultsOverlay.svelte~200Victory/defeat screen with XP/levels/statsresults, progression
components/ReplayControls.svelte~250Playback scrub for 10Hz snapshots — speed, frame step, ghost overlayplayback, speed, ghostToggle
components/SpectatorViewport.svelte~300Free camera (WASD + mouse drag), HP bars, team colors, kill feedcamera, entities, killFeed
components/TutorialOverlay.svelte~300Interactive tutorial — movement, attack, block, special, win conditionstep, onStepComplete, onTutorialComplete
components/VictoryScreen.svelte~220Silhouette, taunt phrase, announcer SFX, particles, 4s auto-dismisswinner, taunt, onDismiss

Backend (games/sisterbrawl/server/)

FileLOCPurposeKey Exports
match_handler.ts3,127Authoritative Nakama match handler — 60Hz tick, 6 character specials, hazards, progression, Elo, replay, spectatormatchInit, matchJoinAttempt, matchJoin, matchLeave, matchLoop, matchTerminate, stepPhysics(), resolveCombat(), awardMatchProgression()
metrics.ts199Prometheus metrics collection — 4 metricssisterbrawl_active_matches, sisterbrawl_tick_latency_ms, sisterbrawl_avg_players, sisterbrawl_player_kills

Nakama Module Registration (nakama-modules/index.ts)

RPCHandlerPurpose
sisterbrawl_get_replayrpcGetReplayFetch replay snapshots for match
sisterbrawl_metricsrpcGetMetricsJSON metrics for internal use
sisterbrawl_metrics_plainrpcGetMetricsPlainPrometheus text format
sisterbrawl_spectaterpcMatchSpectateGet spectate token for match
sisterbrawl_find_spectatablerpcFindSpectatableMatchFind match with open:true label
sisterbrawl_get_leaderboardrpcGetLeaderboardRanked leaderboard (Elo)
sisterbrawl_get_player_statsrpcGetPlayerStatsPlayer profile (XP, level, wins, losses)
sisterbrawl_get_referral_inforpcGetReferralInfoReferral code + rewards
sisterbrawl_record_referralrpcRecordReferralRecord referral attribution
sisterbrawl_get_active_countsrpcGetActiveCountsActive players/matches for dashboard
sisterbrawl_create_matchrpcCreateMatchCreate custom match (host flow)

🔌 API Contracts

Platform Bridge (Injected Props)

// Injected by NativeGameHost.svelte via platformBus
interface PlatformProps {
  matchId: string | null
  userId: string
  username: string
  sessionToken: string
  isHost: boolean
  matchLabel: MatchLabel
  onMatchState: (state: MatchState) => void
  onMatchEnd: (results: MatchResults) => void
  onPlayerJoin: (player: PlayerInfo) => void
  onPlayerLeave: (userId: string) => void
  onError: (error: string) => void
  sendOpcode: (opcode: number, data: any) => void
  rpc: (id: string, payload: any) => Promise<any>
}

Server → Client Opcodes (match_loop broadcasts)

OpcodeNamePayloadDescription
0STATE_UPDATE{ entities, projectiles, tick, matchId, arenaTheme }60Hz authoritative state
1GAME_OVER{ winner, results, progression, eloChanges }Match ended
2PLAYER_JOINED{ userId, username, character, team }New player
3PLAYER_LEFT{ userId, reason }Player disconnected
4COUNTDOWN{ phase, remaining }3-2-1-FIGHT
5LOBBY_UPDATE{ players, matchLabel }Character select changes
6SPECTATOR_JOINED{ userId, username }Spectator joined
7HIT_EVENT{ attacker, target, damage, type, hitstopFrames }For juice sync
8ARENA_THEME{ arenaTheme }Host theme selection

Client → Server Opcodes (via sendOpcode)

OpcodeNamePayloadDescription
0INPUT{ tick, inputs: InputState, sequence }60Hz input frame
1READY_UP{ characterId, skinId }Character select ready
2CHANGE_CHARACTER{ characterId }Switch character in lobby
3SELECT_ARENA{ arenaTheme }Host selects theme
4USE_SPECIAL{ direction }Special ability input
5EMOTE{ emoteId }Victory/defeat emote
6REQUEST_REMATCH{}Vote for rematch
7SPECTATOR_CAMERA{ x, y, zoom }Spectator camera sync

Match Label Schema (Nakama)

interface MatchLabel {
  open: "true" | "false"
  mode: "ranked" | "casual" | "custom"
  arenaTheme: "volcanic" | "frozen" | "forest" | "auto"
  hostId: string
  playerCount: number
  maxPlayers: number
  version: string
}

RPC Payloads

// sisterbrawl_create_match
{ mode: 'ranked'|'casual'|'custom', arenaTheme?: string, maxPlayers?: number }
// → { matchId: string, matchToken: string, label: MatchLabel }
 
// sisterbrawl_find_spectatable
{} // → { matchId: string, label: MatchLabel }[]
 
// sisterbrawl_spectate
{ matchId: string } // → { spectateToken: string }
 
// sisterbrawl_get_leaderboard
{ limit?: number, offset?: number }
// → { leaderboard: [{ userId, username, rating, rank, wins, losses }] }
 
// sisterbrawl_get_player_stats
{ userId?: string }
// → { xp, level, wins, losses, streak, characters: { [charId]: { mastery, wins } } }
 
// sisterbrawl_get_replay
{ matchId: string, fromTick?: number, toTick?: number }
// → { snapshots: ReplaySnapshot[] }

✅ Verification Checklists

Pre-Deploy Checklist

CheckCommandExpected
TypeScript cleantsc --noEmit0 errors
Lint cleaneslint src/0 warnings
Vite build successnpx vite build --mode productionSuccess
Nakama module buildcd nakama-modules && npm run builddist/index.js created
Health check (local)curl -sf http://127.0.0.1:3000/health200 OK
Game loads (prod)curl -sf https://funday.gg/play/sisterbrawl | grep canvasContains canvas

Post-Deploy Verification (Browser)

CheckToolExpected
Canvas alivebrowser_consolecanvas.width > 0 && non_black_pixels > 1%
No JS errorsbrowser_consoleconsole.errors.length === 0
Feature rendersbrowser_visionVision confirms CharacterSelect → Countdown → Gameplay
60fps sustainedbrowser_consolerequestAnimationFrame delta < 18ms for 30s
WebSocket connectsbrowser_consolesocket.onopen logged, STATE_UPDATE received
Solo mode worksbrowser_play2s timeout → bot spawns, 2D canvas renders

Fun Verify Scorecard (Per Phase)

MetricTargetMethod
Hitstop feel2f regular, 5f combo, 3f KOFrame-count in browser console
Shake stackingNo judder, directionalVisual + screenShake.ts unit test
Input latency<16ms (1 frame)performance.now() in prediction loop
60fps sustained0 drops in 5min matchChrome Performance tab
Audio zero-lagSFX on frame 0detectStateChanges() logs
Particle cap≤200 active, no GC spikesparticleSystem.ts pool size
Elo integrity±16 equal, floor respectedcompetitive-integrity-recipes.md tests
Forfeit progressionWinner gets full XPawardMatchProgression() in matchLoop & matchLeave

Ship Gate: fun-verify scorecard avg ≥ 3.5, no 🔴 killers


🔗 Integration Touchpoints

SystemHookValidation
Nakama match handlermatchLoop/matchLeave call awardMatchProgression()Forfeit winner gets XP
Leaderboard RPCsisterbrawl_get_leaderboardnakama-modulespluginId: sisterbrawl, method prefix
SpectatorjoinMatch(id, undefined, {spectator:'true'})Label open:false after matchInit
Elo/Rankmetrics.ts + bridge → Prometheus → GrafanaAlert on active_matches gauge leak
Frontend deploybuild-atomic.shfunday-frontend.service restartHash match verification
Nakama deploynpm run buildkubectl rollout restartStartup done in logs

🎯 Key Constants (Single Source)

ConstantValueLocation
TICK_RATE60 Hzmatch_handler.ts, gameStore.svelte.ts
MATCH_TIMEOUT300s (18,000 ticks)match_handler.ts
KILL_THRESHOLD10 killsmatch_handler.ts
ARENA_SIZE56×56 (±28 units)match_handler.ts, Game.svelte
GRAVITY-20 m/s²match_handler.ts, Game.svelte
ATTACK_RANGE1.5 unitsmatch_handler.ts
ATTACK_COOLDOWN15 ticks (250ms)match_handler.ts
JUMP_VELOCITY10 m/smatch_handler.ts
KNOCKBACK8 m/smatch_handler.ts
HITSTUN20 ticks (333ms)match_handler.ts
BLOCK_REDUCTION80%match_handler.ts
BASE_K (Elo)32match_handler.ts, metrics.ts
MIN_RATING (Elo)100match_handler.ts
PROVISIONAL_K64match_handler.ts
PROVISIONAL_MATCHES10match_handler.ts
PARTICLE_POOL_MAX200particleSystem.ts
DAMAGE_NUMBER_POOL50Game.svelte

🛡️ Risks & Mitigations

RiskMitigation
Build breaks from other gamesgit stash && npx vite build isolate; commit before deploy
Circular $state in Svelte 5untrack() in setters; helper $derived null-safety pattern
WebGL black in headlessHTML/Svelte overlay for screenshots; CDP injection
AudioContext leakcleanup() in onDestroy; null all refs
Stale import.meta.globRebuild on any game file change
Label propagation gapupdateMatchLabel() after every state change
Forfeit progression gapShared helper awardMatchProgression()
Client-server contract driftUnit test server math; import in client vitest
Memory pressure build--clean flag; verify free -h > 4GB

📋 Rollout Commands (Quick Reference)

StageCommandVerification
Local devcd games/sisterbrawl && npm run devHot reload, console clean
Nakama testcd nakama-modules && npm run build && sudo kubectl rollout restart -n funday-platform deployment/nakamaStartup done in logs
Frontend testbash scripts/build-atomic.sh --cleangrep "new-change" build/client/_app/immutable/chunks/*.js
Staging smokecurl https://funday.gg/play/sisterbrawl200, canvas renders, WS connects
LiveDeploy to prodfun-verify scorecard ≥ 3.5

  • Architecture — System diagram, data flow, match lifecycle
  • Systems — Core systems deep-dive
  • Deployment — Build pipeline, health checks, monitoring
  • funday-play-shell skill → references/game-feel-patterns.md
  • funday-play-shell skill → references/victory-screen-pattern.md
  • funday-play-shell skill → references/arena-theme-pattern.md
  • funday-play-shell skill → references/elo-rating-pattern.md
  • funday-play-shell skill → references/nakama-label-propagation-pitfall.md
  • funday-play-shell skill → references/forfeit-progression-gap.md
  • funday-play-shell skill → references/client-server-contract-pitfalls.md

Ask Docs

AI assistant to help answer questions about the documentation. Answers are read-only and cite docs/source.

Hi! How can I help you with the documentation today? Answers are read-only and cite docs/source.

Ctrl+Enter to send