Skip to main content

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.

For framework and library developers

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 install @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-runtime itself does.
  • You need raw bus / discovery / state access from an addon that already uses server-runtime — use core.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;
}
OptionDefaultWhat 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.
schemaVersion0Announced 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.
maxMessage2000Character budget for one script-event message — what the chunker splits against and what a batch is packed up to. Mainly for tests.
instanceIdgeneratedOverrides 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 use server-runtime and core.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 whois at startup, so a late loader catches up and an early loader hears about it.

In this section​

PageDescription
DiscoveryFinding peers: announce, whois, TTL eviction, namespace collisions
RpcRequest/response calls, typed clients, handler maps, timeouts
StateReplicated key/value: deltas, snapshots, requestSync, ownership
EventsA happening: one message, every listener, nothing kept
ProtocolThe bus, the envelope, the supported protocol window, framing and rate limiting