Networking
The Playbit networking APIs allow for communication between apps via Playbit's own data protocols and infrastructure. These APIs are currently available as a technical preview, and are not yet well suited for sensitive information as encryption is not yet implemented.
PBNet
typedef struct PBNet {
PBSysHandle handle;
} PBNet;
PBNet is a general network capability
PBNetSession
typedef struct PBNetSession {
PBSysHandle handle;
} PBNetSession;
PBNetSession is a Playbit network session
PBNetTransport
typedef struct PBNetTransport {
PBSysHandle handle;
} PBNetTransport;
PBNetTransport represents a network connection
PBNetTransportConfig
typedef struct PBNetTransportConfig {
/*
idleTimeoutMs determines how long a connection may stay idle without I/O happening, in milliseconds. Once a connection has been idle for idleTimeoutMs, it's closed (assuming keep-alive is disabled.) A timeout of `0` means "default."
*/
u32 idleTimeoutMs;
PBSysNetTransportFlags flags;
/*
alpn is "Application-Layer Protocol Negotiation" for TLS and QUIC. A semicolon-separated ALPN list, in priority order.
Required to be non-zero for QUIC.
Currently only one ALPN is supported.
*/
PBStrSlice alpn;
// sni is "Server Name Indication" for TLS and QUIC
PBStrSlice sni;
// certPem is a PEM-encoded root certificate for TLS and QUIC peer verification
PBU8Slice certPem;
} PBNetTransportConfig;
PBNetTransportConfig configures a PBNetTransport
PBNetChannelOpenFlags
typedef u64 PBNetChannelOpenFlags;
PBNetChannelOpenFlags are flags for PBNetChannelOpenEx
PBNetChannelOpenFlag
enum PBNetChannelOpenFlag PB_ENUM_TYPE(u64){
/*
PBNetChannelOpenFlag_SUBSCRIBE causes the PBNetChannel to automatically send SUBSCRIBE messages whenever the underlying access point connection is established, or re-established. If this flag is not set, nothing will be delivered to the opened PBNetChannel, until you send a SUBSCRIBE message yourself. Not setting this flag can be useful if you are only writing to a channel.
*/
PBNetChannelOpenFlag_SUBSCRIBE = 1u << 0,
/*
PBNetChannelOpenFlag_TRACK_SUBSCRIBERS enables reception of SUBSCRIBE and UNSUBSCRIBE messages as other peers join and leave the channel. It can be used for participation discovery. PBNetChannelOpenFlag_SUBSCRIBE must be set if PBNetChannelOpenFlag_TRACK_SUBSCRIBERS is set.
Corresponds to setting PBApFrameFlag_TRACK in the AP protocol on outgoing SUBSCRIBE message.
*/
PBNetChannelOpenFlag_TRACK_SUBSCRIBERS = 1u << 1,
};
PBNetChannelPublishFlags
typedef u64 PBNetChannelPublishFlags;
PBNetChannelPublishFlags are flags for PBNetChannelPublishCommit et al
PBNetChannelPublishFlag
enum PBNetChannelPublishFlag PB_ENUM_TYPE(u64){
/*
PBNetChannelPublishFlag_ECHO makes the message delivered back to the sender, in addition to being sent to everyone else in a channel. It corresponds to PBApFrameFlag_ECHO in the AP protocol.
*/
PBNetChannelPublishFlag_ECHO = 1u << 0,
/*
PBNetChannelPublishFlag_DEFER disables sending of the message on publish. Useful if you are sending many small messages in rapid succession: set PBNetChannelPublishFlag_DEFER for the first N-1 publish calls and don't set this flag for the last.
Note that a call to PBNetChannelProcessIO will cause queued messages to be sent regardless of this flag, granted the underlying stream is writable.
*/
PBNetChannelPublishFlag_DEFER = 1u << 1,
};
PBNetChannelFrameType
typedef enum PBNetChannelFrameType PB_ENUM_TYPE(u8){
PBNetChannelFrameType_DIRECT = PBApFrameType_DIRECT,
PBNetChannelFrameType_PUBLISH = PBApFrameType_PUBLISH,
PBNetChannelFrameType_SUBSCRIBE = PBApFrameType_SUBSCRIBE,
PBNetChannelFrameType_UNSUBSCRIBE = PBApFrameType_UNSUBSCRIBE,
PBNetChannelFrameType_ERROR = PBApFrameType_ERROR,
PBNetChannelFrameType_ERROR_CHAN = PBApFrameType_ERROR_CHAN,
PBNetChannelFrameType_ERROR_SUB = PBApFrameType_ERROR_SUB,
PBNetChannelFrameType_ERROR_PEER = PBApFrameType_ERROR_PEER,
PBNetChannelFrameType_ERROR_DIRECT = PBApFrameType_ERROR_DIRECT,
} PBNetChannelFrameType;
PBNetChannelFrameType identifies frames delivered on a NetSession channel stream.
PBNetChannelFrame
typedef struct PBNetChannelFrame {
u32 bodyLen; // Size of body in bytes.
PBNetChannelFrameType type; // Frame type.
u8 flags; // Type-specific AP frame flags.
u8 senderIdLen; // Number of bytes at senderId.
u8 _reserved;
const u8* body; // Pointer to the frame body. Always 16-byte aligned.
u8 senderId[]; // Sender peer ID bytes. May be empty.
} PBNetChannelFrame;
PBNetChannelFrame is a frame of information received via a PBNetChannel
PBNetDefault
PBNet PBNetDefault();
PBNetDefault returns the default/initial network capability provided to the current thread when it was started.
Note: You can check .handle == PBSysHandle_INVALID on the return value to check if the current thread has a default network capability, or use PBExpectValidHandle to get a panic if not. All functions that take a PBNet as an argument will check its validity and panic if invalid, so for programs what want to simply crash if no networking is available, it's easier to just do no checking at all.
PBNetSessionOpen
PBSysErr PBNetSessionOpen(PBNet net,
PBNetSession* netSession,
PBStrSlice sessionId,
const PBSysNetSessionConfig* config);
PBNetSessionOpen opens a logical Playbit network session.
A Playbit network session allows communication between multiple playbit programs participating in the same session. A session houses "channels" (see PBNetChannelOpen and PBNetChannel).
net: Networking capabilitynetSession: Session to initializesessionId: Session identifier, unique within app-id namespaceconfig: Optional configuration. Pass NULL for defaults.
PBNetSessionClose
PBSysErr PBNetSessionClose(PBNetSession* netSession);
PBNetSessionClose closes a network session
PBNetChannelDirect
PBSysErr PBNetChannelDirect(PBNetChannel* netChannel,
const u8* dst,
u32 dstLen,
const u8* body,
u32 bodyLen);
PBNetChannelDirect send a direct message to a peer in a session.
PBNetChannelOpen
PBSysErr PBNetChannelOpen(PBNetChannel* netChannelOut,
const PBNetSession* netSession,
PBStrSlice channelId,
PBMem ma);
PBNetChannelOpen opens a stream to channel identified by channelId, and queues a SUBSCRIBE message.
PBNetChannelOpen is equivalent to calling PBNetChannelOpenEx with PBNetChannelOpenFlag_SUBSCRIBE.
netChannelOut: Points to an open PBNetChannel on successnetSession: The network session to open a channel inchannelId: The identifier of the channel, unique within the sessionma: Memory allocator to use for PBNetChannel and its resources. Must remain valid untilPBNetChannelClosehas been called.
PBNetChannelOpenEx
PBSysErr PBNetChannelOpenEx(PBNetChannel* netChannelOut,
const PBNetSession* netSession,
PBStrSlice channelId,
PBMem ma,
PBNetChannelOpenFlags flags);
PBNetChannelOpen opens a stream to channel identified by channelId.
netChannelOut: Points to an open PBNetChannel on successnetSession: The network session to open a channel inchannelId: The identifier of the channel, unique within the sessionma: Memory allocator to use for PBNetChannel and its resources. Must remain valid untilPBNetChannelClosehas been called.flags: Customizes behavior. SeePBNetChannelOpenFlags
PBNetChannelClose
void PBNetChannelClose(PBNetChannel* netChannel);
PBNetChannelClose closes the underlying stream, frees buffers owned by netChannel, and frees the channel object itself. The caller should discard any saved pointer after calling.
PBNetChannelHandle
PBSysHandle PBNetChannelHandle(const PBNetChannel* netChannel);
PBNetChannelHandle returns the underlying stream handle for a channel
PBNetChannelPublish
PBSysErr PBNetChannelPublish(PBNetChannel* netChannel,
const u8* body,
u32 bodyLen);
PBNetChannelPublish queues one complete PUBLISH frame by copying body into an internal buffer.
Equivalent to calling PBNetChannelPublishBegin with 0 flags, copying body and then calling PBNetChannelPublishCommit with 0 flags.
- Returns:
PBSysErr_TOO_LARGEifbodyLenis too large, as limited byPBNetChannelSetSendLimit
PBNetChannelPublishEx
PBSysErr PBNetChannelPublishEx(PBNetChannel* netChannel,
const u8* body,
u32 bodyLen,
PBNetChannelPublishFlags flags);
PBNetChannelPublishEx queues one complete PUBLISH frame with explicit AP frame flags.
Equivalent to calling PBNetChannelPublishBegin with flags, copying body and then calling PBNetChannelPublishCommit with flags.
- Returns:
PBSysErr_TOO_LARGEifbodyLenis too large, as limited byPBNetChannelSetSendLimit
PBNetChannelPublishBegin
PBSysErr PBNetChannelPublishBegin(PBNetChannel* netChannel,
u32 bodyLen,
u8* bodyOut,
PBNetChannelPublishFlags flags);
PBNetChannelPublishBegin allocates one outgoing PUBLISH frame.
On success, reserves space in the send buffer, stores the body pointer in bodyOut, and expects a matching call to PBNetChannelPublishCommit.
bodyOut: On success,*bodyOutis set to a pointer into an internal buffer of at leastbodyLensize, with at least 16 byte address alignment. The pointer remains valid untilPBNetChannelPublishCommitis called.- Returns:
PBSysErr_TOO_LARGEifbodyLenis too large, as limited byPBNetChannelSetSendLimit
PBNetChannelPublishCommit
PBSysErr PBNetChannelPublishCommit(PBNetChannel* netChannel,
u8* body,
PBNetChannelPublishFlags flags);
PBNetChannelPublishCommit commits a draft PUBLISH frame previously started with PBNetChannelPublishBegin.
body: Must be the exact pointer returned from the begin call
PBNetChannelProcessIO
PBSysErr PBNetChannelProcessIO(PBNetChannel* netChannel,
PBSysSignals signals,
PBNetChannelFrame* frameOut);
PBNetChannelProcessIO processes stream IO for a channel.
signals: Current streamsignalsfrom aPBEventType_SIGNALevent, but 0 is also valid and still allows flushing of buffered outgoing data.- Returns: On success, *frameOut is set to NULL if no complete frame is ready yet, or to a frame pointer owned by
netChannelif one frame was decoded. The returned frame remains valid untilPBNetChannelReleaseFrameis called.
PBNetChannelReleaseFrame
void PBNetChannelReleaseFrame(PBNetChannel* netChannel,
PBNetChannelFrame* frame);
PBNetChannelReleaseFrame releases a frame previously returned by PBNetChannelProcessIO. Frames must be released in the order they were received.
PBNetChannelRecvDiscardCount
u32 PBNetChannelRecvDiscardCount(const PBNetChannel* netChannel);
PBNetChannelRecvDiscardCount returns the number of received messages which were discarded because they exceeded size limit set by PBNetChannelSetRecvLimit
PBNetChannelSetRecvLimit
u32 PBNetChannelSetRecvLimit(PBNetChannel* netChannel,
u32 nbytes);
PBNetChannelSetRecvLimit limits the size of received messages (body, not including protocol header.)
nbytes: Max number of bytes to allow receiving.- Returns: Previous limit
PBNetChannelSetSendLimit
u32 PBNetChannelSetSendLimit(PBNetChannel* netChannel,
u32 nbytes);
PBNetChannelSetSendLimit limits the size of published messages (body, not including protocol header.)
nbytes: Max number of bytes per published message
PBNetTransportOpen
PBNetTransport PBNetTransportOpen(PBNet net,
PBStrSlice uri,
const PBNetTransportConfig* config);
PBNetTransportOpen opens a network transport (like a connection) to the named URI. See PBNetTransportOpenEx for more information.
This is a convenience function for the following:
PBNetTransport transport;
PanicOnErr(PBNetTransportOpenEx(net, &transport, uri, config));
PBNetTransportOpenStream
PBStream PBNetTransportOpenStream(PBNetTransport* transport);
PBNetTransportOpenStream opens a bidirectional stream within transport. See PBNetTransportOpenStreamEx for more information.
This is a convenience function for the following:
PBStream stream;
PanicOnErr(PBNetTransportOpenStreamEx(transport, &stream, PBSysNetTransportStream_RW));
PBNetTransportOpenEx
PBSysErr PBNetTransportOpenEx(PBNet net,
PBNetTransport* transportOut,
PBStrSlice uri,
const PBNetTransportConfig* config);
PBNetTransportOpenEx opens a network transport (like a connection) to the named URI.
URIs have the form protocol:node, i.e.
quic:host:port QUIC transport tcp:host:port TCP/IP socket udp:host:port UDP/IP socket
The underlying implementation may not support the requested protocol.
For quic: URIs, config->alpn is required. ALPN is a semicolon-separated list in priority order; currently only one ALPN is supported. When config->sni is empty, SNI defaults to the URI host.
PBNetTransportOpenStreamEx
PBSysErr PBNetTransportOpenStreamEx(PBNetTransport* transport,
PBStream* streamOut,
PBSysNetTransportStreamFlags flags);
PBNetTransportOpenStreamEx opens a stream within transport.
flags: dictates how the stream may be used
PBNetTransportSetKeepAlive
void PBNetTransportSetKeepAlive(PBNetTransport* transport,
PBTimeDuration interval);
PBNetTransportSetKeepAlive configures a transport's keep-alive interval. When keep-alive is enabled, a transport stays open even across long periods of no I/O. When keep-alive is disabled, a transport connection closes after idleTimeoutMs (as configured by PBSysNetTransportConfig.)
Setting interval to a negative value means "enable keep-alive with implemention-default interval" while setting interval to 0 means "disable keep-alive."
Keep-alive is disabled by default.
interval: How long between keep-alive pings
Playbit Access Point protocol
PBApFrameType
typedef enum PBApFrameType PB_ENUM_TYPE(u8){
PBApFrameType_HELLO = 0, // 'src' is peer ID on recv, body=auth_tok
PBApFrameType_REGISTER = 1, // 'flags' holds APRegisterResStatus
PBApFrameType_DIRECT = 2, // 'dst' is peer ID, 'flags' is unused
PBApFrameType_PUBLISH = 3, // 'dst' is channel ID, 'flags' may have ECHO
PBApFrameType_SUBSCRIBE = 4, // 'dst' is channel ID, 'flags' may have TRACK
PBApFrameType_UNSUBSCRIBE = 5, // 'dst' is channel ID, 'flags' is unused
PBApFrameType_ERROR = 200, // generic error (text description in data)
PBApFrameType_ERROR_CHAN = 201, // operation on non-existing channel
PBApFrameType_ERROR_SUB = 202, // already subscribed or not subscribed
PBApFrameType_ERROR_PEER = 203, // no route to peer
PBApFrameType_ERROR_DIRECT = 204, // failed to deliver direct message
} PBApFrameType;
PBApFrameFlags
typedef u8 PBApFrameFlags;
PBApFrameFlag
enum PBApFrameFlag PB_ENUM_TYPE(u8){
PBApFrameFlag_ECHO = 1u << 0,
PBApFrameFlag_TRACK = 1u << 0,
};
PBApFrameHeaderSize
const int PBApFrameHeaderSize = 8;
PBApFrameBodyAlign
const int PBApFrameBodyAlign = 16;
#include <playbit/multiplayer.h>
PBMPModType
typedef enum PBMPModType : uint8_t {
PBMPModType_NOP,
PBMPModType_SET,
PBMPModType_ADD,
PBMPModType_INSERT,
PBMPModType_REMOVE,
PBMPModType_RANGE,
PBMPModType_MAP,
PBMPModType_FINALIZE,
} PBMPModType;
PBMPRange
typedef struct PBMPRange {
uint32_t offset;
uint32_t count;
uint16_t key;
uint16_t reserved;
uint32_t dataLen;
void* _Nullable data;
} PBMPRange;
PBMPUUID
typedef struct PBMPUUID {
uint64_t p0;
uint64_t p1;
} PBMPUUID;
PBMPDoc
typedef struct PBMPDoc PBMPDoc;
PBMPObject
typedef struct PBMPObject PBMPObject;
PBMPDocCreate
PBMPDoc* PBMPDocCreate();
Documents:
PBMPDocOpen
PBMPDoc* PBMPDocOpen(PBMPUUID uuid,
PBMPUUID key);
PBMPDocGetUUID
PBMPUUID PBMPDocGetUUID(PBMPDoc* doc);
PBMPDocGetKey
PBMPUUID PBMPDocGetKey(PBMPDoc* doc);
PBMPDocDestroy
void PBMPDocDestroy(PBMPDoc* doc);
PBMPUUIDGenerate
PBMPUUID PBMPUUIDGenerate(PBMPDoc* doc);
PBMPRootObject
PBMPObject* PBMPRootObject(PBMPDoc* doc);
PBMPObjectLookup
PBMPObject* PBMPObjectLookup(PBMPDoc* doc,
PBMPUUID uuid);
PBMPObjectLookupChild
PBMPObject* PBMPObjectLookupChild(PBMPObject* object,
PBMPUUID uuid);
PBMPDocDoneFirstImport
bool PBMPDocDoneFirstImport(PBMPDoc* doc);
PBMPGetDataSize
u32 PBMPGetDataSize(PBMPObject* object);
Objects:
PBMPGetDataPtr
const u8* PBMPGetDataPtr(PBMPObject* object);
PBMPGetRanges
const PBMPRange* PBMPGetRanges(PBMPObject* object,
usize* count);
PBMPWriteData
void PBMPWriteData(PBMPObject* object,
u32 offset,
const void* bytes,
usize byteCount,
PBMPModType type);
PBMPRemoveData
void PBMPRemoveData(PBMPObject* object,
u32 offset,
u32 byteCount);
SET, ADD or INSERT
PBMPAddRange
void PBMPAddRange(PBMPObject* object,
u32 offset,
u32 byteCount,
u16 key,
const void* data,
usize dataLen);
PBMPWriteNew
PBMPObject* PBMPWriteNew(PBMPObject* object,
u32 offset,
bool link,
bool insert);
PBMPMapNew
PBMPObject* PBMPMapNew(PBMPObject* object,
PBStrSlice string,
bool link);
PBMPMapLookup
PBMPObject* PBMPMapLookup(PBMPObject* object,
PBStrSlice string);
PBMPMapIterate
PBMPObject* PBMPMapIterate(PBMPObject* object,
PBStrSlice* key,
uintptr_t* position);
PBMPModHook
typedef void (*PBMPModHook)(PBMPObject * _Nonnull, uint32_t, uint32_t, PBMPModType, const uint8_t * _Nullable, void * _Nullable);
PBMPAddModHook
void PBMPAddModHook(PBMPObject* object,
PBMPModHook hook,
void* contextPtr);
PBMPRemoveModHook
void PBMPRemoveModHook(PBMPObject* object,
PBMPModHook hook,
void* contextPtr);
PBMPMarkObject
bool PBMPMarkObject(PBMPObject* object);
PBMPSweepObjects
void PBMPSweepObjects(PBMPDoc* doc);
PBMPGetAndClearHasChangedFlag
bool PBMPGetAndClearHasChangedFlag(PBMPDoc* doc);
Serialization:
PBMPSerializeHistory
void* PBMPSerializeHistory(PBMPDoc* doc,
usize* mark,
usize* len,
u32 flags);
PBMPSerializeAll
void* PBMPSerializeAll(PBMPDoc* doc,
usize* len);
PBMPSerializeHead
void* PBMPSerializeHead(PBMPDoc* doc,
usize* len);
out
PBMPImport_REQUIRE_ENCRYPTION
out
PBMPImport
void PBMPImport(PBMPDoc* doc,
const void* ptr,
usize len,
u32 flags);
PBMPDone
void PBMPDone(void* ptr);
PBMPSubscribe
void PBMPSubscribe(PBMPDoc* doc,
void* reconnectDataPtr,
usize reconnectDataLen);
Networking:
PBMPUnsubscribe
void PBMPUnsubscribe(PBMPDoc* doc);
PBMPSerializeReconnectData
void* PBMPSerializeReconnectData(PBMPDoc* doc,
usize* len);
PBMPOnSignalEvent
void PBMPOnSignalEvent(i32 handle,
u32 signals);
out
PBMPOnTimer
void PBMPOnTimer();
PBMPRelayChanges
void PBMPRelayChanges();
PBMPSetSimulateOffline
void PBMPSetSimulateOffline(bool simulateOffline);
PBMPNetChannelOpenAssociated
PBSysErr PBMPNetChannelOpenAssociated(PBNetChannel* outChannel,
PBMPDoc* doc,
PBStrSlice name);
Open an network channel associted with the document, keyed by the provided name string.
Close with PBNetChannelClose as usual.