Gameplay scripting

This post is very old. Please bear in mind that information here might be incorrect or obsolete, and links can be broken. If something seems wrong, please feel free to comment or contact me and I'll update the post.

For my engine, I want to use a scripting language. The primary purpose for employing a scripting language is to simplify the development of gameplay code and leverage the power of multi-core systems. I’ve been lately a bit more serious about it (and yes, the packages are still in development, good stuff takes time) and did some more research. Read on for design goals, parser generators and general rambling on programming languages. By the way, this is the 100th entry in my blog :) Hooray!

Design goals

The scripting language should first and foremost allow designers and artists to write gameplay code, not more, not less. It does not have to be a fully features scripting language like Python. The focus is on event-driven, mostly parallel gameplay code. This brings several requirements with it:

  • Event-driven code has to be supported natively. Basically, there has to be some keyword for events as everything in niven is going to be event-driven.
  • Parallel execution. The resulting code should run well on a parallel ScriptVM.

Of course, if most of your code is event-driven, parallel execution comes for free as long as your objects are independent of each other. The most simple solution is to stack up all events for an object and then process the objects in parallel. This is completely transparent to the user and does not even require special language support. This means also that event raising will be asynchronous, but that seems rather logical to me.

More design goals will probably emerge as soon as I get a prototype working, but these are the primary ones for now.

Syntax

A word on syntax, what kind of syntax do you prefer given the following choices?

  • function int a (int b)
    { ... }
  • function a (b: int) : int
    { ... }

    or a slight variation, with a more mathematic syntax:

    function (a:int, b:int) -> int
    { ... }
  • function name:a params:{a:int, b:int} return:int
    { ... }

    More verbose, but very friendly to annotations. Imagine if you could add other “attributes” to a function like function name:qsort implements:sort-algorithm complexity:nlogn ... and get it via sort-algorithm.get("complexity","nlogn")?

A note on the last one: I have some idea of something I call a “Rich metadata engine” which would allow adding attributes to each parameter, variable or function. For example, const and reference would become attributes and passed over to the compiler. This would allow compilers to support new attributes (say, checks like [>10]) which could lead to better optimization and better compile-time checks. Image the example function a (q:array [const,ref], i:int [const,=size(q)]).

Parser generators

To make all this stuff happen, I have to write a powerful script VM and a good compiler, and both tasks are not simple.

Pyl

For the compiler, I was playing around with the idea to write it in Python, but unfortunately parser generators for Python are not very powerful (the best I’ve found was Pyl, a yacc port to Python) and transforming ASTs in Python is painful because Python does not know call-by-reference. Anyway, for quick prototyping it’s really good, I managed to get a compiler with constant-folding up & running in 2 hours for a simple test “language” (which understood assignment to variables and basic maths).

Boost.Spirit

Next try was Boost.Spirit, truly impressive, took me also around 2 hours to get a sample running (this time not written from scratch though) and write a simple compiler. Spirit is probably better suited for simpler parsing tasks, although I’ve seen some impressive parsers written with it. I wouldn’t do it because although there is something beautiful about embedding EBNF in C++ I still see the advantage of writing real EBNF and transforming to ugly C++ ;) which leads me to the last tool I’ve tried, ANTLR.

ANTLR

It comes with a truly amazing IDE (yeah, you read right. It really has an IDE for writing parsers), it supports LL(*) grammars (no left-recursion though, but the IDE has a built-in function to refactor left-recursive rules) and it generates ASTs if you want it to. Unfortunately, the current version is under heavy development and not finished yet, meaning I didn’t have the chance to play with the C++ AST yet. Anyway, I’ve been able to implement a rather large grammar already in no time, and I’ll stick with ANTLR until something better pops up which is kinda unlikely.

Related posts:

  1. Thoughts on scripting
  2. VTK and Python …

This entry was posted in Programming and tagged , , . Bookmark the permalink.

3 Responses to Gameplay scripting

  1. hehejo says:

    Zwischen den beiden erste Syntaxen (?) sehe ich keinen großen Unterschied. Wäre ja lediglich “Syntax”. haha
    Naja, erste Variante gefällt mir atm besser – ist halt gewohnt.
    Die Dritte Variante ist auch nicht schlecht – aber eben ewig viel Tipparbeit. Durch die komplette Auszeichnung der Elemente, könnte man – wenn es der Parser erlaubt – ja auch die Elemente vertauschen:
    function params:{a:int, b:int} name:a return:int
    Aber ob das Sinn macht wage ich zu bezweifeln. Außer das der Parser so bisschen komplexer wird und jeder seinen Misch schreiben kann und nivenScript unübersichtlich wird …
    Die andere Sache mit den Metainformationen ist nicht schlecht. Aber dann würde ich dennoch nur diese extra auszeichnen.
    vll in der Art:
    function int a (int b) [implements:sortalgorithm,complexity:nlogn] {..}
    Und dann schaut eine normale Funktion ohne besonder Auszeichnungen eben so aus:
    function int a (int b) [] {..}

    Naja: It’s up to you!

  2. Anteru says:

    Zu #1 und #2: Einmal ist es halt C style, einmal angeleht an eine mathematische Funktion (und JScript 2 wenn ich deren Syntax richtig verstehe).

    Zu den “Rich meta” – die mit [] anhängen tät ja nicht bedeuten dass man function int a (int b) [] {} schreiben muss wenn nix drin steht weil das ganze [] ja optional ist. Die Sache bei #3 ist halt wirklich dem Compiler einfach nen Textblock zu geben mit dem er dann arbeiten kann. Die Reihenfolge kann der Compiler dann ja wenn er mag ankreiden oder auch nicht.

    Mir ist das mit den Metainformationen aber schon recht wichtig, und extra die Typdeklarationen aufblähen halte ich für unnötig. Es ginge ja auch bei (int [const, ref] b), bei (b : int [const, ref]) finde ich wirds ein wenig klarer, man sagt ja auch die Funktion nimmt ein Argument B vom Typ int (z.B. in Mathe ;) ). Aber wie gesagt, dass ist momentan alles im Fluss und es könnte sehr wohl sein dass sich da noch ne Menge tut.

  3. hehejo says:

    Ja, so gesehen ist b:int schon “besser” oder deutlicher.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>