Changelog

Notable changes to the Board Godot SDK. The format follows Keep a Changelog, and the project adheres to Semantic Versioning.

Note: The Godot SDK is at v1.0.0-beta.1, the first beta. It covers the full public-API surface of the Board platform and is the first version intended for building games against, ahead of the stable 1.0.0.

[1.0.0-beta.1] — 2026-05-26

First beta release. The SDK covers the full public-API surface of the Board platform across input, session, save, avatar, pause, and application domains, with a no-build-script export-and-install path and a polished sample app.

Added

  • Board.input.contacts_received — the single per-frame touch signal: a snapshot Array of contact Dictionary entries, each carrying phase_id (BEGAN / MOVED / ENDED / CANCELED / STATIONARY), is_touched, glyph_id, and contact_id. Games that want discrete edges (tap-down / tap-up via is_touched, or piece-lifted) keep their own previous-frame map keyed by contact_id and diff it each frame. This mirrors the Unity SDK’s per-frame snapshot model.
  • Cached, player-scoped avatar loader on Board.avatar (mirrors Unity’s BoardPlayer.avatar):
    • await_load_avatar(avatar_id: int) -> ImageTexture — returns a ready-to-display, already-decoded ImageTexture, or null on failure / off-device. Cached after first load; concurrent loads of the same id coalesce onto one fetch. Pass a player’s avatar_id from Board.session.get_players() (the player Dictionary’s avatar_id is a String, so call with int(player.avatar_id)).
    • await_default_avatar() -> ImageTexture — the default avatar (id 0).
    • clear_cache() -> void — drop cached textures.
  • Board.save.get_app_storage_info() -> Dictionary — synchronous storage query. Keys: total_storage (int bytes), used_storage (int bytes), remaining_storage (int bytes), usage_percentage (float 0.01.0). Off-device returns {}. Use it to render a storage meter or pre-flight whether a save fits. Mirrors Unity’s GetAppStorageInfo.
  • Optional ai_type_indices filter on the session selectorBoard.session.present_add_player(ai_type_indices := PackedInt32Array()) and Board.session.present_replace_player(session_id, ai_type_indices := PackedInt32Array()) now take an optional trailing PackedInt32Array. An empty array (the default) offers all AI types registered via set_ai_player_types(); a non-empty array restricts the selector’s “Add AI” options to that subset. Existing zero-arg / (session_id)-only calls still work. Mirrors Unity’s selector AI-type filter.
  • Documented noCompress one-time setup — after installing the Custom Android Build Template, add noCompress "pck", "sparsepck" to android/build/build.gradle’s aaptOptions block (right after the ignoreAssetsPattern line). Godot’s stock template doesn’t set this; without it the PCK is deflated inside the APK and cold boot takes ~2 minutes instead of a few seconds, because the runtime can mmap the PCK instead of decompressing every FileAccess call through ZipInputStream. This is now part of the project setup docs rather than a build-script side effect.
  • scripts/prune_import_cache.py — idempotent orphan .godot/imported/ cleanup.
  • Sample boot scene templatesample/boot/boot_scene.{tscn,gd} — threaded scene-load with progress bar; ready to swap in as the launch scene.
  • Pause-context restart custom button wired in the sample sidebar: matches on Board.pause.ACTION_CUSTOM_BUTTON + custom_button_id == "restart"get_tree().reload_current_scene.call_deferred().
  • bootstrap-board-godot-game skill — interactive Claude skill that scaffolds a new Board game with every best practice from this SDK baked in.
  • Per-domain consumer-facing CLAUDE.md at addon/addons/board_sdk/CLAUDE.md — drops into game-side addons/ so Claude can read it and know how to use the SDK.
  • Full Godot documentation under docs/ (22 markdown files) mirroring docs.dev.board.fun’s sectioning (Getting Started, Learn, Guides, Reference, More, FAQ).

Changed

  • plugin.cfg version bumped 0.1.01.0.0-beta.1.
  • Board.session.is_ready()Board.session.is_session_ready() — names now mirror the underlying plugin method and pair cleanly with are_services_ready(). Breaking for callers of the previous name.
  • Sample package renamed fun.board.smoketestfun.board.godot_example.
  • Sample display name Board Smoke TestGodot Board Example.
  • board_application.gd — dropped the “(later)” lifecycle-hook stub from the file header; lifecycle stays Godot-native via NOTIFICATION_APPLICATION_*.
  • board_export_plugin.gd — release export with a missing release AAR now emits push_warning(...) instead of silently shipping the debug AAR.
  • board_avatar.gd / board_pause.gd — clarified docstrings on await_load_avatar() and poll_result() so the caching/coalescing behavior and signal/fallback duality are visible.

Removed

  • Board.services_ready signal — was declared on board.gd but never emitted. Use Board.session.are_services_ready() to query state directly.

Compatibility

  • The compiled board.aar ships pre-built with the addon at addon/addons/board_sdk/android/bin/debug/board.aar. End users don’t build it.
  • Requires Godot 4.6.x stable. JDK 17 required for Android builds (Godot 4.6 ships Java 17 bytecode).
  • Targets Android API 33 (compile/target SDK), minimum 29.
  • Single ABI: arm64-v8a.

Migration Notes

If you’re upgrading from a pre-v1.0 prerelease:

  1. Rename is_ready() calls — search-and-replace Board.session.is_ready()Board.session.is_session_ready() across your codebase.
  2. Drop the Board.services_ready signal handler if you wired one — the signal was never emitted; query Board.session.are_services_ready() directly instead.
  3. Replace the addon — re-copy addon/addons/board_sdk from the new SDK release into your project’s addons/ directory. The bundled board.aar updates with it.
  4. Bump plugin.cfg in your local copy if you symlinked the addon at a specific version. New version: 1.0.0-beta.1.
  5. Touch is a single per-frame snapshotBoard.input.contacts_received is the only touch signal. If you need discrete edges (tap-down / tap-up via is_touched, or piece-lifted), keep your own previous-frame map keyed by contact_id and diff each frame:

    var _prev := {}  # contact_id -> is_touched
    
    func _on_contacts_received(contacts: Array) -> void:
        var seen := {}
        for contact in contacts:
            var id: int = contact.contact_id
            seen[id] = true
            var was_touched: bool = _prev.get(id, false)
            if contact.is_touched and not was_touched:
                _on_tap_down(contact)
            elif was_touched and not contact.is_touched:
                _on_tap_up(contact)
            _prev[id] = contact.is_touched
        for id in _prev.keys():
            if not seen.has(id):
                _on_piece_lifted(id)
                _prev.erase(id)
    
  6. Avatars are loaded per-player, not by arbitrary id — replace any raw-bytes loading with await Board.avatar.await_load_avatar(int(player.avatar_id)), which returns a ready-to-display ImageTexture. Drop your own decode and caching; the loader decodes, caches, and coalesces concurrent loads for you.

Earlier Versions

The 0.x prerelease series isn’t documented here. v1.0.0-beta.1 is the first documented release, intended for building games against ahead of the stable 1.0.0.