Zum Inhalt springen
  • Dunkel
  • Hell
  • System

C# mit IntelliSense in VS Code entwickeln

Der eingebaute C#-Editor von Streamer.bot reicht für kurze Snippets, aber sobald eine Action größer wird, fehlt das Wichtigste: Autovervollständigung, Typprüfung und das Abfangen von Tippfehlern vor dem Speichern. Die Lösung ist ein kleines lokales Projekt, das gegen dieselben DLLs referenziert, die Streamer.bot intern lädt. Dann schreibst du in VS Code mit voller IntelliSense und kopierst die fertige Datei zurück in die Execute C# Code-Sub-Action.

Der Trick dabei: lokal muss deine Klasse von CPHInlineBase erben, damit der Compiler das CPH-Objekt kennt. In Streamer.bot heißt die Klasse CPHInline und erbt nicht explizit, weil SB die Verbindung zur API selbst herstellt. Ein Preprocessor-Guard hält beide Fälle in einer Datei, sodass du dieselbe Datei kopieren kannst, ohne vorher etwas herauszuschneiden.

Doku: docs.streamer.bot · Visual Studio Code

In der SB-Action selbst gibt es keine Symbol-Datenbank: CPH. löst keine Vorschlagsliste aus, Generics wie GetGlobalVar<int> werden nicht geprüft, und ein vergessenes Semikolon merkst du erst beim Compile-Versuch. Mit einem lokalen .csproj, das die Streamer.bot-Assemblies referenziert, bekommst du:

  • IntelliSense für das komplette CPH-Objekt und alle .NET-Typen
  • Fehler vor dem Einfügen: Tippfehler, falsche Typargumente, fehlende Usings rot unterstrichen
  • Refactoring und Navigation: Umbenennen, Go-to-Definition, Quick-Fixes

Der lokale Build muss nicht laufen oder gestartet werden. Es geht nur darum, dass Roslyn, die Sprach-Engine hinter VS Code, deinen Code gegen die echten DLLs analysiert.

Lege einen Ordner an, öffne ihn in VS Code mit der offiziellen C# Extension von Microsoft (ms-dotnettools.csharp) und erstelle eine Datei Sb.csproj. Wichtig sind das Target-Framework net481 (Streamer.bot läuft auf .NET Framework 4.8.1) und die Konstante EXTERNAL_EDITOR, die den Guard im Code aktiviert.

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net481</TargetFramework>
<LangVersion>13.0</LangVersion>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<DefineConstants>EXTERNAL_EDITOR</DefineConstants>
<NoWarn>CS0114</NoWarn>
<StreamerBotPath>C:/Tools/streamer.bot</StreamerBotPath>
</PropertyGroup>
<ItemGroup>
<Reference Include="$(StreamerBotPath)/Streamer.bot.Plugin.Interface.dll" />
<Reference Include="$(StreamerBotPath)/Streamer.bot.Common.dll" />
<Reference Include="$(StreamerBotPath)/Twitch.Common.dll" />
<Reference Include="$(StreamerBotPath)/NAudio*.dll" />
<Reference Include="$(StreamerBotPath)/Wpf*.dll" />
<Reference Include="$(StreamerBotPath)/Newtonsoft.Json.dll" />
<Reference Include="$(StreamerBotPath)/System.*.dll" />
<Reference Include="System.Web" />
<Reference Include="System.Net.Http" />
</ItemGroup>
</Project>

Setze StreamerBotPath auf deinen tatsächlichen Streamer.bot-Installationsordner, also das Verzeichnis mit der Streamer.bot.exe und den DLLs daneben. Newtonsoft.Json.dll ist hier referenziert, deshalb funktioniert JsonConvert und JObject.Parse lokal genauso wie in SB, wo Newtonsoft standardmäßig verfügbar ist.

Jetzt die eigentliche Code-Datei, zum Beispiel MyAction.cs. Der Guard sorgt dafür, dass lokal eine eindeutig benannte Klasse von CPHInlineBase erbt, während in Streamer.bot derselbe Text als schlichte CPHInline-Klasse kompiliert.

using System;
using Newtonsoft.Json.Linq;
#if EXTERNAL_EDITOR
public class MyAction : CPHInlineBase
#else
public class CPHInline
#endif
{
public bool Execute()
{
// Ab hier volle IntelliSense: CPH. zeigt alle Methoden
CPH.LogInfo("Action gestartet");
if (CPH.TryGetArg("rawInput", out string input))
{
CPH.SendMessage($"Eingabe war: {input}");
}
int count = CPH.GetGlobalVar<int>("hugCount", true);
CPH.SetGlobalVar("hugCount", count + 1, true);
return true;
}
}

