Compatibility & conformance
“What your designers saw is what your players get” is only worth something if it’s actually true. There’s one versioned contract, and one shared set of tests every runtime has to pass, so this is something you can check, not something you hope for.
The bundle schema is the contract
Section titled “The bundle schema is the contract”A compiled bundle declares a schema version (storylets/bundle@N). A
runtime plays any bundle whose schema it supports. That version is the one thing that cuts
across everything: bumping it is the one change that moves every runtime together. Each
runtime, the editor and the CLI otherwise version on their own.
| Runtime | Ships as | Get it |
|---|---|---|
| Storylet Engine JS | Release zip: @storylet-studio/runtime, @storylet-studio/play-helpers and a browser drop-in |
Download |
| Storylet Engine Unity | Release zip: the package folder and a demo project | Download |
| Storylet Engine Unreal | Release zip: the plugin folder and a demo project | Download |
| Storylet Engine Godot | Release zip: the addon folder | Download |
storyletengine CLI |
Standalone binaries, one per platform | Download |
The shared test suite
Section titled “The shared test suite”Every runtime is checked against one shared suite: a single language-neutral set of cases that pins the exact behaviour a conforming engine must produce. It covers:
- Expressions: the evaluator, the expression dialect, and the seeded random-number generator, giving identical results everywhere.
- Specificity: the score that decides which of two matching cards asked for more.
- Peeks: a peek returns an exact ordered list, and peeking twice returns the same list, because a peek changes nothing.
- Whole runs: dealing, the board, playing outcomes, state writes, turns and cooldowns, save and load round-trips, and reset.
Each runtime ships a small test host that replays those cases in its own language and checks it gets the same answers, down to the random draws. Runs are seeded, so “the same seed deals the same cards” holds across JavaScript, C#, C++ and GDScript alike. The suite runs on every release, and a release doesn’t go out on a runtime that fails it.
Saving is part of the contract too. Loading a save made against edited content is checked: state belonging to something that no longer exists drops harmlessly, never a crash.
Per-engine differences
Section titled “Per-engine differences”Every runtime carries the same API and the same dev tools. A few places differ because the host language differs, and these are the only ones:
- Godot has no exceptions. Errors come back as values:
play(),load()andset_property()return an error string. The state kernel’s accessors areget_valueandset_value, becausegetandsetcollide with Godot’s ownObjectmethods. - Unbounded slots are the string
"unbounded"in JavaScript and positive infinity in the native ports. Every engine has a label helper, so a view prints the same text either way. - Unreal Blueprint gets typed property accessors instead of one generic value pin, and polls the flow’s log instead of subscribing to a trace delegate. Both surfaces are available in full from C++.
What this means for you
Section titled “What this means for you”You ship on one engine. That’s why you can trust that engine: it plays your project exactly as Storyletter’s Board does, the same cards in the same order, the same conditions, the same saves, right down to the random draws. There’s no “works in the editor, behaves differently in my game” gap to chase.
It isn’t “should match”. It’s checked, case by case, on every release.
→ Back to Playing in your game.
MIT-licensed open source · Made by Ian Thomas · storylet.studio