sync
@bedrock-core/sync is the low-level cross-addon transport for Minecraft Bedrock. It layers a message bus, peer discovery, RPC, a replicated state mirror and a broadcast channel on top of script events, so addons in separate script realms can talk.
If you are building a Bedrock addon, you do not need this package directly — use @bedrock-core/server instead. The runtime creates and manages the one sync node for you, and raw transport access is available as core.node whenever you want it.
Install
- npm
- yarn
- pnpm
npm install @bedrock-core/sync
yarn add @bedrock-core/sync
pnpm add @bedrock-core/sync
Or reach it through the meta-package, which pins a matching version:
import { createSync } from '@bedrock-core/server/sync';
When to use it directly
- You are building a library or framework layer on top of sync, as
server-runtimeitself does. - You need raw bus / discovery / state access from an addon that already uses
server-runtime— usecore.node, no extra install needed. - You are writing GameTests that spin up several isolated nodes in one realm to exercise the protocol.
You do not need a second node just to read another addon's state. The state store is a globally shared mirror, so core.node.state.get('other_addon', 'key') works from the one node you already have.
Quick start
Create one SyncNode for the realm and call start() once on boot.
import { createSync } from '@bedrock-core/sync';
export const sync = createSync({
id: 'mycoolitems', // unique, stable id (a-z0-9_) — transport address + default owned namespace
version: '1.0.0',
meta: { /* opaque data peers see as PeerInfo.meta */ },
});
sync.start();
After start(), sync.discovery, sync.rpc, sync.state and sync.events are live.
sync.discovery.onPeerUp(peer => console.warn('peer up', peer.id));
sync.rpc.onRequest('ping', () => 'pong');
sync.state.set('mycoolitems', 'volume', 5);
sync.events.emit('restocked', { item: 'diamond' });
SyncNodeOptions
interface SyncNodeOptions {
id: string;
version?: string;
schemaVersion?: number;
meta?: Record<string, unknown>;
maxMessage?: number;
instanceId?: string;
}
| Option | Default | What it does |
|---|---|---|
id | — | Unique addon id. Used as the envelope src, the RPC address, and the one namespace this node writes. |
version | '0.0.0' | Announced to peers as PeerInfo.version. |
schemaVersion | 0 | Announced alongside version, for higher layers that version their payloads. |
meta | — | Opaque metadata broadcast with every announce; surfaces on peers as PeerInfo.meta. server-runtime puts the addon manifest here. |
maxMessage | 2000 | Character budget for one script-event message — what the chunker splits against and what a batch is packed up to. Mainly for tests. |
instanceId | generated | Overrides the auto-generated instance id. Mainly for tests. |
SyncNode
class SyncNode {
readonly id: string;
readonly bus: Bus;
readonly discovery: Discovery;
readonly rpc: Rpc;
readonly state: State;
readonly events: Events;
start(): void; // idempotent
stop(): void;
}
createSync(options) simply builds one and returns it; new SyncNode(options) is equivalent. start() brings up the bus, RPC, discovery, state and events in that order, and stop() tears them down in reverse.
Notes
- One node per realm. Several
SyncNodes with different ids in one realm is only useful for in-realm testing. Production addons useserver-runtimeandcore.node. - Timing is tick-based. Messages flush over ticks; you will never get an RPC reply on the same tick you sent it.
- Large payloads are fine. An envelope above the size budget is split into frames and reassembled transparently; one that fits is sent whole.
- Small messages travel together. A node that sends several at once has them packed into as few script events as the budget allows — see the outbound queue.
- sync never touches dynamic properties. They are pack-scoped, and persistence is each addon's own responsibility — see State.
- Load order is not a problem. Every node re-announces on a heartbeat and broadcasts a
whoisat startup, so a late loader catches up and an early loader hears about it.
In this section
| Page | Description |
|---|---|
| Discovery | Finding peers: announce, whois, TTL eviction, namespace collisions |
| Rpc | Request/response calls, typed clients, handler maps, timeouts |
| State | Replicated key/value: deltas, snapshots, requestSync, ownership |
| Events | A happening: one message, every listener, nothing kept |
| Protocol | The bus, the envelope, the supported protocol window, framing and rate limiting |