Was passiert hier:

  • Lokal ist EXTERNAL_EDITOR durch das .csproj definiert. Der Compiler nimmt public class MyAction : CPHInlineBase. Weil CPHInlineBase das CPH-Objekt bereitstellt, kennt VS Code jede Methode darauf.
  • In Streamer.bot ist die Konstante nicht gesetzt. SB sieht public class CPHInline ohne explizite Vererbung und stellt die Verbindung zur API selbst her.

Der Klassenname im #if-Zweig muss eindeutig sein, weil in einem .csproj mit mehreren Dateien jeder Klassenname nur einmal vorkommen darf. Benenne ihn am besten wie die Datei. Die using-Direktiven oben gehören mit in die Datei, der Guard betrifft nur die Klassendeklaration.

Anders als beim reinen Inline-Editor schneidest du hier nichts heraus. Du kopierst die gesamte .cs-Datei inklusive der using-Zeilen, des #if-Blocks und der schließenden } in die Execute C# Code-Sub-Action. Die Preprozessor-Direktiven sorgen dafür, dass der : CPHInlineBase-Zweig in SB inaktiv bleibt: dort gilt der #else-Zweig, also public class CPHInline. Genau deshalb funktioniert dieselbe Datei in beiden Umgebungen, ohne dass du sie umschreibst.

using System;
using Newtonsoft.Json.Linq;
#if EXTERNAL_EDITOR
public class MyAction : CPHInlineBase
#else
public class CPHInline
#endif
{
public bool Execute()
{
CPH.LogInfo("Action gestartet");
if (CPH.TryGetArg("rawInput", out string input))
{
CPH.SendMessage($"Eingabe war: {input}");
}
int count = CPH.GetGlobalVar<int>("hugCount", true);
CPH.SetGlobalVar("hugCount", count + 1, true);
return true;
}
}

Nach dem Einfügen klickst du in SB auf Compile und speicherst mit Save and Compile. Generics verhalten sich nach dem Kopieren identisch: GetGlobalVar<int>, JObject.Parse oder DeserializeObject<T> kompilieren in SB genau dann, wenn sie lokal grün waren. Genau das ist der Sinn der Übung.

Wenn der Compiler eine Meldung wirft, die mit (are you missing an assembly reference?) endet, fehlt SB eine DLL, die dein .csproj lokal automatisch dabeihatte. Streamer.bot bietet dafür den Find Refs-Button, der versucht, die passende Referenz automatisch nachzulegen. Welche DLLs SB von Haus aus referenziert, steht in der C#-Dokumentation.

  1. Code in VS Code schreiben, IntelliSense und Fehlerprüfung nutzen, bis alles grün ist.
  2. Methoden gegen docs.streamer.bot verifizieren. Die DLLs zeigen dir Signaturen, aber Verhalten wie runImmediately bei CPH.RunAction(...) steht nur in der Doku.
  3. Die gesamte .cs-Datei markieren und in die Execute C# Code-Sub-Action kopieren, mitsamt #if-Block und using-Zeilen.
  4. In SB Compile klicken. Bei einer Meldung über eine fehlende Assembly-Referenz Find Refs nutzen.
  5. Mit Save and Compile speichern und die Action testweise auslösen.
  • Datei nur teilweise kopiert: Lass den #if-Block und die using-Zeilen drin und kopiere die ganze Datei. Die Direktiven blenden den CPHInlineBase-Zweig in SB automatisch aus, da musst du nichts von Hand entfernen.
  • Falsches Target-Framework: Streamer.bot läuft auf .NET Framework 4.8.1. Stell im .csproj net481 ein, nicht net8.0 oder ähnliches. Sonst schlägt lokal Code an, der in SB nicht existiert, oder umgekehrt.
  • Falscher DLL-Pfad: Zeigt StreamerBotPath nicht auf den echten Installationsordner, findet VS Code die Assemblies nicht und IntelliSense bleibt leer. Der Pfad muss die Streamer.bot.Plugin.Interface.dll direkt enthalten.
  • Namespace-Konflikte: Doppelte Klassennamen über mehrere Dateien im selben .csproj brechen den lokalen Build. Benenne jede Klasse im #if-Zweig eindeutig, am besten nach dem Dateinamen.
  • Lokal grün, in SB rot: Meist fehlt in SB eine DLL-Referenz, die dein .csproj lokal automatisch hatte. Klick im Compile-Fehler auf Find Refs, dann ergänzt SB die passende Referenz.