Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Skript-Engine integrieren (https://www.delphipraxis.net/153978-skript-engine-integrieren.html)

I love Delphi 23. Aug 2010 17:39

Delphi-Version: 2006

Skript-Engine integrieren
 
Hallo Delphianer,
in den letzten Monaten habe ich die Grundlage für ein Lernspiel entwickelt, welches durch Skripts anpassbar sein soll. Allerdings bereitet gerade dies mir Kopf zerbrechen. Ich kenne keine Skript-Engine welche meinen Anforderungen (siehe unten!) entspricht, habe aber auch keine Ahnung wie ich so etwas selber schreiben sollte. Könne sollte sie folgendes:
- Funktionen und Prozeduren aufrufen (allerdings nur bestimmte)
- if-Bedingungen und while-Schleifen
- (kompatibel mit BDS 2006 Explorer)
- Zugriff auf Objekte und deren Attribute (ebenfalls nur bestimmte)
- Der Sprach Dialekt, bzw. die Syntax im allgemeinen ist mir egal. Mir wäre sowohl eine Pascal-, als aber auch eine JavaScript-Syntax recht

Da ich wie geasagt nix passendes kenne und bisher auch nicht gefunden habe nun meine Frage an Euch: Kennnt jemand eine passende Unit/Komponente, oder hat eine Idee wie man eine existierende Skript-Engine an diese Bedürfnisse anpassen könnte?

Würde mich über Beiträge sehr freuen!

Viele Grüße,
I love Delphi!

Florian Hämmerle 23. Aug 2010 18:41

AW: Skript-Engine integrieren
 
Tja alleine hier in der DP wurden in den letzten 12 Monaten 3 Skriptengines vorgestellt:

Von littleDave: Sygys-Skriptengine, SE2 (Hat alles was du dir wünschst :))
Von olee: RUTIS

Schau sie dir mal an

mfg Florian

mkinzler 23. Aug 2010 18:48

AW: Skript-Engine integrieren
 
Sonst halt Lua, Python, ...

Daniela.S 23. Aug 2010 19:01

AW: Skript-Engine integrieren
 
Oder Pascal Script

I love Delphi 23. Aug 2010 19:34

AW: Skript-Engine integrieren
 
Hallo,
also erstmal vielen Dank für Eure Antworten. Also Sysygys ScriptEngine kenne ich bereits und Pascal-Script habe ich mir auch gerade mal angeguckt. Allerdings habe ich bei beiden keinerlei Hinweise darauf gefunden, dass sich die Möglichkeiten auch einbschränken lassen. Zwar könnte man einfach das jeweilige Skript nach "verbotenen" Befehlen besuchen, da es ja jedoch fast unendlich viele Möglichkeiten gibt, wäre dies recht unsinnig. Man könnte es jedoch per "Whitelist" machen, allerdings wäre auch das sehr kompliziert. Weiß jemand von Euch also noch etwas zum Thema Beschränkung der Möglichkeiten? Ich möchte wie gesagt zum Beispiel nur, dass alle Methoden und Attribute eines bestimmten Objekts verfügbar sind.

Vielen Dank!

Mit freundlichen Grüßen,
I love Delphi!

Florian Hämmerle 23. Aug 2010 19:38

AW: Skript-Engine integrieren
 
Mit der SE2 von littleDave hat man nur Zugriff auf die Sachen, die du der Scriptengine zur Verfügung stellst. zB kannst du einen Button verfügbar machen, das Edit links davon aber nicht. Von Anfang an hat die SE2 keinen Zugriff auf dein Programm, erst wenn du ihr Sachen freigibst, kann sie darauf zugreifen.

mfg Florian

igel457 23. Aug 2010 19:40

AW: Skript-Engine integrieren
 
Wie verstehst du das mit dem Beschränken der Möglichkeiten? Ich weiß zwar nicht genau, wie das bei diesen Skriptengines gelöst ist, jedoch bin ich mir ziemlich sicher, dass die aufrufbaren Funktionen/Objekte/Typen zuvor bei der Engine registriert werden müssen. Solange du nicht die mitgelieferten Standardbibliotheken lädst, kannst du somit ganz genau bestimmen was getan werden darf und was nicht.

