playbit / docs

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.


#include <playbit/net.h>

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).

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.

PBNetChannelOpenEx

PBSysErr PBNetChannelOpenEx(PBNetChannel*         netChannelOut,
                            const PBNetSession*   netSession,
                            PBStrSlice            channelId,
                            PBMem                 ma,
                            PBNetChannelOpenFlags flags);

PBNetChannelOpen opens a stream to channel identified by channelId.

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.

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.

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.

PBNetChannelPublishCommit

PBSysErr PBNetChannelPublishCommit(PBNetChannel*            netChannel,
                                   u8*                      body,
                                   PBNetChannelPublishFlags flags);

PBNetChannelPublishCommit commits a draft PUBLISH frame previously started with PBNetChannelPublishBegin.

PBNetChannelProcessIO

PBSysErr PBNetChannelProcessIO(PBNetChannel*      netChannel,
                               PBSysSignals       signals,
                               PBNetChannelFrame* frameOut);

PBNetChannelProcessIO processes stream IO for a channel.

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.)

PBNetChannelSetSendLimit

u32 PBNetChannelSetSendLimit(PBNetChannel* netChannel,
                             u32           nbytes);

PBNetChannelSetSendLimit limits the size of published messages (body, not including protocol header.)

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.

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.


#include <playbit/ap.h>

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

#define PBMPImport_REQUIRE_ENCRYPTION(1 << 0)

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.