Ich habe ein kleines Spiel in C # geschrieben. Es verwendet eine Datenbank als Backend. Es ist ein Sammelkartenspiel , und ich wollte die Funktion der Karten als Skript implementieren.
Was ich damit meine ist, dass ich im Wesentlichen eine Schnittstelle habe, ICard
die eine Kartenklasse implementiert ( public class Card056: ICard
) und die eine Funktion enthält, die vom Spiel aufgerufen wird.
Um das Ding wartbar / modifizierbar zu machen, möchte ich die Klasse für jede Karte als Quellcode in der Datenbank haben und sie im Wesentlichen bei der ersten Verwendung kompilieren. Wenn ich also eine Karte hinzufügen / ändern muss, füge ich sie einfach der Datenbank hinzu und fordere meine Anwendung auf, eine Aktualisierung durchzuführen, ohne dass eine Assembly-Bereitstellung erforderlich ist (insbesondere, da es sich um eine Assembly pro Karte handelt, was Hunderte von Assemblys bedeutet). .
Ist das möglich? Registrieren Sie eine Klasse aus einer Quelldatei und instanziieren Sie sie dann usw.
ICard Cards[current] = new MyGame.CardLibrary.Card056();
Cards[current].OnEnterPlay(ref currentGameState);
Die Sprache ist C #, aber ein zusätzlicher Bonus, wenn das Skript in einer beliebigen .NET-Sprache geschrieben werden kann.
quelle
Antworten:
Die C # -Skriptlösung von Oleg Shilo (bei The Code Project ) ist wirklich eine großartige Einführung in die Bereitstellung von Skriptfunktionen in Ihrer Anwendung.
Ein anderer Ansatz wäre, eine Sprache zu betrachten, die speziell für Skripte entwickelt wurde, wie z. B. IronRuby , IronPython oder Lua .
IronPython und IronRuby sind beide heute verfügbar.
Eine Anleitung zum Einbetten von IronPython finden Sie unter Einbetten der IronPython-Skriptunterstützung in Ihre vorhandene App in 10 einfachen Schritten .
Lua ist eine Skriptsprache, die häufig in Spielen verwendet wird. Es gibt einen Lua-Compiler für .NET, der unter CodePlex erhältlich ist - http://www.codeplex.com/Nua
Diese Codebasis ist eine gute Lektüre, wenn Sie mehr über das Erstellen eines Compilers in .NET erfahren möchten.
Ein ganz anderer Blickwinkel ist das Ausprobieren von PowerShell . Es gibt zahlreiche Beispiele für die Einbettung von PowerShell in eine Anwendung - hier ein ausführliches Projekt zum Thema: Powershell Tunnel
quelle
Möglicherweise können Sie dafür IronRuby verwenden.
Andernfalls würde ich vorschlagen, dass Sie ein Verzeichnis haben, in dem Sie vorkompilierte Assemblys platzieren. Dann könnten Sie in der Datenbank einen Verweis auf die Assembly und Klasse haben und Reflection verwenden, um die richtigen Assemblys zur Laufzeit zu laden.
Wenn Sie wirklich zur Laufzeit kompilieren möchten, können Sie das CodeDOM verwenden, und Sie können Reflection verwenden, um die dynamische Assembly zu laden. Microsoft-Dokumentationsartikel, der helfen könnte .
quelle
Wenn Sie das DLR nicht verwenden möchten, können Sie Boo (mit einem Interpreter) verwenden oder das Script.NET (S #) -Projekt in CodePlex in Betracht ziehen . Mit der Boo-Lösung können Sie zwischen kompilierten Skripten oder der Verwendung des Interpreters wählen. Boo erstellt eine schöne Skriptsprache, verfügt über eine flexible Syntax und eine erweiterbare Sprache über die offene Compiler-Architektur. Script.NET sieht aber auch gut aus, und Sie können diese Sprache sowie ein Open-Source-Projekt problemlos erweitern und verwenden einen sehr benutzerfreundlichen Compiler-Generator ( Irony.net ).
quelle
Sie können jede der DLR-Sprachen verwenden, mit denen Sie Ihre eigene Skriptplattform ganz einfach hosten können. Sie müssen hierfür jedoch keine Skriptsprache verwenden. Sie können C # verwenden und es mit dem C # -Code-Anbieter kompilieren. Solange Sie es in eine eigene AppDomain laden, können Sie es nach Herzenslust laden und entladen.
quelle
Ich würde vorschlagen, LuaInterface zu verwenden, da es Lua vollständig implementiert hat, wobei es den Anschein hat, dass Nua nicht vollständig ist und wahrscheinlich einige sehr nützliche Funktionen (Coroutinen usw.) nicht implementiert.
Wenn Sie einige der externen vorgefertigten Lua-Module verwenden möchten, würde ich empfehlen, etwas in der Art von 1.5.x zu verwenden, im Gegensatz zur 2.x-Serie, die vollständig verwalteten Code erstellt und die erforderliche C-API nicht verfügbar macht.
quelle
Ich verwende LuaInterface1.3 + Lua 5.0 für eine NET 1.1-Anwendung.
Das Problem mit Boo ist, dass jedes Mal, wenn Sie Ihren Code im laufenden Betrieb analysieren / kompilieren / auswerten, eine Reihe von Boo-Klassen erstellt werden, sodass Speicherlecks auftreten.
Lua dagegen macht das nicht, also ist es sehr, sehr stabil und funktioniert wunderbar (ich kann Objekte von C # an Lua und rückwärts übergeben).
Bisher habe ich es noch nicht in PROD aufgenommen, aber es scheint sehr vielversprechend.
Ich hatte Probleme mit Speicherverlusten in PROD mit LuaInterface + Lua 5.0 , daher habe ich Lua 5.2 verwendet und direkt mit DllImport in C # verknüpft. Die Speicherlecks befanden sich in der LuaInterface-Bibliothek.
Lua 5.2: von http://luabinaries.sourceforge.net und http://sourceforge.net/projects/luabinaries/files/5.2/Windows%20Libraries/Dynamic/lua-5.2_Win32_dll7_lib.zip/download
Sobald ich dies tat, waren alle meine Speicherlecks verschwunden und die Anwendung war sehr stabil.
quelle
Die Hauptanwendung, die meine Abteilung verkauft, bietet Kundenanpassungen (was bedeutet, dass ich keine Quelle veröffentlichen kann). Wir haben eine C # -Anwendung, die dynamische VB.NET-Skripte lädt (obwohl jede .NET-Sprache problemlos unterstützt werden kann - VB wurde ausgewählt, weil das Anpassungsteam aus einem ASP-Hintergrund stammt).
Mit CodeDom von .NET kompilieren wir die Skripte aus der Datenbank mit dem VB
CodeDomProvider
(ärgerlicherweise wird standardmäßig .NET 2 verwendet. Wenn Sie 3.5-Funktionen unterstützen möchten, müssen Sie ein Wörterbuch mit "CompilerVersion" = "v3.5" an den Konstruktor übergeben ). Verwenden Sie dieCodeDomProvider.CompileAssemblyFromSource
Methode zum Kompilieren (Sie können Einstellungen übergeben, um das Kompilieren nur im Speicher zu erzwingen.Dies würde zu Hunderten von Assemblys im Speicher führen, aber Sie könnten den gesamten Code der dynamischen Klassen in einer einzigen Assembly zusammenfassen und bei jeder Änderung das gesamte Los neu kompilieren. Dies hat den Vorteil, dass Sie beim Testen ein Flag hinzufügen können, das auf der Festplatte mit einem PDB kompiliert werden soll, sodass Sie den dynamischen Code debuggen können.
quelle
Ja, ich habe darüber nachgedacht, aber ich habe bald herausgefunden, dass eine andere domänenspezifische Sprache (DSL) etwas zu viel sein würde.
Im Wesentlichen müssen sie auf möglicherweise unvorhersehbare Weise mit meinem Spielstatus interagieren. Zum Beispiel könnte eine Karte die Regel "Wenn diese Karten ins Spiel kommen, erhalten alle Ihre untoten Schergen +3 Angriff gegen fliegende Feinde, außer wenn der Feind gesegnet ist". Da Sammelkartenspiele rundenbasiert sind, löst der GameState Manager OnStageX-Ereignisse aus und lässt die Karten andere Karten oder den GameState auf die von der Karte benötigte Weise ändern.
Wenn ich versuche, ein DSL zu erstellen, muss ich einen ziemlich großen Funktionsumfang implementieren und möglicherweise ständig aktualisieren, wodurch die Wartungsarbeiten auf ein anderes Teil verlagert werden, ohne es tatsächlich zu entfernen.
Aus diesem Grund wollte ich bei einer "echten" .NET-Sprache bleiben, um im Wesentlichen nur das Ereignis auslösen und die Karte den Gamestate auf irgendeine Weise manipulieren zu lassen (innerhalb der Grenzen der Codezugriffssicherheit).
quelle
In der nächsten Version von .NET (5.0?) Wurde viel über das Öffnen des "Compilers als Dienst" gesprochen, der beispielsweise eine direkte Skriptauswertung ermöglichen würde.
quelle