Lediglich die Funktionalität mancher Skriptsprachen externe Bibliotheken (DLLs) zu laden musst du irgendwie unterbinden.

Phoenix 23. Aug 2010 19:42

AW: Skript-Engine integrieren
 
Was PascalScript angeht kannst du da mal im Supportforum bzw. den Support newsgroups von RemObjects Nachfragen. Die Jungs da kennen sich aus ;)

littleDave 23. Aug 2010 20:12

AW: Skript-Engine integrieren
 
In meiner SEII kann man nur auf Objekte, Eigenschaften und Methoden zugreifen, die man selbst auch der Script-Engine zur Verfügung stellt.

Falls du meine Script-Engine ausprobieren willst, würde ich dir das PDF-File, welches sich in meinem Download befindet, empfehlen. Ich denke, dass es eine schnelle Übersicht über SEII geben sollte.

Von deiner Anforderungsliste wird von meiner Script-Engine alles Unterstützt - und sogar noch einiges mehr ;-)

I love Delphi 23. Aug 2010 20:33

AW: Skript-Engine integrieren
 
Hallo nochmal,
nochmals vielen Dank! Ich werde mich jetzt mal an littleDaves SEII "dran machen". Mein Kompliment an Dich, dass du sowas entwickelt hast :)
Wenn ich es eingebaut habe melde ich mich mal bei dir :D

Vielen Dank an Euch, ist echt ne tolle Community hier!

Viele Grüße,
I love Delph!

I love Delphi 24. Aug 2010 15:11

AW: Skript-Engine integrieren
 
So,
habe mich jetzt in das PDF zu SE2 eingelesen, das "einbauen" ist mir allerdings nicht geglückt. Zwar bekomme ich keinerlei Fehlermeldungen, dafür aber auch nicht das was ich will. Wenn ich die Dokumentation richtig verstanden habe, brauche ich doch eine Unit die die Prozeduren registriert und den Quelltext der, welche die external-Aufrufe beinhaltet, in einer Konstanten enthält. Auf die sollte ich dann während der Ausführung zugreifen können, richtig?

Mein Code:
Delphi-Quellcode:
unit Skripting;

interface

uses AdSprites, Dialogs, murmel, SysUtils, uSE2Compiler, uSE2UnitCacheMngr, uSE2Errors, uSE2Reader, uSE2PEData, uSE2RunTime, uSE2RunAccess, uSE2UnitManager, uSE2Consts, Murmelspiel_Form1_CODE;

  procedure Prepare();
  procedure ParseSkript(ASkript: String; AMurmel: TMurmel);
  procedure ZeigeNachricht(ANachricht: String);
  procedure Murmel_SetXSpeed(AXSpeed: double; AMurmel: TMurmel);
  procedure Murmel_SetYSpeed(AYSpeed: double; AMurmel: TMurmel);

const
// diese Variable mit dem Namen der Unit füllen
C_UnitName = 'SkriptCommands';
// in diese Variable kommt der komplette Script-Quelltext
C_UnitSource =
  'unit SkriptCommands;'+#13#10+ #13#10+
  'interface'+#13#10+#13#10+
  'uses Murmel, Skripting;'+#13#10+#13#10+
  'procedure ZeigeNachricht(ANachricht: String); external;'+#13#10+
  'procedure Murmel_SetXSpeed(AXSpeed: double; AMurmel: TMurmel); external;'+#13#10+
  'procedure Murmel_SetYSpeed(AYSpeed: double; AMurmel: TMurmel); external;'+#13#10+#13#10+
  'implementation'+#13#10+#13#10+
  'end.';

var
  Compiler : TSE2Compiler;
  RunTime : TSE2RunTime;
  PEData : TSE2PE;

implementation

procedure Unit_GetSource(var Target: string);
begin
  Target := C_UnitSource;
end;

procedure Unit_RegisterMethods(const Target: TSE2RunAccess);
begin
  if Target.HasUnit(C_UnitName) then
  begin
    // hier die Funktions-Pointer setzen
    Target.Method['ZeigeNachricht[0]', C_UnitName] := @ZeigeNachricht;
  end;
end;

