The Dink Network

Dink with Lua scripting, first public alpha

May 24th 2014, 04:57 AM
wizardb.gif
Phoenix
Peasant He/Him Norway
Back from the ashes 
Hello, Dink gang.

I've completed implementing Lua in Dink, at least as much as is necessary to re-create the first "chapter" of the original Dink adventure in Lua, which I've also done. (If this sounds all new to you, feel free to check out the original discussion thread first.)

Using this alpha requires you to be rather skilled with computers, so I'm not expecting all that many people to use it. Stick around, nonetheless if you're not skilled with computers, as I will be posting DinkLua scripts that you can all look at to get a feel for how those will be like.

I know there are some people who use the Dink Network who have the skill set required to test this, so I hope to get at least a few testers. You're going to have to download and build my FreeDink version yourself. If you've ever done "./configure; make; make install" before, you know what it's about. If you do find any bugs, or have any issues, please report them on the issues page on my GitHub project.

I'm currently working on writing some documentation for my project's Wiki. Among other things, it will document the main differences between DinkC and DinkLua (apart from them being completely different languages, naturally), and I will also try to write a small Lua primer/tutorial for newcomers to the Lua language.

Anyway, without further ado, feel free to download
- The FreeDink with Lua source code (./configure; make; make install) Alternatively, you can get the source code with git from my project page on GitHub.
- The Lua version of the first chapter of the Dink adventure. Replace the Story folder of the "dink dmod" with this one to test the DinkLua version. (If you want to be sure that only Lua scripts get loaded by the Dink engine, run it with the "--disable-dinkc" parameter.)
May 24th 2014, 08:43 AM
farmer.gif
beuc
Peasant He/Him France
 
Hi,

Have you made progress on the automated testing (regression tests) side?
I remember we discussed it was a priority to avoid the current situation in DinkC were the slightest fix breaks existing D-Mods :/
I've been looking a the 'check' testing framework (http://check.sf.net/) which sounds nice enough.
May 24th 2014, 07:53 PM
custom_iplaydink.gif
iplaydink
Peasant He/Him Sweden
Hmm.. 
Cool! I'm looking forward to playing with this when I get time
May 25th 2014, 12:09 AM
custom_king.png
redink1
King He/Him United States bloop
A mother ducking wizard 
Could you post a sample lua script, or link to one? (I tried navigating github, but I didn't find any lua scripts in my traversal). It would be interesting to look at.

When I helped make the updates for 1.08, 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).

Edit: or I could actually read your post and look at the copious amount of examples in the other thread. Never mind!
May 25th 2014, 02:34 AM
wizardb.gif
Phoenix
Peasant He/Him Norway
Back from the ashes 
Also, the last link in my post is a tarball of scripts, in effect re-implementing the first "chapter" of the original Dink adventure in Lua. My "favorite" script in that collection is my "_cutscene.lua" script, which was my least DinkC-like script in the whole collection. I wrote it to cut down on the amount of typing you'd have to do for cutscene scripting. To see a good example usage of its features, take a look at "s1-wiz.lua".

There is one Lua script in the GitHub project, the bootstrapping/initialization script, which does the work of tying together Lua and the Dink Engine.
May 25th 2014, 02:46 AM
wizardb.gif
Phoenix
Peasant He/Him Norway
Back from the ashes 
Hello!

I do recall our discussion of regression tests, and I also recall saying how I wouldn't know what to test for, because I am not particularly familiar with either DinkC or its fragile reliance on the Dink Engine. I've only done my best to not modify DinkC as best I could. Most importantly, I've left the parsing code ENTIRELY alone.

There has been some refactoring besides that, but mostly I've just moved the DinkC-specific code verbatim into a new function in a new file, and then called it directly from its old location. I did this with code I considered "shared" between DinkC and Lua just to not have the Lua part of the code rely on the DinkC part of the code.

If you know what to test for, you may feel free to write some of these tests, or at least tell me what things to look for so that I can write them myself. I've come to a point where I really don't want to deal with DinkC ever again... biggest part of why I wrote this Lua implementation, to be honest.

In the meantime, or if you're not interested in merging my code into yours because of worries over breaking DinkC, I will just keep a separate version of Dink going where DinkC backwards compatability isn't a concern, and then people will just have to use two different executables for DinkC mods and Lua mods. I obviously favor a merge, but it's not paramount.
May 25th 2014, 12:32 PM
farmer.gif
Beuc
Peasant He/Him France
 
Well, the first thing you need to test is what you add - aka regression tests for Lua support.
Then I believe you have the skills to implement more tests without my listing them E.g. tests for the bugs we had to fix in the past.
Hmm, Lua as yet another backward-incompatible Dink variant that will end up unmaintained? I thought you were aiming higher buddy
May 25th 2014, 02:01 PM
peasantm.gif
shevek
Peasant They/Them Netherlands
Never be afraid to ask, but don't demand an answer 
Very cool! I've reported an issue which seems to have to do with the way I installed gnulib into the source; using your tarball it works fine.

Trying to play through the first part using your scripts, I get an error which is in the lua code, so it doesn't belong on github:

[ERROR] Error while resuming script s1-h1-o.lua:
[ERROR] ./Story/_cutscene.lua:256: Unknown scene command ssa

Comparing it to s1-wiz.lua, the "ssa" in s1-h1-o.lua:54 should be "ass".
May 25th 2014, 05:52 PM
wizardb.gif
Phoenix
Peasant He/Him Norway
Back from the ashes 
Correct. The "ass" command used to be named "ssa", and I guess I never fixed it in that script. I'll upload a fixed archive. It's also showing a small bug in my _cutscene.lua file: The error is supposed to refer to the point in the s1-h1-o.lua file, not the _cutscene.lua file.

