Globale Variablen: GetGlobalVar & SetGlobalVar
Eine Variable in deinem C#-Code lebt nur, solange das Execute() läuft. Nach dem return ist sie weg. Wenn du einen Zähler hochzählen oder dir den letzten Wert merken willst, brauchst du Speicher der über die Action hinaus bestehen bleibt. Dafür gibt es Globals: CPH.SetGlobalVar schreibt einen Wert, CPH.GetGlobalVar liest ihn später wieder. Das ist das C#-Pendant zu den No-Code-Globals aus dem Globals Pattern, nur eben direkt im Code.
Doku:
Setzen und Lesen
Abschnitt betitelt „Setzen und Lesen“Zwei Methoden, ein Begriffspaar. Beim Setzen gibst du Name, Wert und das Persisted-Flag mit. Beim Lesen sagst du über den generischen Typ-Parameter, als was du den Wert zurückbekommen willst.
// schreibenCPH.SetGlobalVar("hypeCount", 42, true);
// lesen, als intint count = CPH.GetGlobalVar<int>("hypeCount", true);Die Methode SetGlobalVar nimmt einen object-Wert an, du kannst also Zahlen, Strings oder Bools speichern. Beim Lesen legst du mit GetGlobalVar<T> den erwarteten Typ fest, etwa GetGlobalVar<int> oder GetGlobalVar<string>.
| Parameter | Bei welcher Methode | Bedeutung |
|---|---|---|
| varName | beide | Name des Globals, ohne % oder ~. Exakt gleicher Name beim Schreiben und Lesen. |
| value | SetGlobalVar | Der zu speichernde Wert. Typ object, also Zahl, String oder Bool. |
| persisted | beide | Dritter Parameter, bool. true = überlebt den Restart, false = nur im Arbeitsspeicher. |
<T> | GetGlobalVar | Generischer Typ-Parameter: als was der Wert zurückkommt, z.B. int, string, bool. |
Persisted vs Non-Persisted
Abschnitt betitelt „Persisted vs Non-Persisted“Der dritte Parameter entscheidet, wie lange ein Global lebt.
true(persisted): Streamer.bot schreibt den Wert auf die Festplatte. Er überlebt einen Neustart der App. Richtig für Zähler, Highscores oder gemerkte URLs.false(non-persisted): Der Wert lebt nur im Arbeitsspeicher, bis Streamer.bot beendet wird. Praktisch für Werte die nur eine Session lang gelten sollen, etwa ein Rate-Limit pro Stream.
Im Zweifel true nehmen. Das ist auch die Logik hinter dem No-Code-Pendant, mehr dazu im Globals Pattern.
Null-Sicherheit
Abschnitt betitelt „Null-Sicherheit“Hier lauert die häufigste Falle. Wenn der Global noch nie gesetzt wurde, gibt GetGlobalVar nicht etwa eine 0 oder einen leeren String zurück, sondern den Default des Typs: bei Referenztypen und Nullable-Typen ist das null.
Die Lösung ist der Null-Coalescing-Operator ??. Du liest in einen Nullable-Typ und gibst direkt einen Default an, falls null zurückkommt:
// liefert 0 statt null beim ersten Malint n = CPH.GetGlobalVar<int?>("count", true) ?? 0;
// das Gleiche für Stringsstring name = CPH.GetGlobalVar<string>("lastWinner", true) ?? "noch niemand";Wichtig ist GetGlobalVar<int?> (das Fragezeichen macht aus int einen nullable Typ), denn nur ein nullable Wert kann überhaupt null sein und damit den ??-Zweig auslösen. Schreibst du GetGlobalVar<int>, kommt beim ersten Mal eine 0 zurück, was bei einem reinen Zähler oft schon reicht. Mit int? und ?? 0 bist du aber explizit und auf der sicheren Seite.
Beispiel: globaler Zähler
Abschnitt betitelt „Beispiel: globaler Zähler“Ein klassischer FPS-Use-Case: ein !aces-Command, der mitzählt wie viele Aces im Stream schon gefallen sind, und den Stand in den Chat postet.
public class CPHInline { public bool Execute() { // aktuellen Stand holen, null-sicher mit Default 0 int aces = CPH.GetGlobalVar<int?>("aceCount", true) ?? 0;
// hochzählen aces += 1;
// zurückschreiben, persisted true damit es den Restart überlebt CPH.SetGlobalVar("aceCount", aces, true);
// in den Chat posten CPH.SendMessage($"🎯 Ace Nummer {aces} dieser Session. GG!");
return true; }}Get mit ?? 0, plus eins, SetGlobalVar, fertig. Beim ersten Aufruf ist aceCount noch nicht gesetzt, der Default 0 greift, danach steht eine 1 im Global. Beim nächsten Mal liest er 1, macht 2 daraus, und so weiter. Weil persisted auf true steht, ist der Stand auch nach einem Neustart von Streamer.bot noch da.
UnsetGlobalVar
Abschnitt betitelt „UnsetGlobalVar“Zum Löschen oder Zurücksetzen eines Globals gibt es CPH.UnsetGlobalVar. Praktisch für einen !resetaces-Command, der den Zähler wieder auf null bringt:
CPH.UnsetGlobalVar("aceCount", true);Nach dem Entfernen verhält sich der Global wieder wie nie gesetzt: das nächste GetGlobalVar<int?>("aceCount", true) liefert null, und dein ?? 0 fängt das sauber ab. Der dritte Parameter ist auch hier das Persisted-Flag und sollte zum Speicher passen, in dem der Wert liegt.
Häufige Fallen
Abschnitt betitelt „Häufige Fallen“- Persisted-Flag vergessen oder auf
false: Der Zähler steht nach einem Restart wieder bei null. Für dauerhafte Werte immertrueals dritten Parameter setzen, beim Schreiben und beim Lesen. - GetGlobalVar ohne Null-Absicherung: Beim ersten Aufruf kommt
nullzurück und die Action wirft eineNullReferenceException. ImmerGetGlobalVar<int?>(...) ?? 0oder einen passenden Default nutzen. - Falscher Typ-Parameter: Einen Wert als
intgespeichert, aber mitGetGlobalVar<string>gelesen (oder umgekehrt). Schreib- und Lese-Typ müssen zusammenpassen. - Name-Tippfehler:
aceCountgeschrieben,acecountgelesen. Globals sind exakte Strings. Ein vertippter Name zeigt auf einen leeren Global und du bekommst stillschweigend den Default. Am besten den Namen einmal in eineconst string-Variable legen und überall verwenden.