Syncromesh/Network
Per-board networking, connection focus windows, entity lifecycle queues, and event-driven ownership transfer handshakes.
Provides ENet-backed board networking. A board can listen for incoming peers and connect to other boards. Replication packets are streamed from the listening side to incoming subscribers. Connecting peers set focus regions, consume entity lifecycle events, and coordinate explicit ownership transfer through offer/intent/award/ack handshakes driven by JS.
Import #
import * as Network from 'Syncromesh/Network';Connection lifecycle #
listen(port: number, options?: { maxConnections?: number }): Promise<boolean>connect(host: string, port: number, options?: { clientOnly?: boolean }): Promise<number>disconnect(connectionId: number): Promise<boolean>disconnectAll(): Promise<boolean>listConnections(): Promise<Array<{ id: number, host: string, port: number, connected: boolean, incoming: boolean, clientOnly: boolean, focus: AABB | null, authState: 'disabled' | 'pending' | 'accepted' | 'rejected', authRequired: boolean }>>Authentication handshake #
setAuthConfig(options?: { enabled?: boolean, timeoutMs?: number, allowUnauthenticatedMessages?: boolean }): Promise<{ enabled: boolean, timeoutMs: number, allowUnauthenticatedMessages: boolean }>getAuthConfig(): Promise<{ enabled: boolean, timeoutMs: number, allowUnauthenticatedMessages: boolean }>acceptConnection(connectionId: number): Promise<boolean>denyConnection(connectionId: number, options?: { reason?: number }): Promise<boolean>getConnectionAuthState(connectionId: number): Promise<'disabled' | 'pending' | 'accepted' | 'rejected'>Focus and ownership #
setFocus(connectionId: number, focus: AABB): Promise<boolean>clearFocus(connectionId: number): Promise<boolean>setBoardArea(area: AABB): Promise<boolean>clearBoardArea(): Promise<boolean>null if unset.getBoardArea(): Promise<AABB | null>localToWorld(position: Point): Promise<Point>worldToLocal(position: Point): Promise<Point>ownsPoint(position: Point): Promise<boolean>isEntityOwned(entityId: number): Promise<boolean>isEntityRemoteBound(entityId: number): Promise<boolean>offerEntityOwnership(entityId: number): Promise<boolean>ackEntityOwnershipOffer(connectionId: number, remoteEntityId: number, localEntityId: number): Promise<boolean>setClientOnly(clientOnly: boolean): Promise<boolean>isClientOnly(): Promise<boolean>Message channel #
send(connectionId: number, topic: string, payload: string): Promise<boolean>audience: 'incomingClientOnly'targets end-client peers only (client-only boards), excluding sibling server-capable boards.audience: 'incoming'targets all incoming peers.audience: 'all'targets both incoming and outgoing peers.
broadcast(topic: string, payload: string, options?: { audience?: 'all' | 'incoming' | 'incomingClientOnly' }): Promise<number>consumeMessages(options?: { max?: number }): Promise<Array<{ connectionId: number, topic: string, payload: string }>>Entity events #
consumeEntityLifecycleEvents(options?: { max?: number }): Promise<Array<{ type: 'created' | 'deleted' | 'ownershipChanged', localEntityId: number, owned: boolean, scriptType: string }>>ownershipTransferOffered event dispatch for live gameplay code.consumeOwnershipTransferOffers(options?: { max?: number }): Promise<Array<{ connectionId: number, remoteEntityId: number, uid: number }>>Event dispatch #
Helix/Event only when work exists.setEventDispatch(options?: { messageEvents?: boolean, entityLifecycleEvents?: boolean, maxMessagesPerTick?: number, maxEntityLifecycleEventsPerTick?: number }): Promise<{ messageEvents: boolean, entityLifecycleEvents: boolean, maxMessagesPerTick: number, maxEntityLifecycleEventsPerTick: number }>getEventDispatch(): Promise<{ messageEvents: boolean, entityLifecycleEvents: boolean, maxMessagesPerTick: number, maxEntityLifecycleEventsPerTick: number }>When enabled, runtime emits:
- networkMessage with { connectionId, topic, payload } - ownershipTransferOffered with { connectionId, remoteEntityId, uid } - entityCreated with { type: 'created', localEntityId, owned, scriptType } - entityDeleted with { type: 'deleted', localEntityId, owned, scriptType } - entityOwnershipChanged with { type: 'ownershipChanged', localEntityId, owned, scriptType }
Use Helix/Event.on(...) to subscribe. Avoid mixing queue polling (consumeMessages / consumeEntityLifecycleEvents) with event dispatch for the same stream.
Auth handshakes are expected to use networkMessage topics such as auth:challenge, auth:response, and auth:result.
Replication cadence #
setReplicationRate(hz: number): Promise<number>getReplicationRate(): Promise<number>setEntityReplicationMask(entityId: number, mask: { position?: boolean, rotation?: boolean, linearVelocity?: boolean, angularVelocity?: boolean, travel?: boolean, identity?: boolean, logic?: boolean, data?: boolean, locomotion?: boolean, waypoint?: boolean }): Promise<object>getEntityReplicationMask(entityId: number): Promise<{ position: boolean, rotation: boolean, linearVelocity: boolean, angularVelocity: boolean, travel: boolean, identity: boolean, logic: boolean, data: boolean, locomotion: boolean, waypoint: boolean }>clearEntityReplicationMask(entityId: number): Promise<boolean>await Network.setEntityReplicationMask(entityId, {
position: false,
rotation: false,
linearVelocity: false,
angularVelocity: false,
identity: true,
data: true,
});Notes #
- Default outgoing replication cadence is 20 Hz.
- Replication stream direction is listener -> incoming peer.
- Connecting peers send focus and full-state requests; they do not stream authoritative deltas upstream.
- When auth is enabled, replication/focus/full-state-request handling is admitted only after acceptance. Pending peers can be disconnected by timeout.
- Authentication is enforced by the listener for incoming peers; connecting clients do not transition auth state locally.
- Use
broadcast(..., { audience: 'incomingClientOnly' })for local-only FX/events that should appear on end clients but not sibling server-capable boards. - Board area defines coordinate frame translation between local simulation space and world replication space.
- Entity replication masks affect recurring delta packets. Full-state snapshots remain complete so newly visible replicas and mask changes can rebaseline cleanly.