Skip to content
Runtime State & Snapshot API

Runtime State & Snapshot API

NovaMark exports runtime state to clients through a unified interface. Renderers, GUIs, and debugging tools should read variables, inventory, text configuration, and definition metadata from this interface rather than relying on inline UI description syntax.

NovaMark no longer directly controls HUD or other client interface elements through scripts. Developers should decide UI layout and refresh timing based on runtime data such as variables, inventory, and dialogue.

Runtime State JSON

Current Web/WASM export interface:

const char* nova_export_runtime_state_json(void* vm, size_t* outSize);

This interface now acts as a unified presentation snapshot:

  • it preserves all previously exposed fields
  • it adds renderer-facing presentation fields
  • hosts no longer need to reconstruct the full visual state from many individual getters in the common case

In other words, hosts are now encouraged to consume this JSON snapshot first, while the old fine-grained getters remain available for compatibility and low-level optimization.

Example return structure:

{
  "status": 0,
  "runtimeStateVersion": 12,
  "runtimeStateChangeFlags": 0,
  "currentScene": "scene_start",
  "currentLabel": "",
  "textConfig": {
    "defaultFont": "SourceHanSansCN-Regular.ttf",
    "defaultFontSize": 28,
    "defaultTextSpeed": 60,
    "baseBgPath": "bg/",
    "baseSpritePath": "sprites/",
    "baseAudioPath": "audio/"
  },
  "bg": "room.png",
  "bgTransition": "fade",
  "bgm": "theme.mp3",
  "bgmVolume": 1.0,
  "bgmLoop": true,
  "sprites": [
    {
      "id": "Alice",
      "url": "alice_happy.png",
      "position": "left",
      "opacity": 1.0,
      "zIndex": 1
    }
  ],
  "sfx": [
    {
      "id": "click",
      "path": "click.wav",
      "loop": false,
      "volume": 0.5
    }
  ],
  "dialogue": {
    "isShow": true,
    "speaker": "Alice",
    "text": "Hello world",
    "segments": [
      { "text": "Hello ", "style": "" },
      { "text": "world", "style": "accent" }
    ],
    "emotion": "happy",
    "color": "#90EE90"
  },
  "choice": {
    "isShow": true,
    "question": "Is your name Alice?",
    "questionSegments": [
      { "text": "Is your ", "style": "" },
      { "text": "name", "style": "accent" },
      { "text": " Alice?", "style": "" }
    ],
    "options": [
      {
        "id": "0",
        "text": "Continue",
        "target": ".next",
        "disabled": false,
        "segments": [
          { "text": "Continue", "style": "" }
        ]
      }
    ]
  },
  "endingId": "good_end",
  "endingTitle": "Good Ending",
  "variables": {
    "numbers": { "hp": 100, "gold": 20 },
    "strings": { "playerName": "Alice" },
    "bools": { "metSpirit": true }
  },
  "inventory": {
    "healing_potion": 2
  },
  "itemDefinitions": {
    "healing_potion": {
      "id": "healing_potion",
      "name": "Healing Potion",
      "description": "Restore HP"
    }
  },
  "characterDefinitions": {
    "ForestSpirit": {
      "id": "ForestSpirit",
      "color": "#90EE90",
      "description": "Guardian of the forest",
      "sprites": {
        "happy": "spirit_happy.png"
      }
    }
  },
  "inventoryItems": [
    {
      "id": "healing_potion",
      "name": "Healing Potion",
      "description": "Restore HP",
      "count": 2
    }
  ]
}

Field Reference

status / runtimeStateVersion / runtimeStateChangeFlags

FieldTypeDescription
statusnumberRuntime status: 0=running, 1=waiting for choice, 2=ended
runtimeStateVersionnumberRuntime state version counter for cache/incremental refresh decisions
runtimeStateChangeFlagsnumberBit flags describing what changed since the previous snapshot

textConfig

FieldTypeDescription
defaultFontstringDefault font file
defaultFontSizenumberDefault font size in pixels
defaultTextSpeednumberDefault typewriter speed (chars/sec)
baseBgPathstringBase path for background assets
baseSpritePathstringBase path for sprite assets
baseAudioPathstringBase path for audio assets

Presentation fields

FieldTypeDescription
bgstringCurrent background asset
bgTransitionstringBackground transition hint
bgmstringCurrent background music asset
bgmVolumenumberCurrent BGM volume
bgmLoopbooleanWhether current BGM loops
spritesarrayCurrent sprite presentation state
sfxarrayCurrent sound effect presentation queue/state
endingIdstringCurrent ending id if the game has ended
endingTitlestringCurrent ending title if available

