Skip to content

Socket Events

Felipe Lippelt edited this page Jun 19, 2026 · 2 revisions

Socket Events

Contrato completo dos eventos socket.io entre cliente e servidor.

Tipos em shared/src/index.ts:

ServerToClientEvents { state, campaigns }
ClientToServerEvents { ...todos os abaixo... }

Server → Client

state

state: (state: SessionState) => void

Snapshot completo da sessão. Enviado:

  • Imediatamente após connect (todo client recebe).
  • Após qualquer mutação válida.

campaigns

campaigns: (list: CampaignSummary[]) => void

Lista de campanhas disponíveis (id, title, genre, era). Enviado:

  • Imediatamente após connect.
  • Quando o cliente pede via listCampaigns.

Client → Server

Cenas

setActiveScene: (sceneId: string | null) => void

Define a cena ativa. null limpa o display. Validado contra isTreatmentAllowed().

Lighting

setLighting: (patch: Partial<Lighting>) => void

Merge no estado atual. Cores são saneadas; valores fora do range são clamped (intensity em 0..1).

Áudio

setAudioLayer: (id: string, patch: { playing?: boolean; volume?: number }) => void

Atualiza uma camada. volume clamp em [0,1].

Dice

rollDice: (notation: string) => void
customRoll: (result: { notation, rolls, modifier, total, notes? }) => void

rollDice faz parse + sorteio no server. customRoll é pra resultados já calculados (system rules) — server sanitiza e broadcasta.

Combat Tracker

addCombatant: (
  name: string,
  initiative: number,
  extras?: Record<string, number | boolean>,
  hp?: number,
  maxHp?: number,
) => void

updateCombatant: (
  id: string,
  patch: Partial<Pick<Combatant, 'name' | 'initiative' | 'hp' | 'maxHp' | 'statuses' | 'extra'>>,
) => void

removeCombatant: (id: string) => void
nextTurn: () => void
setCombatActive: (active: boolean) => void
clearCombat: () => void

extra é mesclado (não substituído) no updateCombatant. Status array tem cap em 12 itens.

Campanha

listCampaigns: () => void
selectCampaign: (id: string) => void

selectCampaign recarrega o JSON, tenta restaurar .session.json se for a mesma campanha, ou reseta.

Notas do mestre

setNotes: (text: string) => void

Substitui o texto inteiro. Cap em 16384 chars.

Clocks

addClock: (name: string, segments: number) => void
updateClock: (id: string, patch: { filled?, name?, segments?, color? }) => void
removeClock: (id: string) => void
clearClocks: () => void

Ver Clocks. Estado em SessionState.clocks, persiste no .session.json.

Party Resources

setPartyResource: (key: string, value: number) => void

Clampado por min/max do PartyResourceDef do sistema ativo. Ver Party Resources.

Biblioteca de criaturas

importCreature5e: (rawJson: string, systemOverride?: string) => void
saveCreature: (entry: Omit<CreatureLibraryEntry, 'id' | 'createdAt'>) => void
deleteCreature: (id: string) => void
spawnCombatantFromCreature: (creatureId: string, initiative: number) => void

Persiste em .creatures.json (global). Ver Creature Library.

Biblioteca de encontros

saveEncounter: (entry: { name, system, combatants, notes? }) => void
deleteEncounter: (id: string) => void
spawnEncounter: (id: string) => void

Persiste em .encounters.json (global). Ver Encounter Library.

Tabelas aleatórias

saveTable: (entry: { name: string; entries: string[] }) => void
updateTable: (id: string, patch: { name?: string; entries?: string[] }) => void
deleteTable: (id: string) => void

Persiste em .tables.json (global). A rolagem é local no cliente. Ver Random Tables.

Trilha por cena (Spotify)

setSceneMusic: (sceneId: string, music: SceneMusic | null) => void

Vincula um contexto do Spotify a uma cena; ao ativar a cena, o servidor manda tocar. Persiste em .scene-music.json (indexado por campanha). Ver Spotify.

REST endpoints (não-socket)

Pra completar a referência:

Método Rota Detalhes
GET /spotify/login OAuth start (302 → Spotify)
GET /spotify/callback OAuth callback (302 → /control)
GET /spotify/state SpotifyState JSON
GET /spotify/playlists { playlists: SpotifyPlaylist[] }
POST /spotify/command corpo: SpotifyCommand, retorna { ok }
POST /system/open-assets loopback-only; abre file manager
GET /assets/* static files de assets/
GET /* (fallback SPA) serve client/dist/index.html

Sanitização

Toda entrada socket passa por server/src/validate.ts:

  • toFiniteInt(n) — só Number.isFinite(n) ? n : 0.
  • clamp(n, min, max) — bound.
  • sanitizeRolls(arr) — array de inteiros, limites de tamanho.
  • sanitizeNotes(arr) — strings, até 8 itens × 80 chars.
  • sanitizeStatuses(arr) — até 12, strings curtas.
  • sanitizeExtras(obj) — só number/boolean values, chaves curtas.
  • isSafeCssColor(s) — só hex/rgb/named seguros.
  • capNotation(s) — corta strings absurdas (>200 chars).

Filosofia: trust no caller, never crash. Entrada inválida → ignore silenciosamente; nunca propaga undefined pro broadcast.

Clone this wiki locally