procedure RegisterUnit;
var p : TSE2MethodUnit;
begin
  p := TSE2MethodUnit.Create;
  p.DoRegisterMethods := Unit_RegisterMethods;
  p.DoGetUnitSource := Unit_GetSource;
  p.UnitName := C_UnitName;
  TSE2UnitManager.RegisterUnit(p);
end;

procedure ParseSkript(ASkript: String; AMurmel: TMurmel);
begin
  //ShowMessage('Hallo');
  //Das Parsen übernimmt nun die S(cript) E(ngine) 2 (http://www.delphipraxis.net/140590-scriptengine-ii-v-0-5-3-2-a-4.html)
  Compiler := TSE2Compiler.Create;
  try
    PEData := Compiler.Compile(ASkript);
    if PEData <> nil then
    begin
      RunTime := TSE2RunTime.Create;
      try
        RunTime.AppCode := PEData;
        RunTime.Initialize;
        Pause:=true; //Spiel während Skript-Ausführung anhalten
        RunTime.Run;
        Pause:=false;
        RunTime.Finalize;
        RunTime.AppCode := nil;
      finally
        RunTime.Free;
      end;
    end;
  except
    ShowMessage('Beim Ausführen des Skripts ist ein Fehler aufgetreten!');
  end;
  PEData.Free;
end;

procedure Murmel_SetXSpeed(AXSpeed: Double; AMurmel: TMurmel);
begin
  AMurmel.XSpeed := AXSpeed;
end;

procedure Murmel_SetYSpeed(AYSpeed: Double; AMurmel: TMurmel);
begin
  AMurmel.YSpeed := AYSpeed;
end;

procedure ZeigeNachricht(ANachricht: string);
begin
  ShowMessage(ANachricht);
end;

initialization
RegisterUnit;