dialogue

FieldTypeDescription
textstringFlattened plain text, kept for backward compatibility
segmentsarrayStructured text segments; style == "" means plain text
emotionstringCurrent emotion tag
colorstringCurrent dialogue color

choice

FieldTypeDescription
questionstringFlattened plain text choice question
questionSegmentsarrayStructured segments for the choice question
options[].textstringFlattened plain text option label
options[].segmentsarrayStructured segments for each choice option

variables

FieldTypeDescription
numbersobjectNumeric variables (key → value)
stringsobjectString variables (key → value)
boolsobjectBoolean variables (key → value)

inventory / inventoryItems

FieldTypeDescription
inventoryobjectRaw item ID → count mapping
inventoryItemsarrayGUI-friendly item array with name, description, count

itemDefinitions / characterDefinitions

Static definition metadata for GUI lookup:

  • Item display names and descriptions
  • Character colors, descriptions, and sprite mappings

Recommended Consumption Pattern

Prefer the unified snapshot

For most hosts, the recommended path is to consume nova_export_runtime_state_json directly instead of rebuilding the full presentation state from many individual getters:

  1. Read the full presentation snapshot once
  2. Render background, BGM, sprites, dialogue, choices, and status UI from it
  3. Use dialogue.segments, choice.questionSegments, and choice.options[].segments when structured text is needed

When to keep using granular getters

Granular getters still make sense when:

  • you only need one field
  • you want to avoid JSON parsing
  • you are implementing a very low-level bridge or incremental optimization path

All legacy getters remain available and unchanged.

Renderer Responsibilities

NovaMark only outputs state; it doesn’t decide specific UI layout for Web, Native, or CLI. Clients should:

  1. Read runtime state snapshots
  2. Decide how to display variables, inventory, and other UI elements
  3. Render dialogue using dialogue.color or character definition colors
  4. Render local styling from dialogue.segments / choice.*Segments
  5. Control typewriter speed based on textConfig.defaultTextSpeed

Web Debugging

In Web templates, use directly:

novaDebug.runtimeState()
novaDebug.snapshot().runtimeState

The template-side NovaRenderer also provides:

renderer.getRuntimeState()
renderer.getPresentationState()

Where:

  • getRuntimeState() is the legacy name kept for compatibility
  • getPresentationState() is the newer semantic alias emphasizing that this is a unified presentation snapshot

Both currently return the same structure.

AST Snapshot Export

In addition to runtime state snapshots, NovaMark also provides AST snapshot export for debugging parse results, building Creator/editor tooling, and verifying the structure produced from scripts.

The difference is:

  • Runtime state snapshot: describes where the game is now, what variables contain, and what the UI should display
  • AST snapshot: describes the syntax tree produced after parsing the scripts

C++ export APIs

std::string export_ast_snapshot_string(const ProgramNode* program);
std::string export_ast_snapshot_string_from_scripts(const std::vector<MemoryScript>& scripts);
std::string export_ast_snapshot_string_from_path(const std::string& path);

C API export APIs

char* nova_export_ast_snapshot_from_path(const char* path);
char* nova_export_ast_snapshot_from_scripts(const NovaMemoryScript* scripts, size_t count);

Common use cases

  • Debugging parser output
  • Verifying the final merged Program structure in multi-script projects
  • Providing AST browsing in Creator / editor tools
  • Exporting stable JSON for test or CI comparisons

Output format

AST snapshots are currently exported as JSON strings. The top-level shape looks like this:

{
  "version": 1,
  "root": {
    "type": "Program",
    "children": []
  }
}

For nodes containing interpolated text, the snapshot includes an InterpolatedText structure with segment metadata such as:

  • PlainText: normal text segment
  • Interpolation: a {{expr}} segment
  • InlineStyle: an inline {style:text} segment

This lets tools understand not only the original text, but also which parts are expressions and which parts are styling markers. The same segment model is now also used at runtime through dialogue.segments, choice.questionSegments, and choice.options[].segments.

Save Format

NovaMark recommends this responsibility split:

  • Official save files: Binary format, for production use
  • JSON snapshots: For debugging, testing, Web/WASM toolchains

JSON remains an important development format but is no longer recommended as the official player save format.

Last updated on