api.sync
SyncApi
Editing the map in front together with other editors: every change to it is handed out as an op for the plugin to carry to them, and theirs come back in, in the order a server puts them. The scmjs.dev plugin's shared maps are built on it; the transport, the server and who may join are the plugin's.
Members
start
start(options: SyncStartOptions): SyncSession | null;
Share the map in front. Null when no map is open or another session is running (one at a time). A session a plugin started stops when the plugin is turned off.
Types
Declarations only this group names.
WireLocationChangeinterface
interface WireLocationChange
index: number | |
|---|---|
after: LocationRecord | |
name: string | null | The name as text ( |
created: boolean | The slot was empty before: a new location, which moves to a free slot if someone else took this one. |
SyncEditOpinterface
interface SyncEditOp
One undo step's worth of terrain and objects. The grids are flat number lists: terrain
[at, mtxm, tile, …], the others [at, value, …]. reverse marks the undo of an edit:
its parts go in the reverse of the usual order, as undo walks them.
kind: "edit" | |
|---|---|
label: string | |
width: number | The map size the cells index; a grid part is skipped on a map of another size. |
height: number | |
reverse?: boolean | |
terrain?: number[] | |
isom?: number[] | |
doodadTiles?: number[] | |
fog?: number[] | |
isomWhole?: string | null | The whole ISOM lattice (base64 of the u16 cells) the edit gave the map, or null for taking it away. |
maskWhole?: string | null | |
units?: WireListChange<UnitRecord>[] | |
doodads?: WireListChange<DoodadRecord>[] | |
sprites?: WireListChange<SpriteRecord>[] | |
locations?: WireLocationChange[] |
SYNC_FIELDSconst
const SYNC_FIELDS: readonly [
"type",
"fileVersion",
"nameIndex",
"descriptionIndex",
"playerTypes",
"editorPlayerTypes",
"playerRaces",
"playerColors",
"playerRgb",
"forces",
"unitSettings",
"unitAvailability",
"upgradeSettings",
"upgradeRestrictions",
"techSettings",
"techRestrictions",
"wavs",
"cuwp",
"cuwpUsed",
"triggers",
"briefing",
"switchNames"
]
SyncFieldtype
type SyncField = (typeof SYNC_FIELDS)[number];
SyncFieldsOpinterface
interface SyncFieldsOp
kind: "fields" | |
|---|---|
set: Partial<Record<SyncField, unknown>> | Each changed field's new value, packed ( |
strings?: [ number, string | null, string | null ][] | String slots that changed: |
stringsLength?: number | The table's length afterwards (trailing blank slots are dropped). |
stringsFormat?: { extended: boolean; encoding: TextEncoding; } | The table's width and text encoding, when they changed. |
SyncExtrasOpinterface
interface SyncExtrasOp
kind: "extras" | |
|---|---|
set: [ string, string | null ][] |
|
SyncResetOpinterface
interface SyncResetOp
kind: "reset" | |
|---|---|
label: string | |
chk: string | The whole scenario, base64. |
SyncOptype
type SyncOp = SyncEditOp | SyncFieldsOp | SyncExtrasOp | SyncResetOp;
SyncHoldtype
type SyncHold = "stroke" | "dialog" | "behind";
Why other people's changes are waiting: a stroke under way on the map, a dialog that edits the map open, another map in front.
SyncReportinterface
interface SyncReport
ops: number | Other people's ops applied. |
|---|---|
dropped: number | Parts of them that could not be applied — the unit they moved was deleted, the map was resized… |
lost: number | Parts of this editor's unconfirmed changes that no longer applied on top of theirs. |
SyncStartOptionsinterface
interface SyncStartOptions
send(op: SyncOp): void | Every change made to the shared map, as a plain object JSON can carry, in the order made: commits, undo and redo, the dialogs' writes, archive files, and a resize or a tileset change (the whole scenario). Each must reach the server, and every other editor, in this order. |
|---|---|
onEnd?(reason: "closed" | "stopped"): void | The session ended: the shared map was closed or replaced by another, or |
onApplied?(report: SyncReport): void | Other people's changes were applied to the map. |
SyncSessioninterface
interface SyncSession
One shared map. The server confirms each op this editor sent (confirm, in order) and
relays everyone else's (receive, in the order it gave them); the session applies
them to the map when nothing is in the way (holding) and rebases this editor's
unconfirmed changes on top, so every editor that has seen the same ops has the same map.
readonly documentId: number | The shared map's |
|---|---|
receive(op: unknown): boolean | Another editor's op, in the server's order. False, and nothing done, for a value that is not an op. |
confirm(): void | The server confirmed the oldest op this editor sent. |
pending(): number | Ops sent and not yet confirmed. |
waiting(): number | Ops and confirmations received and not yet applied. |
snapshot(): Promise<Uint8Array | null> | The shared map as a file ( |
holding(): SyncHold | null | |
stop(): void |
const session = api.sync.start({ send: (op) => socket.send(JSON.stringify({ type: "op", op })) });
socket.onmessage = (m) => {
const msg = JSON.parse(m.data);
if (msg.type === "ack") session?.confirm();
if (msg.type === "op") session?.receive(msg.op);
};