The Dink Network

Reply to Re: Dink with Lua scripting, first public alpha

If you don't have an account, just leave the password field blank.
Username:
Password:
Subject:
Antispam: Enter Dink Smallwood's last name (surname) below.
Formatting: :) :( ;( :P ;) :D >( : :s :O evil cat blood
Bold font Italic font hyperlink Code tags
Message:
 
 
May 26th 2014, 06:01 AM
wizardb.gif
Phoenix
Peasant He/Him Norway
Back from the ashes 
I remember thinking about how to use python or c# instead of DinkC, and getting conceptually stuck, because of how DinkC was tied to the engine (things like wait, say_stop, and move_stop seemed impossible to replicate with another language).

Yeah, this exact issue was the thing that took me the longest to figure out how to accomplish. The problem, basically, boiled down to "how can I get the script to stop running, return control to the engine, and then resume running from where it stopped, once the engine is done doing its thing?"

At first, the problem seemed insurmountable. After all, once you fire off a Lua script or function, it wants to run to completion before returning control to you. I'm assuming the same goes for most other languages too, such as Python. I didn't want to modify how the engine worked, which would've been one way to get around the problem, by having features like wait or say_stop call back into the engine to let it do its thing. That sounded like a call stack recursion nightmare just waiting to happen.

The salvation came when I learned of Lua's coroutines. Basically, you can fire off a function as a coroutine, and this function can then yield back to its caller, which can do whatever it needs to, and then resume the coroutine where it left off at a later time. All DinkLua scripts are run as coroutines, and while the DinkLua scripts don't have direct access to the coroutine library, the C/Lua glue code yields at all the relevant places, making it just work™.

I see that Python also has coroutine functionality, so while I haven't taken a very close look at it, I wouldn't be surprised if Python could also be implemented as a Dink scripting language. In fact, during my pursuit of adding Lua to the Dink Engine, I made it script-language agnostic and easily extensible, so that while I had to do quite a bit of work to add Lua to it, adding additional languages will be a much simpler task. The most time-consuming factor at this point will be creating the "glue" code that lets the script code run the various, relevant engine functions... and I can think of ways to make that task simpler in the future as well.

If you look in scripting.h, it has this struct in it, which is "all" you really have to implement to add a new script language (not counting all the glue code, that is.):


struct script_engine
{
  /*
   * Sentinel entry indicator (should always be true on non-sentinel entries)
   */
  int active;

  /*
   * Name of the script engine.
   */
  char *name;

  /*
   * Array of file extensions supported by engine, don't forget NULL sentinel
   * This array and its entries should all be allocated in such a way that they
   * can be free'd safely. In other words, use malloc or strdup or other
   * functions that return free-able pointers.
   */
  char **extensions;

  /*
   * Create pointer to engine-specific data storage, accessible through
   * sinfo[#]->data later. Returning 0 indicates an error condition. Setting
   * *data to NULL in this function results in free_data() never being called.
   */
  int (*allocate_data)(void **data);

  /*
   * Free data previously created by allocate_data(); If you don't use the data
   * field (i.e. *data is set to NULL in allocate_data()), this pointer can be
   * left unset.
   */
  void (*free_data)(void *data);

  /*
   * Called to load a script, receives the path to the script, and the script
   * number this script is assigned to. Returning 0 indicates that an error
   * occurred during the script loading.
   *
   * At the point this function is called, sinfo[script] is initialized, as is
   * sinfo[script]->data, using the allocate_data() function.
   */
  int (*load_script)(const char *path, int script);

  /*
   * Nothing has to be done in this function, but it's a way for you to clean
   * up things you hold on to associated with the script that is not in
   * sinfo[script]->data (which is cleaned up automatically, using the
   * free_data() function)
   */
  void (*kill_script)(int script);

  /*
   * Return 0 if the procedure does not exist in the script, otherwise,
   * return 1. Not that this function is not necessarily called before
   * run_script_proc() is called, and only exists because the Dink engine
   * occasionally makes decisions depending on whether a script function
   * exists or not before actually running the function.
   */
  int (*script_proc_exists)(int script, const char *proc);

  /*
   * Run the procedure in the given script. The return value of this
   * function should be as with script_proc_exists. In addition to
   * checking for existence, however, this function also runs the procedure.
   */
  int (*run_script_proc)(int script, const char *proc);

  /*
   * Resumes a script after the script has yielded.
   */
  void (*resume_script)(int script);
};