# Bloxd Mock API — run Bloxd world code outside Bloxd

> Source: <https://gist.github.com/Oreo-belt25/f48cbe09cb9e4b45dd58282fcf433828>
> Published: 2026-09-26 01:45:35+00:00

Run Bloxd world code on your own computer, without Bloxd. Catch typos, bad arguments and known engine traps before you paste anything into a world.

Made by Oreo_belt25 with Claude. Free to use and change, just give a shoutout.

| File | What it does | 
|---|---|
| `mkmock.py` | Reads the editor's TypeScript declarations (or, as a fallback, the docs pages) and writes `bloxd_api.json` : every api function with its parameters, which ones are optional, and its return type, plus every callback | 
| `bloxd_api.json` | The generated description: 247 functions (95 optional parameters marked) and 80 callbacks, from `bloxd-types` 0.1.2, checked identical to the live editor's types on 25 Sep 2026 | 
| `mock_api.mjs` | Builds a fake `api` object from that JSON | 
| `quirks.mjs` | Places where the real engine disagrees with the docs, found by testing in-engine | 
| `run.mjs` | Command-line runner: loads your world code, simulates players and ticks, prints a report | 
| `selftest.mjs` | 45 checks that the mock still catches what it should. Run `node selftest.mjs` after any change | 
| `broken_world.js` | A deliberately broken file to see the report in action | 
| `quirk_probe_1.js` | World code that re-checks every rule in `quirks.mjs` in a real Bloxd world. Paste it into a test world and type`!help` | 

- Node.js 18 or newer
- Python 3 (only to regenerate `bloxd_api.json` ). On Windows the command is
usually`python` or`py` instead of`python3` . Without Python, the two`mkmock.py` self-test checks are skipped and everything else still runs

No packages to install.

```
node run.mjs my_world.js
node run.mjs my_world.js --players 4 --ticks 400
node run.mjs my_world.js --chat "!help" --chat "p2:!stats" -v
```

The runner joins the players, runs the tick loop (20 ticks = 1 second), sends any chat messages halfway through, then prints:

