Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>Pika StudioNew to Visual Studio Code? Get it now.
Pika Studio

Pika Studio

Phạm Văn Toán

|
68 installs
| (0) | Free
Run and debug Pika Lua game packs in the native simulator directly from VS Code.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Pika Studio

Run and debug Pika Lua game packs in the native simulator directly from VS Code.

Features

  • Game Packs tree view — browse all packs under SD/games in the Activity Bar
  • Configuration — set what the host passes into game_start(params): server params (level, difficulty, …), language, and is_a2a
  • Run / Stop — launch the native simulator with one click or from the command palette
  • Debug — set breakpoints in Lua files and step through code with the built-in Pika Lua debugger
  • Manifest form editor — visual editor for manifest.json (open via the gear icon in the editor title bar)
  • Output console — simulator stdout/stderr streamed to the "Pika Engine" Output channel

Requirements

  • Windows x64 (the bundled simulator pika_studio.exe is a Windows native binary)
  • A workspace containing a SD/games folder, or open SD/games directly

Quick Start

  1. Install the extension (.vsix) via Extensions → ... → Install from VSIX
  2. Open the Pika firmware repo (or the SD/games folder) in VS Code
  3. Click the Pika icon in the Activity Bar
  4. Right-click a game pack → Run, or press the ▶ button

Commands

Command Description
Pika: Run Game… Pick a game pack from a quick-pick list and run it
Pika: Stop Simulator Stop the running simulator process
Pika: Refresh Reload the game packs list
Pika: Open Manifest Form Open the visual manifest editor
Pika: Configure Voice ASR Server Set the address of the Voice ASR server

Extension Settings

Setting Default Description
pika.startParams {} Server start params for game_start(params) — free-form, server-defined (128 B / 32 nodes, shared with game_id)
pika.language "vi" params.language, the robot's UI language — one of the 10 language_t codes
pika.simulateTalkFlow false params.is_a2a — launch as an A2A talk flow
pika.rankMode "real" Where Ranking.get_result() data comes from — real (full robot behaviour, writes a live score row), fetch (real board, no upload), mock (inject by hand)
pika.sdk.source "" Pika SDK source (local folder or git URL): assets, docs, libraries, examples
pika.voiceServer "" External voice ASR server (host:port, mock inject path)
pika.gameVoice.* "" Device credentials for the real /ws/v3/game_command voice path

game_start(params) — where each field comes from

The Configuration section mirrors how the robot assembles that table, and keeps the two sources apart because confusing them is the common mistake:

Field Source on the robot Control
language Host-side (game_get_language), the robot's current UI language. Dropdown — the robot's full language_t enum (kLangIso[]), 10 codes. Out of range → vi.
is_a2a Host-side, true when launched inside an A2A talk flow. Dropdown — true / false.
level, difficulty, … The backend's GAME_START data object, forwarded verbatim. Add-a-row, name is a combobox with presets.

Those two host fields are the only ones the firmware defines by name, which is why only they get a closed list. They are attached after the server object is decoded, so a server field of the same name cannot override them.

Everything else is server-defined: back stashes the whole data object without inspecting it, precisely so a new server field needs no firmware change. The presets (difficulty → easy/medium/hard, level, game_type, hero_label) are conventions drawn from packs and docs, not a contract — so the name field stays typeable and any new server field remains testable here.

Only an A2A launch carries server params. A menu launch sends GameStartMsg_st, which holds nothing but game_id. Setting params with is_a2a off therefore describes a launch the robot cannot perform, and the section warns when you do.

The 128-byte budget includes game_id

conversation.cpp serializes the whole data object into the char[128] params_json field, and game_id is part of that object — so the pack name eats into the same budget (and costs one of the 32 nodes). The meter accounts for it: the Configuration section sizes against the longest installed pack name, and the runner sizes against the pack actually being launched.

This matters because the overflow is silent on the device: back logs a warning but strncpys anyway, the clipped JSON no longer parses, and the game receives an empty table — indistinguishable in-game from "the server sent nothing". Studio therefore refuses to save an over-budget payload and drops it rather than truncating at launch.

Ranking on PC (pika.rankMode)

On the robot, back closes the ranking loop by itself: Ranking.report() arrives as IPC_CMD_GAME_RESULT, back POSTs /games/result, then GETs /games/rank, unwraps data, and sends it down as IPC_CMD_GAME_RANK_RESULT. Head latches that reply and Ranking.get_result() consumes it once.

The simulator runs no back, so nothing closes that loop on its own and Ranking.get_result() would return nil forever — which looks exactly like a broken pack. pika.rankMode decides how it gets closed:

Mode Ranking.report() Ranking.get_result()
real (default) POSTs a real score row real GET /games/rank
fetch logged only, not uploaded real GET /games/rank
mock logged to the dashboard whatever you inject by hand

The default is real so the PC behaves like the robot without being configured into it — a leaderboard branch that works here works there.

It writes to the live board. Every Ranking.report() in real mode appends a score row children will see. When you are iterating on a pack and don't want that trail, switch to fetch: same real board data, no upload. mock needs no credentials at all and is the offline fallback.

Note that all three are inert without pika.gameVoice.* credentials — an unconfigured install falls back to mock injection and says so in the log.

Both real modes reuse the pika.gameVoice.* credentials (robot id + session or master key) and send the same robot-id / challenge / request-token headers back sends. They are independent of pika.voiceMode: fetching a leaderboard does not turn on the microphone.

Note that packs poll for a short window after reporting — word_maze gives up after 6 s — so in mock mode the injected board has to land inside that window. The real modes have no such race.

The POST body is built field-for-field against Application::PostGameResult (robot_id, game_id, version, entry, ended_reason, score, duration), and both calls carry firmware_version in the query like every other device API call back makes. Both modes were verified end-to-end against the live backend: fetch returns the real board, and real gets {"recorded":true} back with the board updating in the same run.

Two behaviours worth knowing before reading a real-mode result: the backend keeps only your best score, so posting below your current best changes nothing visible even though the row was written; and duration is 0 unless the pack actually ran that long, since the engine computes it from VM start.

Debug

Add a launch.json configuration:

{
  "type": "pika-lua",
  "request": "launch",
  "name": "Pika: Debug Game",
  "gameId": "${command:pika.pickGame}"
}

Or press F5 — Pika Studio will prompt you to pick a game pack.

Icons

File Role
media/icon-source.svg Vector master (colour badge). Edit this one.
media/icon.png Marketplace icon, 512×512, rendered from the master.
media/icon.svg Activity-bar icon. VS Code renders it as a monochrome mask, so it is a flat silhouette — colour and gradients there are discarded.

icon.png is a build product: after changing icon-source.svg, re-render it or the shipped icon silently keeps the old art.

npx sharp-cli -i media/icon-source.svg -o media/icon.png resize 512 512

License

MIT

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft