Come funziona un'azione
Cosa fa Vovó di un'azione dal momento in cui una scena la mostra: cosa garantisce, e su cosa un'azione non deve contare. Per chi scrive azioni da consegnare o da vendere; come scriverne una è in Azioni.
Una pagina tutta sua
Ogni azione è un documento a sé, in un riquadro che la pagina della scena le dà: il suo HTML, con il suo style.css e il suo script.js scritti dentro, lo script per ultimo, così gira quando l'HTML c'è già. Un'azione rotta non fa cadere le altre. Una scena mostra le sue azioni una sopra l'altra, nel suo ordine, la prima in basso.
Il riquadro non riceve clic né tasti: un overlay si guarda, non si preme mai. Ciò che fa chi gestisce la diretta, lo fa sul pad, nel pannello dell'azione o in Personalizza, e arriva all'azione come un evento.
Un'azione sente un evento solo finché una pagina aperta la mostra, e solo nel profilo attivo. Senza nessuna pagina aperta, niente conta i follow.
Niente rete, niente salvato nel browser
Un'azione raggiunge i propri file e ciò che Vovó le serve, e nient'altro. Immagini, suoni, video e caratteri vengono dai file dell'azione, per nome, o da un indirizzo dato da un evento (un'emote, una copertina, un file scelto in Personalizza), mai da internet. Il suo codice e i suoi stili sono solo script.js e style.css: nessuno script o foglio di stile da un file o da altrove, niente eval, niente worker. fetch, XMLHttpRequest e WebSocket vengono rifiutati, anche per i file dell'azione stessa, quindi i dati che le servono vanno nel suo script. L'archiviazione del browser (localStorage, cookie, IndexedDB) non c'è: ciò che un'azione conserva, lo conserva in vovo.state.
Ciò che le serve da fuori lo chiede a Vovó, entro i permessi che dichiara: una riga in chat, un webhook per nome, un suono del pad, una clip nel suo player ufficiale.
Ogni pagina aperta la esegue
La stessa azione gira ovunque la sua scena sia aperta: in OBS, nella finestra di anteprima della scena, in una scheda del browser, in due fonti insieme. Ogni copia riceve gli stessi eventi e disegna la stessa cosa. Perciò una pagina disegna e riproduce soltanto, e ciò che deve accadere una volta si chiede a Vovó, che lo fa una volta per le pagine che lo chiedono mentre gestiscono lo stesso evento: una riga in chat, un'estrazione, una voce, un webhook, un suono del pad, una fonte in OBS, un evento proprio dell'azione.
Vovó distingue la chiamata di una pagina da quella di un'altra dall'evento che si sta gestendo. Una chiamata fatta dopo che il listener è tornato (dopo un await, quando finisce un'animazione, in un timer) indica con { event: e } l'evento a cui risponde, l'oggetto che il listener ha ricevuto; senza, Vovó non può distinguere una seconda pagina da una seconda volta.
vovo.on("raid", async (e) =>
{
const gift = await vovo.draw(["a hug", "a song", "a dance"]);
vovo.show("raid", { user: e.user, gift: gift.value });
vovo.chat.send(e.user + " gets " + gift.value + "!", { event: e });
});
Ciò che una pagina avvia da sola non ha un evento alle spalle: un setInterval dell'azione gira in ogni pagina, e così ciò che fa al caricamento. Vovó può allora basarsi solo su ciò che viene chiesto (la stessa riga in chat, la stessa voce, lo stesso webhook o evento proprio entro 5 secondi è uno solo), quindi ciò che conta, o deve accadere una volta, va nell'attivatore timer, che è dell'app e uno solo per tutte le pagine.
Stato, mai replay
vovo.state è tenuto da Vovó, non dalla pagina. Una pagina che si carica (OBS che parte, una fonte aggiornata, il panico che finisce, Vovó riaperta) lo ha tutto prima che il suo script giri, e una chiave impostata in una pagina arriva subito alle altre, senza una parola, dato che hanno gestito lo stesso evento. Gli eventi non vengono mai riprodotti di nuovo: una pagina che non c'era quando è arrivato un raid non lo viene a sapere dopo. Perciò un'azione tiene nel suo stato tutto ciò che deve mostrare di nuovo (il conteggio di un obiettivo, i nomi di un'estrazione, quando finisce un conto alla rovescia) e disegna a partire da lì al caricamento.
function draw()
{
document.getElementById("count").textContent = vovo.state.count || 0;
}
vovo.on("follow", () =>
{
vovo.state.count = (vovo.state.count || 0) + 1;
draw();
});
draw();
- Imposta di nuovo la chiave quando cambia ciò che contiene:
vovo.state.names = names.concat([user]). Un elenco o un oggetto modificato sul posto (push) non viene salvato. - Cambia lo stato nel listener stesso, prima di qualsiasi
await: ogni pagina parte così dallo stesso stato e arriva allo stesso. Dopo unawaitun'altra pagina può aver già scritto, quindi scrivi ciò che non dipende da quello che c'è (il nome di un vincitore, un orario), mai uno in più di quanto contiene. - Una chiave contiene al massimo 64 KB, in JSON, e il suo nome 64 caratteri.
Ciò che è attuale, e ciò che viene letto di continuo
Quattro eventi sono un valore tanto quanto un momento: obs.scene, media.track, twitch.viewers e screen.shared. Un'azione che si carica riceve subito il più recente di ciascuno di quelli che ascolta, come se fosse appena successo, e mostra così il brano in riproduzione senza aspettare il prossimo. Torna a ogni caricamento, in ogni pagina, senza un evento alle spalle: disegna a partire da lui, e non rispondergli con ciò che deve accadere una volta.
audio.level, screen.color, input.move e speech.text sono letture, fino a 60 al secondo: consegnate e dimenticate, lette solo finché una pagina aperta mostra un'azione che le ascolta. Tieni leggeri i loro listener, e lascia fare il movimento al CSS.
Caricare, ricaricare e il panico
Una pagina può caricarsi in qualsiasi momento, e più di una volta: OBS avvia una fonte quando la sua scena compare, può spegnerla mentre è nascosta e aggiornarla quando ricompare, e Vovó ricarica un'azione quando il suo codice o i suoi file cambiano. Il panico svuota tutte le scene in una volta e ricarica le loro azioni, il che ferma qualunque cosa stessero facendo; quando finisce, si caricano ancora una volta e ripartono dal loro stato. Niente arriva a un'azione durante il panico, e niente di ciò che è arrivato nel frattempo viene riprodotto dopo; il valore di un controllo resta, perché è stato.
Quindi un'azione torna giusta dal solo stato, in qualsiasi momento, senza contare su nulla che la pagina precedente abbia lasciato a metà.
Il palco
Un'azione disegna su un palco in pixel CSS il cui lato corto è 1080: 1920 × 1080 per una diretta in 16:9, a qualsiasi risoluzione (una fonte 4K lo disegna nitido al doppio della misura), 1080 × 1920 per una verticale, un'altra forma per uno schermo di un'altra forma. Il suo documento ha la misura del palco ed è trasparente, quindi 100vw per 100vh è l'intera immagine, e ciò che non disegna lascia vedere il gioco. Un'azione fatta per le dirette degli altri non dà per scontata la forma: posiziona le cose a partire dai bordi e in percentuali, e usa @media (orientation: portrait) per una diretta verticale.
Il browser di OBS
OBS mostra le scene in un browser suo, più vecchio di quello dell'editor e della finestra di anteprima di Vovó (Chromium 127 in OBS 32), che può girare senza l'aiuto della scheda grafica. Controlla una novità di CSS o JavaScript contro quella versione prima di contarci, e guarda l'azione in OBS stesso prima di consegnarla. Lì l'audio parte subito, senza clic, e va in diretta: niente che sia solo per chi fa la diretta trova posto in un'azione.
Una pagina non ha console in OBS. Gli errori che l'azione non cattura, e ciò che Vovó ha da dire sulle sue chiamate, arrivano nella console di Vovó sotto il nome dell'azione, con la riga; console.log no. Per quello, apri l'indirizzo dell'anteprima in un browser (vovo dev lo mostra) e usa i suoi strumenti per sviluppatori.
Ritmi e limiti
- Chat: una riga di 500 caratteri; 20 righe in 30 secondi per tutte le azioni, una al secondo per ciascuna.
- Webhook: 16 KB di JSON; 30 al minuto, uno al secondo per ogni azione.
- Clip e video: 3 in attesa di approvazione per ogni azione.
- Videocamere: 4 mostrate alla volta, e 4 impostazioni di videocamera per azione.
- Una chiamata vocale: 300 secondi al massimo.
- Un pulsante mostrato al lavoro: 15 minuti al massimo.
- Stato: 64 KB per chiave.
- Un pacchetto: 200 file, 32 MB ciascuno e 128 MB in tutto;
index.html,style.cssescript.js, 2 MB ciascuno.
Una chiamata oltre il suo ritmo viene lasciata fuori, mai messa in coda: la sua promise viene rifiutata, dicendo perché.
Prima di consegnarla
- Ricarica la scena nel mezzo di tutto ciò che fa: torna giusta, a partire dal suo stato.
- Apri la scena due volte, in OBS e nella finestra di anteprima, e fai scattare ogni attivatore: una riga in chat, un suono, un conteggio.
- Attiva e disattiva il panico mentre mostra qualcosa.
- Provala su un profilo verticale, e in OBS stesso.
- Chiedi solo i permessi che usa, e di' che cos'è sotto Comportamento: il suo autore, il suo sito e una riga.
- Dalle un id, e una versione più alta ogni volta che la consegni, così il suo aggiornamento trova l'azione che sostituisce e dice quale versione porta.
- Di' con quale licenza la consegni, con il testo completo quando le condizioni sono tue.