Thanks for reporting.
May 26th 2014, 01:30 AM
peasantm.gif
shevek
Peasant They/Them Netherlands
Never be afraid to ask, but don't demand an answer 
Ok, now I finished this "demo". I found another bug: s1-nut.c sets global.nuttree to 3 instead of 1, which means that Milder's love-cutscene is skipped. (Of course, with --disable-dinkc it's skipped anyway. )

I liked your ending text. I was about to fast forward it when I noticed it wasn't saying the usual things.

A question: in lua, it would be easy to have a two tables for the inventory instead of DinkC's limited functions. Then it would be possible to arm a specific item, for example, without knowing the history of what was added and removed. It seems you just used the functions so far. Would you consider changing that?
May 26th 2014, 04:01 AM
wizardb.gif
Phoenix
Peasant He/Him Norway
Back from the ashes 
Give me a usage code example of how you'd like it to work, and I'll try to make it so. I'm pretty sure you can already arm a specific item without knowing the history... couldn't you just do this?

local item = dink.get_item("item-scriptname")
if item ~= 0 then
  global.cur_weapon = item
  dink.arm_weapon()
end


Not saying I won't add your requested feature, I'm just checking if this would do what you're asking for.
May 26th 2014, 04:25 AM
wizardb.gif
Phoenix
Peasant He/Him Norway
Back from the ashes 
I've done some reading on the topic, and I can't really figure out where to start. I can't help but notice you haven't added any regression testing in your code either. If you had already added some tests, I would find it a lot easier to get started, seeing how I could just base my tests off yours, but so far, there are none to base mine off of.

All examples of testing I've seen say how to test libraries, not executables. Again, as I've said, I can't fully figure this out, but if you're willing to help get me started, I'll do the brunt of the work. You seem to have a knack for figuring out how to do these things; I'll freely admit it's not my strong suit.

As for my version being unmaintained, that's a bit presumptuous. As long as there's interest from the community, I'll do my best to keep it updated. I am aiming and hoping for eventual "official" FreeDink inclusion, but it's not the end of the world if it doesn't happen. One of the major advantages to free or open source software is that there is no central authority; nothing can stop me from making, maintaining and releasing my version as I please.

Speaking of which, I don't suppose I should really be using the GNU name in my not-officially-released/endorsed-by-GNU releases... do you know anything about relevant policies regarding that?
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);
};
May 26th 2014, 01:27 PM
peasantm.gif
shevek
Peasant They/Them Netherlands
Never be afraid to ask, but don't demand an answer 
get_item is not in my DinkC reference (3.1, and there's more that isn't in there), so I didn't know it existed. Yes, this seems to allow what I asked for.

Anyway, I'd still like to have something like dink.items and dink.magic, which are tables with 16 and 8 elements respectively, each element being a table of 3 elements: script name, sequence and frame.

It may be more useful for debugging than anything else, especially given that get_item exists, but debugging is a good thing to support, too. However, that means that it's also quite acceptable (and probably easier to implement) to define get_inventory and get_magic functions which return such tables. The main difference would be that these returned tables cannot be used to make changes to the active state.

By the way, do you support global variables of non-int type (obviously non-saved and lua-only)? (The wiki isn't very informative yet. )
May 26th 2014, 02:02 PM
wizardb.gif
Phoenix
Peasant He/Him Norway
Back from the ashes 
I'll see about adding those readonly debugging tables for you.

Are you talking about the Wiki where I just finished adding the document that describes the differences between DinkC and DinkLua? The answer to your question is actually in there. (But TL;DR version: Yes, I do support such global non-int, non-saved, Lua-only variables.)
May 26th 2014, 05:51 PM
wizardg.gif
leprochaun
Peasant He/Him Japan bloop
Responsible for making things not look like ass 
get_item exists? Really? That seems strange to me. Not the command itself but rather the name of it. It feels like it should be get_weapon instead. You sure that's not what it is?
May 26th 2014, 06:13 PM
custom_magicman.gif
magicman
Peasant They/Them Netherlands duck
Mmmm, pizza. 
The names of those functions aren't really consistent. It's get_item(), to match add_item(), count_item(), kill_cur_item(), free_items(), and kill_this_item(). But there's also compare_weapon(), arm_weapon(), and the variable is &cur_weapon.
May 27th 2014, 12:59 AM
peasantmb.gif
yeoldetoast
Peasant They/Them Australia
LOOK UPON MY DEFORMED FACE! 
What about a DinkC-based test suite? A collection of DinkC and/or Lua scripts that tests each of the various functions in the engine that produce an expected outcome along with edge cases that produce weird results, which could then be packaged up into a DMOD.
May 27th 2014, 01:11 AM
custom_coco.gif
Cocomonkey
Bard He/Him United States
Please Cindy, say the whole name each time. 
What does get_item do?
May 27th 2014, 01:23 AM
wizardg.gif
leprochaun
Peasant He/Him Japan bloop
Responsible for making things not look like ass 
Based on the piece of code shown above, it seems to find an inventory item with a specified script and sets a variable to the found item's inventory number(1-16). If it doesn't exist in the inventory it will return 0.
May 27th 2014, 08:05 AM
wizardb.gif
Phoenix
Peasant He/Him Norway
Back from the ashes 
I tried making you guys a Windows version last night, but it turned out to be very difficult. While Beuc has been very helpful in working out how to do it, it wasn't enough to get me to the finish line. After lots of strange, unexplainable error messages and things not being detected correctly, I gave up on trying for now. I'm sorry, y'all.