- which callbacks your code defines
- total api calls, the average per tick, and **the busiest tick** with what
was called on it (useful for spotting work that should be spread across ticks)
- errors thrown inside callbacks (typos, calls that crash in-engine)
- warnings (arguments the docs or the engine won't accept)

Options: `--players N`, `--ticks N`, `--chat MSG` (repeatable), `--null off|warn|throw`,
`--null-returns` (every "possibly null" function returns null, to test your null guards),
`--timeout-ms N` (how long one callback may run, default 10000),
`--fire NAME` (fire any callback once halfway through, with placeholder arguments
built from its documented parameters; repeatable),
`-v` to print everything shown to players (chat, middle and crosshair text,
flying messages, popups, killfeed, and HUD text options like `RightInfoText`;
HUD text re-set unchanged every tick is printed once), `--json` for machine-readable output.
The exit code is 2 if any callback threw, so it works in scripts.

1. 
**The editor's types become data.**`mkmock.py` reads the same TypeScript
declarations the Bloxd code editor uses for autocomplete, so it knows exact
types and which arguments are optional. (It can also read the plain-text docs
pages, which don't mark optional arguments.) Each
function's documented return type is mapped to a placeholder: a`number` returns 0, an inventory item returns`{name: "Stone", amount: 1, attributes: {}}` ,
and so on. Ids are fresh on every call (`m_mock_1` ,`m_mock_2` ...), so two
spawned mobs never share an id. "Possibly null" (`PNull<...>` ) functions
return a real value by default, so spawns succeed and the happy path runs;`--null-returns` flips them all to null to check your guards.
2. 
**Every call is checked.** Too many or too few arguments (a required one left
out), null where the types don't allow it, explicit`undefined` in the middle of an argument list, and oversized
arguments all get flagged. Calling a function that isn't in the docs throws,
which catches typos like`api.sendMesage` .
3. 
**Some functions really work.** Player lists, player and lobby DB values,
positions,`getBlock` /`setBlock` and`api.now()` keep real state, so most
world code runs normally instead of falling over on placeholder values.
4. 
**The clock behaves like Bloxd's.**`Date.now()` is frozen for the whole tick
and jumps 50 ms between ticks, as it does in-engine. A "stop after 5 ms" loop
budget never trips in Bloxd, so when one callback reads`Date.now()` hundreds of times in a single call, the report flags it with a`[clock]` warning before it lags a lobby.
Like the engine, any callback that runs for about 10 s is stopped and
reported as`InternalError: interrupted` , so an infinite loop shows up as
an error instead of freezing your terminal. The mock then stops firing
that callback for the rest of the run.
5. 
**Callback return values are checked.** The Callbacks page lists what each
callback may return. Returning something else, such as`"preventchange"` instead of`"preventChange"` , does nothing in-engine, so the mock warns and
suggests the right spelling. Callbacks whose documented return includes a
named object type are skipped rather than risk a false warning.
6. 
**Engine findings override the docs.**`quirks.mjs` holds behaviour confirmed
by in-engine probes that contradicts or goes beyond the docs. Every rule was
re-checked in-engine on 27 Sep 2026 by`quirk_probe_1.js` . Examples:
  - returning `null` from a callback**prevents** the action: a block change, a
drop, or a hit on a mob or player, just like the documented`"prevent..."` strings. Return nothing to allow it. The typo`"preventchange"` is ignored
  - `setItemSlot` and both chest functions reject amount`-1` , although the docs suggest it
  - `setMobSetting(..., "heldItemName", null)` is the correct way to clear it;`""` throws
  - `setMobAiState` needs an explicit`null` for params
  - `getCraftingRecipesForPlayer` throws an error`try/catch` can't catch
  - an `{icon}` in a player's name tag is rejected, although the types allow it
  - `setItemStat` 's`CrosshairText` and bow`secondaryDamage` are accepted but do nothing
  - a `ParticleEmitter` attached to an entity node, and a mesh`Box` with only`emissiveColor` , are accepted but draw nothing
  - `"peacefulAdventure"` is rejected; gamemode names are all lowercase
  - `isNearInterrupt` does turn true late in a long callback; the mock
says true once a callback has used 80% of`--timeout-ms`
 The probe also caught four rules Bloxd had fixed, which the mock no longer flags: `animateEntity(id, null)` ,`giveItem` with an explicit`undefined` ,`getHeldItem` without`attributes` , and hand-built`tameInfo` .
7. returning 

``` js
import { makeMockApi } from "./mock_api.mjs";

const mock = makeMockApi({
  players: ["p1", "p2"],
  overrides: { getBlock: () => "Stone" },   // replace any function
});
mock.api.setPlayerDbValue("p1", "coins", 5);
mock.api.getPlayerDbValue("p1", "coins");   // -> 5
mock.countCalls("setPlayerDbValue");        // -> 1
mock.warnings;                               // anything suspicious
```

**From the editor's types (recommended).** The npm package `bloxd-types`
mirrors the declarations built into the Bloxd code editor (checked line by
line against the live editor on 25 Sep 2026: identical apart from a few
`//@ts-ignore` comments). With Node installed:

```
npm pack bloxd-types            # downloads bloxd-types-<version>.tgz
tar xzf bloxd-types-*.tgz       # unpacks to package/
python3 mkmock.py --dts package/types/@bloxd/index.d.ts package/types/@bloxd/globals.d.ts
```

If the package ever falls behind, you can pull the same files out of the
editor: open World Code, press F12, go to **Sources**, press Ctrl+Shift+F and
search for `interface GameApi`. The matching script holds the declarations as
a block of text.

**From the docs text (fallback).** Copy the API Reference and Callbacks pages
from bloxd.io/docs into text files (select all, paste into a `.txt`), then:

```
python3 mkmock.py API_Reference.txt Callbacks.txt
```

This works, but the docs text doesn't say which arguments are optional, so the "missing required argument" check is switched off.

Either way, if far fewer entries than expected turn up (under 200 functions or 70
callbacks), `mkmock.py` prints a warning and does **not** overwrite `bloxd_api.json`,
since that usually means a cut-off paste or a changed layout. Pass `--force` to write it anyway.

- **Single-file world code only.** Files using`import` /`export` aren't supported yet.
- **Most callbacks only run if you ask.** Join, tick, chat and leave fire
automatically; anything else needs`--fire NAME` , and gets placeholder
arguments (player`p1` , coordinates 0, block`"Stone"` , shop keys`"mock_category"` /`"mock_item"` , and a sensible value for other string or
number types).
- **Placeholders aren't real game state.** Functions without built-in behaviour
return a placeholder, so code that depends on, say, real mob positions won't
behave as it would in a world. Use`overrides` to fake what you need.
- **Only as good as the docs plus the quirks list.** Where the docs are wrong and
nobody has probed it yet, the mock is wrong too. Test in-engine before shipping.

- Use the full types for basic type checks (a number passed where an item name belongs, a string where coordinates belong).
- Print where the docs and the editor's types disagree, as a doc-bug report.
- Support multi-file world code.
- Grow `quirks.mjs` as more engine behaviour gets confirmed, and re-run`quirk_probe_1.js` after each Bloxd update.
