inkle / ink

inkle's open source scripting language for writing interactive narrative.
http://www.inklestudios.com/ink
MIT License
4.15k stars 495 forks source link

C++ compatible Ink library #465

Open fire opened 6 years ago

fire commented 6 years ago

Any suggestions how to use Ink and a native c++ engine?

I imagined porting https://github.com/premek/pink to c++ via https://github.com/yhirose/cpp-peglib.

Any other ideas?

The game engine is Godot Engine, but I don't use C#. https://github.com/paulloz/godot-ink and it prevents exports to html5 and mobile devices.

joethephish commented 6 years ago

The only ports we'd recommend using are inkjs (in JavaScript) and blade-ink in Java. So your best route is to embed the runtime for one the languages within C++. For the original C# code this would mean embedding Mono as was done in the Unreal demo or a JavaScript engine such as V8.

But given you say you're using Godot and don't want to embed C#... you're probably out of luck? Porting the full runtime library to a new platform is a significant amount of work I'm afraid!

fire commented 6 years ago

If I decided to port it, is there a test suite?

joethephish commented 6 years ago

Yup: https://github.com/inkle/ink/blob/master/tests/Tests.cs On 29 Aug 2018, 21:25 +0100, K. S. Ernest (iFire) Lee notifications@github.com, wrote:

If I decided to port it, is there a test suite? — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

Skrylar commented 6 years ago

It's not exceptionally difficult to write an interpreter in GDScript or Rust (have done both, but they need polishing), but:

For instance the bytecode isn't laid out in one giant blob with a label->offset index like most bytecode, things are separated in to nested containers. In my Rust work, the loader flattens this out so all containers are stored in a single array and nested containers are replaced with a reference. I also store upwards links and the container's position within its parent.

In GDScript and others, you don't necessarily have to do this flattening stage. You probably want to, and the reason its done in Rust is because arenas are more efficient and memory safe than the alternatives of smart pointers or "unsafe" code.

The self position and parent references are necessary because Inklecate often emits sub-containers for no purpose other than acting as a jump target, or jumps somewhere that doesn't return, and you have to interpret the end of the array as an implicit "jump up and set program counter to my position in the parent + 1."

Names are stored in two formats varying by context. Some elements are named by their parent, some are named by a special "#n" tag. I also flatten this out with the loader, it calculates based on context and maintains a key/value map of names to the container ID (remember that every container is flattened to have a unique ID in the arena.)

If you're writing your own, pre-processing is key. You can try to skip preprocessing and operate off raw json (I did this in GDScript) but you will need a lot of coffee and replacement hair to pull out when debugging. The GDScript version had to store a stack of json references to jump back to parents, the Rust version has only a single "this container, this offset" iterator.

fire commented 6 years ago

I was attempting to write a parser for the raw ink format, but working off the json format works too.

Copying the approach of https://github.com/premek/pink.

Hm.