1

I'm currently implementing a script engine in a game I wrote using the C# "dynamic" feature.

How the system should work is when a script is called, it should register the events it listens for, then return control to the application. Then when an event that the script is listening for is fired the script should execute. One thing I'd really like to implement in the script engine is to have methods with certain names automatically bind to events. For example, the onTurnStart() listener should automatically bind to the turnStart event.

The scripts will mostly need to execute existing methods and change variable values in classes; stuff like player.takeDamage() and player.HP = somevalue. Most scripts will need to wait for the start of the players' turn and the end of the players' turn before being unloaded.

The complicated part is that these scripts need to be able to be changed without making any code changes to the game. (Security aside) the current plan is to have all the script changes automatically download when the game starts up to ensure all the players are using the same version of the scripts.

However I have three questions:
1) How do I register and unregister the script event listeners?
2) Can dynamic code listen for events?
3) (How) can I register events dynamically?

This is my first time using C#'s dynamic feature, so any help will be appreciated.

Thanks
--Michael

edude05
  • 744
  • 1
  • 10
  • 23

1 Answers1

2

I'm not sure you've got the right end of the stick with the dynamic keyword. It doesn't by itself let you interpret new code at runtime. All it does it let you bypass static type checking by delaying the resolution of operations until runtime.

If you're looking to "script" your game, you probably want to take a look at integrating Lua, IronPython, or one of the other DLR languages:-

Otherwise, the usual thing to do is have something along the lines of:-

interface IBehavior
{
  // Binds whatever events this behaviour needs, and optionally adds
  // itself to a collection of behaviours on the entity.
  void Register(Entity entity);
}

// Just an example
public abstract class TurnEndingDoSomethingBehavior
{
  public void Register(Entity entity)
  {
    entity.TurnEnding += (s, e) => DoSomething();
  }

  private abstract void DoSomething();
}

The question is, do you want to be able to add entirely new behaviours after compile-time? If so you'll need to expose some or all of your game-state to a scripting language.

Or is it sufficient to be able to compose existing behaviours at runtime?


After your edit

I'm still unsure, to be honest, about your requirement for the dynamic keyword and the DLR. Your game's launcher can download a class library full of behaviours just as easily as it can pull down a set of scripts! (That's what Minecraft's launcher does if memory serves)

If you absolutely must use the DLR then take a look at the links I posted. You'll have to expose as much of your game state as necessary to one of the DLR languages. Events get exposed as first-order-function properties. You shouldn't even need the "dynamic" keyword for basic stuff.

Quick example in IronPython:-

def DoSomethingWhenDamageTaken(*args):
  #do whatever...

player.DamageTaken += DoSomethingWhenDamageTaken

The player class:-

public class Player
{
  public event EventHandler DamageTaken;

  // ...
}

You set it up like:-

ScriptEngine engine = Python.CreateEngine();
ScriptRuntime runtime = engine.Runtime;
ScriptScope scope = runtime.CreateScope();

// In an actual application you might even be
// parsing the script from user input.
ScriptSource source = engine.CreateScriptSourceFromFile(...); 

Player p = new Player();
scope.SetVariable("player", p);
source.Execute(scope);

Some links to get you started:-

Community
  • 1
  • 1
Iain Galloway
  • 18,669
  • 6
  • 52
  • 73
  • I've edited the original post to make most post more clear. Thanks for the feedback so far. – edude05 Dec 20 '10 at 16:53
  • This post is so loaded with code and examples, that I wish I could give you my rep for all your help. – edude05 Jan 03 '11 at 01:02