Wie eine Aktion läuft
Was Vovó mit einer Aktion tut, sobald eine Szene sie zeigt: was sie zusichert und worauf sich eine Aktion nicht verlassen darf. Für alle, die Aktionen schreiben, um sie weiterzugeben oder zu verkaufen; wie man eine schreibt, steht unter Aktionen.
Eine eigene Seite
Jede Aktion ist ein eigenes Dokument, in einem Rahmen, den die Seite der Szene ihr gibt: ihr HTML, mit ihrer style.css und ihrer script.js hineingeschrieben, das Skript zuletzt, sodass es läuft, wenn das HTML schon da ist. Eine kaputte Aktion reißt die anderen nicht mit. Eine Szene zeigt ihre Aktionen übereinander, in ihrer Reihenfolge, die erste zuunterst.
Der Rahmen nimmt keinen Klick und keine Taste an: Ein Overlay wird angesehen, nie gedrückt. Was die Person tut, die den Stream führt, tut sie auf dem Pad, im Panel der Aktion oder unter Anpassen, und es erreicht die Aktion als Ereignis.
Eine Aktion hört ein Ereignis nur, solange eine offene Seite sie zeigt, und nur im aktiven Profil. Ist keine Seite offen, zählt nichts die Follows.
Kein Netzwerk, nichts im Browser gespeichert
Eine Aktion erreicht ihre eigenen Dateien und das, was Vovó ihr liefert, und sonst nichts. Bilder, Töne, Videos und Schriften kommen aus den Dateien der Aktion, über ihren Namen, oder von einer Adresse, die ein Ereignis mitgebracht hat (ein Emote, ein Cover, eine unter Anpassen gewählte Datei), nie aus dem Internet. Ihr Code und ihre Stile sind allein script.js und style.css: kein Skript und kein Stylesheet aus einer Datei oder von woanders, kein eval, kein Worker. fetch, XMLHttpRequest und WebSocket werden abgelehnt, auch für die eigenen Dateien der Aktion, also gehören Daten, die sie braucht, in ihr Skript. Der Speicher des Browsers (localStorage, Cookies, IndexedDB) ist nicht da: Was eine Aktion behält, behält sie in vovo.state.
Was sie von außen braucht, erbittet sie von Vovó, innerhalb der Berechtigungen, die sie deklariert: eine Zeile im Chat, ein Webhook über seinen Namen, ein Ton des Pads, ein Clip in seinem offiziellen Player.
Jede offene Seite führt sie aus
Dieselbe Aktion läuft überall, wo ihre Szene offen ist: in OBS, im Vorschaufenster der Szene, in einem Browser-Tab, in zwei Quellen zugleich. Jede Kopie bekommt dieselben Ereignisse und zeichnet dasselbe. Deshalb zeichnet und spielt eine Seite nur, und was einmal geschehen muss, wird von Vovó erbeten, die es einmal tut für die Seiten, die beim Behandeln desselben Ereignisses darum bitten: eine Zeile im Chat, eine Ziehung, eine Sprachausgabe, ein Webhook, ein Ton des Pads, eine Quelle in OBS, ein eigenes Ereignis der Aktion.
Vovó unterscheidet den Aufruf einer Seite von dem einer anderen an dem Ereignis, das gerade behandelt wird. Ein Aufruf, der gemacht wird, nachdem der Listener zurückgekehrt ist (nach einem await, wenn eine Animation endet, in einem Timer), nennt mit { event: e } das Ereignis, auf das er antwortet, das Objekt, das der Listener bekommen hat; ohne das kann Vovó eine zweite Seite nicht von einem zweiten Mal unterscheiden.
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 });
});
Was eine Seite von sich aus beginnt, hat kein Ereignis dahinter: Ein setInterval der Aktion läuft in jeder Seite, ebenso das, was sie beim Laden tut. Vovó kann sich dann nur an das halten, was erbeten wird (dieselbe Zeile im Chat, dieselbe Sprachausgabe, derselbe Webhook oder dasselbe eigene Ereignis innerhalb von 5 Sekunden ist eins), also gehört das, was zählt oder einmal geschehen muss, in den Auslöser timer, der der App gehört und für alle Seiten einer ist.
Zustand, nie Wiederholung
vovo.state wird von Vovó gehalten, nicht von der Seite. Eine Seite, die lädt (OBS startet, eine Quelle wird aktualisiert, die Panik endet, Vovó wird wieder geöffnet), hat ihn ganz, bevor ihr Skript läuft, und ein in einer Seite gesetzter Schlüssel erreicht die anderen sofort, ohne ein Wort, da sie dasselbe Ereignis selbst behandelt haben. Ereignisse werden nie noch einmal abgespielt: Eine Seite, die nicht da war, als ein Raid kam, erfährt später nichts davon. Deshalb hält eine Aktion in ihrem Zustand alles, was sie wieder zeigen muss (den Stand eines Ziels, die Namen einer Verlosung, wann ein Countdown endet), und zeichnet beim Laden daraus.
function draw()
{
document.getElementById("count").textContent = vovo.state.count || 0;
}
vovo.on("follow", () =>
{
vovo.state.count = (vovo.state.count || 0) + 1;
draw();
});
draw();
- Setze den Schlüssel neu, wenn sich ändert, was er hält:
vovo.state.names = names.concat([user]). Eine Liste oder ein Objekt, an Ort und Stelle geändert (push), wird nicht gespeichert. - Ändere den Zustand im Listener selbst, vor jedem
await: Jede Seite geht dann vom selben Zustand aus und kommt beim selben an. Nach einemawaitkann eine andere Seite schon geschrieben haben, also schreibe, was nicht davon abhängt, was dort steht (den Namen eines Gewinners, eine Zeit), nie eins mehr, als er hält. - Ein Schlüssel hält höchstens 64 KB, als JSON, und sein Name 64 Zeichen.
Was aktuell ist und was laufend gemessen wird
Vier Ereignisse sind ebenso sehr ein Wert wie ein Moment: obs.scene, media.track, twitch.viewers und screen.shared. Eine Aktion, die lädt, bekommt sofort den neuesten von jedem, den sie hört, als wäre es gerade passiert, und zeigt so den laufenden Titel, ohne auf den nächsten zu warten. Er kommt bei jedem Laden wieder, in jeder Seite, ohne ein Ereignis dahinter: Zeichne daraus, und beantworte ihn nicht mit dem, was einmal geschehen soll.
audio.level, screen.color, input.move und speech.text sind Messwerte, bis zu 60 pro Sekunde: geliefert und vergessen, nur gelesen, solange eine offene Seite eine Aktion zeigt, die darauf hört. Halte ihre Listener leicht, und lass CSS die Bewegung machen.
Laden, Neuladen und Panik
Eine Seite kann jederzeit laden, und mehr als einmal: OBS startet eine Quelle, wenn ihre Szene erscheint, kann sie abschalten, solange sie verborgen ist, und sie aktualisieren, wenn sie wieder erscheint, und Vovó lädt eine Aktion neu, wenn sich ihr Code oder ihre Dateien ändern. Panik leert alle Szenen auf einmal und lädt ihre Aktionen neu, was beendet, was immer sie gerade taten; wenn sie endet, laden sie noch einmal und gehen von ihrem Zustand aus. Während der Panik erreicht nichts eine Aktion, und nichts, was inzwischen kam, wird danach abgespielt; der Wert eines Steuerelements bleibt erhalten, denn er ist Zustand.
Eine Aktion kommt also allein aus ihrem Zustand richtig zurück, jederzeit, ohne sich auf etwas zu verlassen, das die Seite vor ihr halb fertig hinterlassen hat.
Die Bühne
Eine Aktion zeichnet auf einer Bühne in CSS-Pixeln, deren kurze Seite 1080 misst: 1920 × 1080 für einen Stream in 16:9, gleich in welcher Auflösung (eine 4K-Quelle zeichnet sie scharf in doppelter Größe), 1080 × 1920 für einen im Hochformat, eine andere Form für einen Bildschirm anderer Form. Ihr Dokument hat die Größe der Bühne und ist durchsichtig, also ist 100vw mal 100vh das ganze Bild, und was sie nicht zeichnet, zeigt das Spiel. Eine Aktion für die Streams anderer Leute setzt die Form nicht voraus: Platziere Dinge von den Rändern aus und in Prozent, und nutze @media (orientation: portrait) für einen Hochformat-Stream.
Der Browser von OBS
OBS zeigt Szenen in einem eigenen Browser, älter als der in Vovós Editor und Vorschaufenster (Chromium 127 in OBS 32), der ohne die Hilfe der Grafikkarte laufen kann. Prüfe eine neue CSS- oder JavaScript-Funktion gegen diese Version, bevor du dich auf sie verlässt, und sieh dir die Aktion in OBS selbst an, bevor du sie weitergibst. Ton spielt dort sofort, ohne Klick, und geht in den Stream: Nichts, was nur für die streamende Person gedacht ist, gehört in eine Aktion.
Eine Seite hat in OBS keine Konsole. Fehler, die die Aktion nicht abfängt, und was Vovó zu ihren Aufrufen zu sagen hat, erreichen Vovós Konsole unter dem Namen der Aktion, mit der Zeile; console.log nicht. Öffne dafür die Adresse der Vorschau in einem Browser (vovo dev gibt sie aus) und nutze dessen Entwicklerwerkzeuge.
Takt und Grenzen
- Chat: eine Zeile mit 500 Zeichen; 20 Zeilen in 30 Sekunden für alle Aktionen, eine pro Sekunde für jede.
- Webhooks: 16 KB JSON; 30 pro Minute, einer pro Sekunde für jede Aktion.
- Clips und Videos: 3, die auf Freigabe warten, für jede Aktion.
- Kameras: 4 gleichzeitig zu sehen, und 4 Kamera-Einstellungen pro Aktion.
- Ein Sprachanruf: höchstens 300 Sekunden.
- Ein Button, der als bei der Arbeit gezeigt wird: höchstens 15 Minuten.
- Zustand: 64 KB pro Schlüssel.
- Ein Paket: 200 Dateien, je 32 MB und 128 MB insgesamt;
index.html,style.cssundscript.jsje 2 MB.
Ein Aufruf über seinem Takt fällt weg, er wird nie eingereiht: Sein Promise wird abgelehnt und sagt, warum.
Vor dem Weitergeben
- Lade die Szene mitten in allem neu, was sie tut: Sie kommt richtig zurück, aus ihrem Zustand.
- Öffne die Szene zweimal, in OBS und im Vorschaufenster, und löse jeden Auslöser aus: eine Zeile im Chat, ein Ton, eine Zählung.
- Schalte die Panik ein und aus, während sie etwas zeigt.
- Probiere sie in einem Hochformat-Profil aus, und in OBS selbst.
- Bitte nur um die Berechtigungen, die sie nutzt, und sag unter Verhalten, was sie ist: ihr Autor, ihre Website und eine Zeile.
- Gib ihr eine ID und bei jeder Weitergabe eine höhere Version, damit ihr Update die Aktion findet, die es ersetzt, und sagt, welche Version es bringt.
- Sag, unter welcher Lizenz du sie weitergibst, mit ihrem ganzen Text, wenn die Bedingungen deine eigenen sind.