end.
Das hier wäre der Aufruf, der funktioniert jedoch:
Delphi-Quellcode:
Skripting.ParseSkript(Sprite.Skript, self);
Vielleicht habe ich auch einfach das Konzept dahinter noch nicht ganz verstanden... :(


Ich hoffe, jemand versteht mich.

Vielen Dank!

Viele Grüße,
I love Delphi!

EDIT: Als Skript habe ich folgendes verwendet:
Delphi-Quellcode:
program MyScriptProject;
uses
SkriptCommands;
begin
ZeigeNachricht('Hallo');
end.

Florian Hämmerle 24. Aug 2010 15:37

AW: Skript-Engine integrieren
 
Hi!

So auf die schnelle ohne Delphi kann ich dir auch nicht helfen. Aber ich habs so gemacht, dass ich erst ne ganz kleine Unit geschrieben habe und die dann getestet habe, ob alles funktioniert und erst bei erfolg hab ich die Unit erweitert. Vielleicht machst du das einfach nochmal von vorne und probierst nach jeder neuen Methode aus, ob noch alles funktioniert, dann kannst du den Fehler eingrenzen.
ODER: du schmeißt mal den Debugger an und steppst durch dein Spiel um zu schauen ob das Script überhaupt ausgeführt wird oder was los ist.

mfg Florian

I love Delphi 24. Aug 2010 15:44

AW: Skript-Engine integrieren
 
Hey,
also ausgeführt müsste es werden, das habe ich mit einem beispielhaften Aufruf von ShowMessage() in der Prozedure ParseSkript() getestet.
Leider wüsste ich gar nicht, inwiefern ich die Klasse noch reduzieren sollte: Ich habe lediglich die drei Funktionen/Prozeduren Murmel_SetYSpeed, Murmel_SetXSpeed und ZeigeNachricht() hinzugefügt. Wenn ich diese jedoch nicht mehr habe, dann kann ich ja die Funktionstüchtigkeit nicht mehr prüfen! Mist...

Viele Grüße,
I love Delphi!

littleDave 24. Aug 2010 15:49

AW: Skript-Engine integrieren
 
Ok, ich versuche mal eine Schnell-Anleitung zu geben, wie man das ganze am schnellsten macht (nicht abschrecken von den paar Punkten - habe versucht es möglich genau zu machen.
  • Öffne die Beispiel-IDE, die mit im Download-Paket im Ordner "IDE" enthalten ist
  • Erstelle ein neues Projekt (ist egal wie es heißt)
  • Klicke mit der rechten Maustaste im Tab "Project" auf den oberen Eintrag in der TreeView und wähle den Eintrag "Add new Unit"
  • Gib den Unit-Names "SkriptCommands" ein und drücke ok
  • Wiederhole das ganze mit "Murmel" und "Skripting"
  • Nun kannst du die Units per Doppelklick auf den entsprechenden Eintrag öffnen
  • Fülle den Inhalt der Units entsprechend aus (also das, was bei dir in der Konstante C_UnitSource steht
  • Wiederhole das mit "Murmel" und "Skripting"
  • Gehe nun in den ersten Tab bei der Quelltext-Liste (da wo die Units offen sind) (program ... etc.)
  • Füge deine hinzugefügten Units in die Uses-Liste hinzu (es sollte keine uses-Liste vorhanden sein, also einfach ganz normal erstellen)
  • Nun solltest du kompilieren können.
  • Jetzt wählst du wieder den Tab der Unit "SkriptCommands" aus.
  • Dann klickst du im Hauptmenü unter "Project" auf "Generate Unit" -> "Application Unit"
  • Es wird ein neuer Tab erstellt, in dem der komplett fertige Delphi-Quelltext drinnen steht. Diesen kopierst du einfach in die Zwischenablage und fügst ihn in eine neue Unit in deinem Projekt hinzu. Wahrscheinlich musst du noch ein paar uses-Sachen für die entsprechenden Typen anpassen, aber und vielleicht ein paar Methoden - aber es sollte eigentlich funktionieren ;-)
  • Wiederhole die letzten 3 Punkte mit den Units "Murmel" und "SkriptCommands"

Die neuen Units, die du gerade erstellt hast, solltest du nicht mehr groß erweitert. Die sind nur dafür da, um die Script-Engine mit deinen Methoden usw. zu füllen.

Jetzt baue ich dein Beispiel-Script nach, wie es eigentlich funktionieren sollte (hab gerade kein Delphi zur Hand, daher ist alles nur ausm Kopf heraus geschrieben)

Erstelle in deinem Delphi-Projekt eine neue Unit und schreibe folgenden Quelltext hinein:
Delphi-Quellcode:
unit [was auch immer]

interface

procedure RunScript(const ScriptSource: string);

implementation

uses
  uSE2Compiler, uSE2RunTime, uSE2PEData;

procedure RunScript(const ScriptSource: string);
var Compiler: TSE2Compiler;
    PEData : TSE2PE;
    RunTime : TSE2RunTime;
begin
  Compiler := TSE2Compiler.Create;
  try
    // Compilieren
    PEData := Compiler.Compile(ASkript);
    if PEData <> nil then
    begin
      // Compilieren erfolgreich - PEData muss nicht manuell
      // freigegeben werden, da die RunTime das für uns erledigt.
      // Daher gibt es hier für PEData keinen try-finally-Block
      RunTime := TSE2RunTime.Create;
      try
        RunTime.AppCode := PEData;

        // RunTime initialisieren
        RunTime.Initialize;
        // ausführen
        RunTime.Run;
        // aufräumen
        RunTime.Finalize;
      finally
        // wie gesagt, PEData wird hier mit freigegeben
        RunTime.Free;
      end;
    end;
  finally
    Compiler.Free;
  end;
end;

end.
Folgende Punkte sind im PDF für dich noch wichtig:
  • 2.2 Auf Compiler-Fehler reagieren
  • 2.5 Auf Laufzeitfehler reagieren - denn dein (except-block mit ShowMessage('Beim Ausführen des Skripts ist ein Fehler aufgetreten!'); wird nur bei wirklich fatalen Fehlern erreicht - alle Exceptions innerhalb eines Scriptes (z.B. Dividieren durch 0) müssen anders behandelt werden

Was ich auch noch erwähnen wollte: die SEII ist nicht dazu optimiert, ständig den Quelltext zu kompilieren und diesen dann direkt auszuführen. Die SEII ist für das Ausführen von Scripten extrem optimiert (schneller als Pascal-Script, Sysygy, Delphi++, etc...). Daher wäre es sinnvoll, dein Script einmal durch den Compiler zu jagen und dir das TSE2PE-Objekt zu merken. Aber das nur so am Rande, jetzt schau ma erstmal, dass dein Basis-Ding funktioniert ;-)


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:09 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz