From 4f65e7eccb23b9deb8417ed07b97a21b608bb2c8 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sat, 10 Aug 2024 10:30:07 +0200 Subject: [PATCH 01/85] wip --- nbnet.h | 1533 ++++++++++--------------------------------------------- 1 file changed, 263 insertions(+), 1270 deletions(-) diff --git a/nbnet.h b/nbnet.h index 94c756c..a53900c 100644 --- a/nbnet.h +++ b/nbnet.h @@ -105,7 +105,6 @@ typedef struct NBN_ConnectionTable enum { NBN_MEM_MESSAGE_CHUNK, - NBN_MEM_BYTE_ARRAY_MESSAGE, NBN_MEM_CONNECTION, #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) @@ -299,6 +298,7 @@ int NBN_MeasureStream_SerializeBool(NBN_MeasureStream *, bool *); int NBN_MeasureStream_SerializePadding(NBN_MeasureStream *); int NBN_MeasureStream_SerializeBytes(NBN_MeasureStream *, uint8_t *, unsigned int); void NBN_MeasureStream_Reset(NBN_MeasureStream *); +unsigned int NBN_MeasureStream_CountBytes(NBN_MeasureStream *); #pragma endregion /* NBN_MeasureStream */ @@ -310,34 +310,27 @@ void NBN_MeasureStream_Reset(NBN_MeasureStream *); #define NBN_LIBRARY_RESERVED_CHANNELS 3 #define NBN_MAX_MESSAGE_TYPES 255 /* Maximum value of uint8_t, see message header */ #define NBN_MESSAGE_RESEND_DELAY 0.1 /* Number of seconds before a message is resent (reliable messages redundancy) */ +#define NBN_MESSAGE_HEADER_SIZE 6 /* See NBN_MessageHeader struct */ typedef int (*NBN_MessageSerializer)(void *, NBN_Stream *); typedef void *(*NBN_MessageBuilder)(void); typedef void (*NBN_MessageDestructor)(void *); +// IMPORTANT: make sure you update NBN_MESSAGE_HEADER_SIZE if you modify NBN_MessageHeader struct typedef struct NBN_MessageHeader { uint16_t id; + uint16_t length; uint8_t type; uint8_t channel_id; } NBN_MessageHeader; -/* - * Holds the user message's data as well as a reference count for message recycling - */ -typedef struct NBN_OutgoingMessage -{ - uint8_t type; - unsigned int ref_count; - void *data; -} NBN_OutgoingMessage; - typedef struct NBN_Message { NBN_MessageHeader header; NBN_Connection *sender; - NBN_OutgoingMessage *outgoing_msg; /* NULL for incoming messages */ - void *data; + unsigned int ref_count; + uint8_t *data; } NBN_Message; /** @@ -352,7 +345,7 @@ typedef struct NBN_MessageInfo uint8_t channel_id; /** Message's data */ - void *data; + uint8_t *data; /** * The message's sender. @@ -362,72 +355,8 @@ typedef struct NBN_MessageInfo NBN_ConnectionHandle sender; } NBN_MessageInfo; -int NBN_Message_SerializeHeader(NBN_MessageHeader *, NBN_Stream *); -int NBN_Message_Measure(NBN_Message *, NBN_MeasureStream *, NBN_MessageSerializer); -int NBN_Message_SerializeData(NBN_Message *, NBN_Stream *, NBN_MessageSerializer); - #pragma endregion /* NBN_Message */ -#pragma region RPC - -#define NBN_RPC_MAX_PARAM_COUNT 16 /* Maximum number of parameters a RPC signature can accept */ -#define NBN_RPC_MAX 32 /* Maximum number of registered RPCs */ -#define NBN_RPC_STRING_MAX_LENGTH 256 /* Maximum length of a RPC string parameter */ - -/* Helper macros */ -#define NBN_RPC_BuildSignature(pc, ...) ((NBN_RPC_Signature){.param_count = pc, .params = {__VA_ARGS__}}) -#define NBN_RPC_Int(v) ((NBN_RPC_Param){.type = NBN_RPC_PARAM_INT, .value = {.i = v}}) -#define NBN_RPC_Float(v) ((NBN_RPC_Param){.type = NBN_RPC_PARAM_FLOAT, .value = {.f = v}}) -#define NBN_RPC_GetInt(params, idx) (params[idx].value.i) -#define NBN_RPC_GetFloat(params, idx) (params[idx].value.f) -#define NBN_RPC_GetBool(params, idx) (params[idx].value.b) -#define NBN_RPC_GetString(params, idx) (params[idx].value.s) - -typedef enum NBN_RPC_ParamType -{ - NBN_RPC_PARAM_INT, - NBN_RPC_PARAM_FLOAT, - NBN_RPC_PARAM_BOOL, - NBN_RPC_PARAM_STRING -} NBN_RPC_ParamType; - -typedef struct NBN_RPC_String -{ - char string[NBN_RPC_STRING_MAX_LENGTH]; - unsigned int length; -} NBN_RPC_String; - -typedef union NBN_RPC_ParamValue -{ - int i; - float f; - bool b; - char s[NBN_RPC_STRING_MAX_LENGTH]; -} NBN_RPC_ParamValue; - -typedef struct NBN_RPC_Param -{ - NBN_RPC_ParamType type; - NBN_RPC_ParamValue value; -} NBN_RPC_Param; - -typedef struct NBN_RPC_Signature -{ - unsigned int param_count; - NBN_RPC_ParamType params[NBN_RPC_MAX_PARAM_COUNT]; -} NBN_RPC_Signature; - -typedef void (*NBN_RPC_Func)(unsigned int, NBN_RPC_Param[NBN_RPC_MAX_PARAM_COUNT], NBN_ConnectionHandle sender); - -typedef struct NBN_RPC -{ - unsigned int id; - NBN_RPC_Signature signature; - NBN_RPC_Func func; -} NBN_RPC; - -#pragma endregion /* RPC */ - #pragma region NBN_Packet /* @@ -440,7 +369,7 @@ typedef struct NBN_RPC #define NBN_PACKET_MAX_SIZE 1400 #define NBN_MAX_MESSAGES_PER_PACKET 255 /* Maximum value of uint8_t, see packet header */ -#define NBN_PACKET_HEADER_SIZE sizeof(NBN_PacketHeader) +#define NBN_PACKET_HEADER_SIZE 13 /* Maximum size of packet's data (NBN_PACKET_MAX_DATA_SIZE + NBN_PACKET_HEADER_SIZE = NBN_PACKET_MAX_SIZE) */ #define NBN_PACKET_MAX_DATA_SIZE (NBN_PACKET_MAX_SIZE - NBN_PACKET_HEADER_SIZE) @@ -458,12 +387,13 @@ typedef enum NBN_PacketMode NBN_PACKET_MODE_READ } NBN_PacketMode; +// IMPORTANT: don't forget to update NBN_PACKET_HEADER_SIZE after modifying this structure typedef struct NBN_PacketHeader { uint32_t protocol_id; + uint32_t ack_bits; uint16_t seq_number; uint16_t ack; - uint32_t ack_bits; uint8_t messages_count; } NBN_PacketHeader; @@ -475,135 +405,32 @@ typedef struct NBN_Packet uint8_t buffer[NBN_PACKET_MAX_SIZE]; unsigned int size; /* in bytes */ bool sealed; - - // streams - NBN_WriteStream w_stream; - NBN_ReadStream r_stream; - NBN_MeasureStream m_stream; } NBN_Packet; void NBN_Packet_InitWrite(NBN_Packet *, uint32_t, uint16_t, uint16_t, uint32_t); -int NBN_Packet_InitRead(NBN_Packet *, NBN_Connection *, uint8_t[NBN_PACKET_MAX_SIZE], unsigned int); -uint32_t NBN_Packet_ReadProtocolId(uint8_t[NBN_PACKET_MAX_SIZE], unsigned int); -int NBN_Packet_WriteMessage(NBN_Packet *, NBN_Message *, NBN_MessageSerializer); +int NBN_Packet_WriteMessage(NBN_Packet *, NBN_Message *); int NBN_Packet_Seal(NBN_Packet *, NBN_Connection *); #pragma endregion /* NBN_Packet */ -#pragma region NBN_MessageChunk +#pragma region Library reserved messages -/* Chunk max size is the number of bytes of data a packet can hold minus the size of a message header minus 2 bytes +#define NBN_MESSAGE_CHUNK_TYPE (NBN_MAX_MESSAGE_TYPES) +#define NBN_CLIENT_CLOSED_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 1) +#define NBN_CLIENT_ACCEPTED_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 2) +#define NBN_DISCONNECTION_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 3) +#define NBN_CONNECTION_REQUEST_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 4) + +/* + * Chunk max size is the number of bytes of data a packet can hold minus the size of a message header minus 2 bytes * to hold the chunk id and total number of chunks. */ -#define NBN_MESSAGE_CHUNK_SIZE (NBN_PACKET_MAX_DATA_SIZE - sizeof(NBN_MessageHeader) - 2) -#define NBN_MESSAGE_CHUNK_TYPE (NBN_MAX_MESSAGE_TYPES - 1) /* Reserved message type for chunks */ +#define NBN_MESSAGE_CHUNK_SIZE (NBN_PACKET_MAX_DATA_SIZE - NBN_MESSAGE_HEADER_SIZE - 2) -typedef struct NBN_MessageChunk -{ - uint8_t id; - uint8_t total; - uint8_t data[NBN_MESSAGE_CHUNK_SIZE]; - NBN_OutgoingMessage *outgoing_msg; -} NBN_MessageChunk; - -NBN_MessageChunk *NBN_MessageChunk_Create(void); -void NBN_MessageChunk_Destroy(NBN_MessageChunk *); -int NBN_MessageChunk_Serialize(NBN_MessageChunk *, NBN_Stream *); - -#pragma endregion /* NBN_MessageChunk */ - -#pragma region NBN_ClientClosedMessage - -#define NBN_CLIENT_CLOSED_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 2) /* Reserved message type */ - -typedef struct NBN_ClientClosedMessage -{ - int code; -} NBN_ClientClosedMessage; - -NBN_ClientClosedMessage *NBN_ClientClosedMessage_Create(void); -void NBN_ClientClosedMessage_Destroy(NBN_ClientClosedMessage *); -int NBN_ClientClosedMessage_Serialize(NBN_ClientClosedMessage *, NBN_Stream *); - -#pragma endregion /* NBN_ClientClosedMessage */ - -#pragma region NBN_ClientAcceptedMessage - -#define NBN_CLIENT_ACCEPTED_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 3) /* Reserved message type */ #define NBN_SERVER_DATA_MAX_SIZE 1024 #define NBN_CONNECTION_DATA_MAX_SIZE 512 -typedef struct NBN_ClientAcceptedMessage -{ - unsigned int length; - uint8_t data[NBN_SERVER_DATA_MAX_SIZE]; -} NBN_ClientAcceptedMessage; - -NBN_ClientAcceptedMessage *NBN_ClientAcceptedMessage_Create(void); -void NBN_ClientAcceptedMessage_Destroy(NBN_ClientAcceptedMessage *); -int NBN_ClientAcceptedMessage_Serialize(NBN_ClientAcceptedMessage *, NBN_Stream *); - -#pragma endregion /* NBN_ClientAcceptedMessage */ - -#pragma region NBN_ByteArrayMessage - -#define NBN_BYTE_ARRAY_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 4) /* Reserved message type */ -#define NBN_BYTE_ARRAY_MAX_SIZE 4096 - -typedef struct NBN_ByteArrayMessage -{ - uint8_t bytes[NBN_BYTE_ARRAY_MAX_SIZE]; - unsigned int length; -} NBN_ByteArrayMessage; - -NBN_ByteArrayMessage *NBN_ByteArrayMessage_Create(void); -void NBN_ByteArrayMessage_Destroy(NBN_ByteArrayMessage *); -int NBN_ByteArrayMessage_Serialize(NBN_ByteArrayMessage *, NBN_Stream *); - -#pragma endregion /* NBN_ByteArrayMessage */ - -#pragma region NBN_DisconnectionMessage - -#define NBN_DISCONNECTION_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 7) /* Reserved message type */ - -void *NBN_DisconnectionMessage_Create(void); -void NBN_DisconnectionMessage_Destroy(void *); -int NBN_DisconnectionMessage_Serialize(void *, NBN_Stream *); - -#pragma endregion /* NBN_DisconnectionMessage */ - -#pragma region NBN_ConnectionRequestMessage - -#define NBN_CONNECTION_REQUEST_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 8) /* Reserved message type */ - -typedef struct NBN_ConnectionRequestMessage -{ - unsigned int length; - uint8_t data[NBN_CONNECTION_DATA_MAX_SIZE]; -} NBN_ConnectionRequestMessage; - -NBN_ConnectionRequestMessage *NBN_ConnectionRequestMessage_Create(void); -void NBN_ConnectionRequestMessage_Destroy(NBN_ConnectionRequestMessage *); -int NBN_ConnectionRequestMessage_Serialize(NBN_ConnectionRequestMessage *, NBN_Stream *); - -#pragma endregion /* NBN_ConnectionRequestMessage */ - -#pragma region NBN_RPC_Message - -#define NBN_RPC_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 9) /* Reserved message type */ - -typedef struct NBN_RPC_Message -{ - unsigned int id; - unsigned int param_count; - NBN_RPC_Param params[NBN_RPC_MAX_PARAM_COUNT]; -} NBN_RPC_Message; - -void *NBN_RPC_Message_Create(void); -void NBN_RPC_Message_Destroy(NBN_RPC_Message *); -int NBN_RPC_Message_Serialize(NBN_RPC_Message *, NBN_Stream *); - -#pragma endregion /* NBN_RPC_Message */ +#pragma endregion /* Library reserved messages */ #pragma region NBN_Channel @@ -652,7 +479,7 @@ struct NBN_Channel NBN_MessageSlot outgoing_message_slot_buffer[NBN_CHANNEL_BUFFER_SIZE]; NBN_MessageSlot recved_message_slot_buffer[NBN_CHANNEL_BUFFER_SIZE]; NBN_MessageChunk *recv_chunk_buffer[NBN_CHANNEL_CHUNKS_BUFFER_SIZE]; - NBN_OutgoingMessage outgoing_message_pool[NBN_CHANNEL_OUTGOING_MESSAGE_POOL_SIZE]; + NBN_Message outgoing_message_pool[NBN_CHANNEL_OUTGOING_MESSAGE_POOL_SIZE]; bool (*AddReceivedMessage)(NBN_Channel *, NBN_Message *); bool (*AddOutgoingMessage)(NBN_Channel *, NBN_Message *); @@ -796,7 +623,6 @@ struct NBN_ConnectionListNode NBN_Connection *NBN_Connection_Create(uint32_t, uint32_t, NBN_Endpoint *, NBN_Driver *, void *); void NBN_Connection_Destroy(NBN_Connection *); int NBN_Connection_ProcessReceivedPacket(NBN_Connection *, NBN_Packet *, double); -int NBN_Connection_EnqueueOutgoingMessage(NBN_Connection *, NBN_Channel *, NBN_Message *); int NBN_Connection_FlushSendQueue(NBN_Connection *, double); int NBN_Connection_InitChannel(NBN_Connection *, NBN_Channel *); bool NBN_Connection_CheckIfStale(NBN_Connection *, double); @@ -925,18 +751,13 @@ void NBN_PacketSimulator_Stop(NBN_PacketSimulator *); #define NBN_IsReservedMessage(type) (type == NBN_MESSAGE_CHUNK_TYPE || type == NBN_CLIENT_CLOSED_MESSAGE_TYPE \ || type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE || type == NBN_BYTE_ARRAY_MESSAGE_TYPE \ -|| type == NBN_DISCONNECTION_MESSAGE_TYPE || type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE \ -|| type == NBN_RPC_MESSAGE_TYPE) +|| type == NBN_DISCONNECTION_MESSAGE_TYPE || type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE) struct NBN_Endpoint { NBN_ChannelBuilder channel_builders[NBN_MAX_CHANNELS]; NBN_ChannelDestructor channel_destructors[NBN_MAX_CHANNELS]; - NBN_MessageBuilder message_builders[NBN_MAX_MESSAGE_TYPES]; - NBN_MessageDestructor message_destructors[NBN_MAX_MESSAGE_TYPES]; - NBN_MessageSerializer message_serializers[NBN_MAX_MESSAGE_TYPES]; NBN_EventQueue event_queue; - NBN_RPC rpcs[NBN_RPC_MAX]; bool is_server; double time; /* Current time */ @@ -1017,21 +838,6 @@ void NBN_GameClient_Stop(void); */ unsigned int NBN_GameClient_ReadServerData(uint8_t *data); -/** - * Register a type of message on the game client, has to be called after NBN_GameClient_Start. - * - * - * @param msg_type A user defined message type, can be any value from 0 to 245 (245 to 255 are reserved by nbnet). - * @param msg_builder The function responsible for building the message - * @param msg_destructor The function responsible for destroying the message (and releasing memory) - * @param msg_serializer The function responsible for serializing the message - */ -void NBN_GameClient_RegisterMessage( - uint8_t msg_type, - NBN_MessageBuilder msg_builder, - NBN_MessageDestructor msg_destructor, - NBN_MessageSerializer msg_serializer); - /** * Poll game client events. * @@ -1051,73 +857,42 @@ int NBN_GameClient_Poll(void); */ int NBN_GameClient_SendPackets(void); -/** - * Send a byte array message on a given channel. - * - * It's recommended to use NBN_GameClient_SendUnreliableByteArray or NBN_GameClient_SendReliableByteArray - * unless you really want to use a specific channel. - * - * @param bytes The byte array to send - * @param length The length of the byte array to send - * @param channel_id The ID of the channel to send the message on - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameClient_SendByteArray(uint8_t *bytes, unsigned int length, uint8_t channel_id); - /** * Send a message to the server on a given channel. * * It's recommended to use NBN_GameClient_SendUnreliableMessage or NBN_GameClient_SendReliableMessage * unless you really want to use a specific channel. * - * @param msg_type The type of message to send; it needs to be registered (see NBN_GameClient_RegisterMessage) + * @param type The user-defined type of message to send * @param channel_id The ID of the channel to send the message on - * @param msg_data A pointer to the message to send (managed by user code) + * @param data A pointer to the message data buffer (managed by user code) + * @param length The length of the message data buffer in bytes * * @return 0 when successful, -1 otherwise */ -int NBN_GameClient_SendMessage(uint8_t msg_type, uint8_t channel_id, void *msg_data); +int NBN_GameClient_SendMessage(uint8_t type, uint8_t channel_id, uint8_t *data, uint16_t length); /** * Send a message to the server, unreliably. * - * @param msg_type The type of message to send; it needs to be registered (see NBN_GameClient_RegisterMessage) - * @param msg_data A pointer to the message to send (managed by user code) + * @param type The type of message to send + * @param data A pointer to the message data buffer (managed by user code) + * @param length The length of the message data buffer in bytes * * @return 0 when successful, -1 otherwise */ -int NBN_GameClient_SendUnreliableMessage(uint8_t msg_type, void *msg_data); +int NBN_GameClient_SendUnreliableMessage(uint8_t type, uint8_t *data, uint16_t length); /** * Send a message to the server, reliably. * - * @param msg_type The type of message to send; it needs to be registered (see NBN_GameClient_RegisterMessage) - * @param msg_data A pointer to the message to send (pointing to user code memory) - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameClient_SendReliableMessage(uint8_t msg_type, void *msg_data); - -/* - * Send a byte array to the server, unreliably. - * - * @param bytes The byte array to send - * @param length The length of the byte array to send + * @param type The type of message to send + * @param data A pointer to the message data buffer (managed by user code) + * @param length The length of the message data buffer in bytes * * @return 0 when successful, -1 otherwise */ -int NBN_GameClient_SendUnreliableByteArray(uint8_t *bytes, unsigned int length); - -/* - * Send a byte array to the server, reliably. - * - * @param bytes The byte array to send - * @param length The length of the byte array to send - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameClient_SendReliableByteArray(uint8_t *bytes, unsigned int length); +int NBN_GameClient_SendReliableMessage(uint8_t type, uint8_t *data, uint16_t length); /** * For drivers only! NOT MEANT TO BE USED BY USER CODE. @@ -1155,26 +930,6 @@ int NBN_GameClient_GetServerCloseCode(void); */ bool NBN_GameClient_IsConnected(void); -/** - * Register a new RPC on the game client. - * - * The same RPC must be register on both the game server and the game clients. - * - * @param id User defined RPC ID, must be an integer between 0 and NBN_RPC_MAX - * @param signature The RPC signature - * @param func The function to call on the callee end (NULL on the caller end) - * - * @return true if packet encryption is enabled, false otherwise - */ -int NBN_GameClient_RegisterRPC(unsigned int id, NBN_RPC_Signature signature, NBN_RPC_Func func); - -/** - * Call a previously registered RPC on the game server. - * - * @param id The ID of the RPC to execute on the game server (must be a registered ID) - */ -int NBN_GameClient_CallRPC(unsigned int id, ...); - #ifdef NBN_DEBUG void NBN_GameClient_Debug_RegisterCallback(NBN_ConnectionDebugCallback, void *); @@ -1244,18 +999,6 @@ int NBN_GameServer_StartEx(const char *protocol_name, uint16_t port); */ void NBN_GameServer_Stop(void); -/** - * Register a type of message on the game server, has to be called after NBN_GameServer_Start. - * - * - * @param msg_type A user defined message type, can be any value from 0 to 245 (245 to 255 are reserved by nbnet). - * @param msg_builder The function responsible for building the message - * @param msg_destructor The function responsible for destroying the message (and releasing memory) - * @param msg_serializer The function responsible for serializing the message - */ -void NBN_GameServer_RegisterMessage( - uint8_t msg_type, NBN_MessageBuilder msg_builder, NBN_MessageDestructor msg_destructor, NBN_MessageSerializer msg_serializer); - /** * Poll game server events. * @@ -1301,18 +1044,6 @@ int NBN_GameServer_CloseClient(NBN_ConnectionHandle connection_handle); */ int NBN_GameServer_CloseClientWithCode(NBN_ConnectionHandle connection_handle, int code); -/** - * Send a byte array to a client on a given channel. - * - * @param connection_handle The connection to send the message to - * @param bytes The byte array to send - * @param length The length of the byte array to send - * @param channel_id The ID of the channel to send the message on - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_SendByteArrayTo(NBN_ConnectionHandle connection_handle, uint8_t *bytes, unsigned int length, uint8_t channel_id); - /** * Send a message to a client on a given channel. * @@ -1320,57 +1051,38 @@ int NBN_GameServer_SendByteArrayTo(NBN_ConnectionHandle connection_handle, uint8 * unless you really want to use a specific channel. * * @param connection_handle The connection to send the message to - * @param msg_type The type of message to send; it needs to be registered (see NBN_GameServer_RegisterMessage) + * @param type The type of message to send * @param channel_id The ID of the channel to send the message on - * @param msg_data A pointer to the message to send + * @param data A pointer to the message data buffer (managed by user code) + * @param length The length of the message data buffer in bytes * * @return 0 when successful, -1 otherwise */ -int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, uint8_t channel_id, void *msg_data); +int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle, uint8_t type, uint8_t channel_id, uint8_t *data, uint16_t length); /** * Send a message to a client, unreliably. * * @param connection_handle The connection to send the message to - * @param msg_type The type of message to send; it needs to be registered (see NBN_GameServer_RegisterMessage) - * @param msg_data A pointer to the message to send (managed by user code) + * @param type The type of message to send + * @param data A pointer to the message data buffer (managed by user code) + * @param length The length of the message data buffer in bytes * * @return 0 when successful, -1 otherwise */ -int NBN_GameServer_SendUnreliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, void *msg_data); +int NBN_GameServer_SendUnreliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t type, uint8_t *data, uint16_t length); /** * Send a message to a client, reliably. * * @param connection_handle The connection to send the message to - * @param msg_type The type of message to send; it needs to be registered (see NBN_GameServer_RegisterMessage) - * @param msg_data A pointer to the message to send (managed by user code) - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_SendReliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, void *msg_data); - -/** - * Send a byte array to a client, unreliably. - * - * @param connection_handle The connection to send the message to - * @param bytes The byte array to send - * @param length The length of the byte array to send + * @param type The type of message to send + * @param data A pointer to the message data buffer (managed by user code) + * @param length The length of the message data buffer in bytes * * @return 0 when successful, -1 otherwise */ -int NBN_GameServer_SendUnreliableByteArrayTo(NBN_ConnectionHandle connection_handle, uint8_t *bytes, unsigned int length); - -/** - * Send a byte array to a client, reliably. - * - * @param connection_handle The connection to send the message to - * @param bytes The byte array to send - * @param length The length of the byte array to send - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_SendReliableByteArrayTo(NBN_ConnectionHandle connection_handle, uint8_t *bytes, unsigned int length); +int NBN_GameServer_SendReliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t type, uint8_t *data, uint16_t length); /** * Accept the last client connection request and send a blob of data to the client. @@ -1458,27 +1170,6 @@ NBN_MessageInfo NBN_GameServer_GetMessageInfo(void); */ NBN_GameServerStats NBN_GameServer_GetStats(void); -/** - * Register a new RPC on the game server. - * - * The same RPC must be register on both the game server and the game clients. - * - * @param id User defined RPC ID, must be an integer between 0 and NBN_RPC_MAX - * @param signature The RPC signature - * @param func The function to call on the callee end (NULL on the caller end) - * - * @return true if packet encryption is enabled, false otherwise - */ -int NBN_GameServer_RegisterRPC(unsigned int id, NBN_RPC_Signature signature, NBN_RPC_Func func); - -/** - * Call a previously registered RPC on a given client. - * - * @param id The ID of the RPC to execute (must be a registered ID) - * @param connection_handle The connection on which to call the RPC - */ -int NBN_GameServer_CallRPC(unsigned int id, NBN_ConnectionHandle connection_handle, ...); - #ifdef NBN_DEBUG void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback, void *); @@ -1887,7 +1578,6 @@ static void MemoryManager_Init(void) NBN_LogDebug("MemoryManager_Init without pooling!"); nbn_mem_manager.mem_sizes[NBN_MEM_MESSAGE_CHUNK] = sizeof(NBN_MessageChunk); - nbn_mem_manager.mem_sizes[NBN_MEM_BYTE_ARRAY_MESSAGE] = sizeof(NBN_ByteArrayMessage); nbn_mem_manager.mem_sizes[NBN_MEM_CONNECTION] = sizeof(NBN_Connection); #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) @@ -1897,7 +1587,6 @@ static void MemoryManager_Init(void) NBN_LogDebug("MemoryManager_Init with pooling!"); MemPool_Init(&nbn_mem_manager.mem_pools[NBN_MEM_MESSAGE_CHUNK], sizeof(NBN_MessageChunk), 256); - MemPool_Init(&nbn_mem_manager.mem_pools[NBN_MEM_BYTE_ARRAY_MESSAGE], sizeof(NBN_ByteArrayMessage), 256); MemPool_Init(&nbn_mem_manager.mem_pools[NBN_MEM_CONNECTION], sizeof(NBN_Connection), 16); #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) @@ -1910,7 +1599,6 @@ static void MemoryManager_Deinit(void) { #if !defined(NBN_DISABLE_MEMORY_POOLING) MemPool_Deinit(&nbn_mem_manager.mem_pools[NBN_MEM_MESSAGE_CHUNK]); - MemPool_Deinit(&nbn_mem_manager.mem_pools[NBN_MEM_BYTE_ARRAY_MESSAGE]); MemPool_Deinit(&nbn_mem_manager.mem_pools[NBN_MEM_CONNECTION]); #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) @@ -2452,8 +2140,7 @@ void NBN_MeasureStream_Init(NBN_MeasureStream *measure_stream) measure_stream->number_of_bits = 0; } -int NBN_MeasureStream_SerializeUint( - NBN_MeasureStream *measure_stream, unsigned int *value, unsigned int min, unsigned int max) +int NBN_MeasureStream_SerializeUint(NBN_MeasureStream *measure_stream, unsigned int *value, unsigned int min, unsigned int max) { (void)*value; @@ -2482,8 +2169,7 @@ int NBN_MeasureStream_SerializeInt(NBN_MeasureStream *measure_stream, int *value unsigned int abs_min = MIN(abs(min), abs(max)); unsigned int abs_max = MAX(abs(min), abs(max)); unsigned int abs_value = abs(*value); - unsigned number_of_bits = NBN_MeasureStream_SerializeUint( - measure_stream, &abs_value, (min < 0 && max > 0) ? 0 : abs_min, abs_max); + unsigned number_of_bits = NBN_MeasureStream_SerializeUint(measure_stream, &abs_value, (min < 0 && max > 0) ? 0 : abs_min, abs_max); measure_stream->number_of_bits++; // +1 for int sign @@ -2543,16 +2229,18 @@ void NBN_MeasureStream_Reset(NBN_MeasureStream *measure_stream) measure_stream->number_of_bits = 0; } +unsigned int NBN_MeasureStream_CountBytes(NBN_MeasureStream *measure_stream) +{ + return (measure_stream->number_of_bits + 8) / 8; +} + #pragma endregion /* NBN_MeasureStream */ #pragma endregion /* Serialization */ #pragma region NBN_Packet -static int Packet_SerializeHeader(NBN_PacketHeader *, NBN_Stream *); - -void NBN_Packet_InitWrite( - NBN_Packet *packet, uint32_t protocol_id, uint16_t seq_number, uint16_t ack, uint32_t ack_bits) +void NBN_Packet_InitWrite(NBN_Packet *packet, uint32_t protocol_id, uint16_t seq_number, uint16_t ack, uint32_t ack_bits) { packet->header.protocol_id = protocol_id; packet->header.messages_count = 0; @@ -2564,77 +2252,51 @@ void NBN_Packet_InitWrite( packet->sender = NULL; packet->size = 0; packet->sealed = false; - packet->m_stream.number_of_bits = 0; - - NBN_WriteStream_Init(&packet->w_stream, packet->buffer + NBN_PACKET_HEADER_SIZE, NBN_PACKET_MAX_DATA_SIZE); - NBN_MeasureStream_Init(&packet->m_stream); } -int NBN_Packet_InitRead( - NBN_Packet *packet, NBN_Connection *sender, uint8_t buffer[NBN_PACKET_MAX_SIZE], unsigned int size) +int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_Message *message) { - packet->mode = NBN_PACKET_MODE_READ; - packet->sender = sender; - packet->size = size; - packet->sealed = false; - - memcpy(packet->buffer, buffer, size); - - NBN_ReadStream header_r_stream; - - NBN_ReadStream_Init(&header_r_stream, packet->buffer, NBN_PACKET_HEADER_SIZE); - - if (Packet_SerializeHeader(&packet->header, (NBN_Stream *)&header_r_stream) < 0) - return NBN_ERROR; - - NBN_ReadStream_Init(&packet->r_stream, packet->buffer + NBN_PACKET_HEADER_SIZE, packet->size); - - return 0; -} + if (packet->mode != NBN_PACKET_MODE_WRITE || packet->sealed) + return NBN_PACKET_WRITE_ERROR; -uint32_t NBN_Packet_ReadProtocolId(uint8_t buffer[NBN_PACKET_MAX_SIZE], unsigned int size) -{ - if (size < NBN_PACKET_HEADER_SIZE) - return 0; + unsigned int message_size = NBN_MESSAGE_HEADER_SIZE + message->header.length; - NBN_ReadStream r_stream; + if ( + packet->header.messages_count >= NBN_MAX_MESSAGES_PER_PACKET || + packet->size + message_size > NBN_PACKET_MAX_DATA_SIZE) + { + return NBN_PACKET_WRITE_NO_SPACE; + } - NBN_ReadStream_Init(&r_stream, buffer, NBN_PACKET_HEADER_SIZE); + // Write message header + // TODO: endianess - NBN_PacketHeader header; + uint8_t *packet_ptr = packet->buffer + packet->size; - if (Packet_SerializeHeader(&header, (NBN_Stream *)&r_stream) < 0) - return 0; + *((uint16_t *)packet_ptr) = message->header.id; + packet_ptr += 2; + *((uint16_t *)packet_ptr) = message->header.length; + packet_ptr += 2; + *packet_ptr = message->header.type; + packet_ptr++; + *packet_ptr = message->header.channel_id; + packet_ptr++; - return header.protocol_id; -} + assert(packet_ptr - packet->buffer == NBN_MESSAGE_HEADER_SIZE); -int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_Message *message, NBN_MessageSerializer msg_serializer) -{ - if (packet->mode != NBN_PACKET_MODE_WRITE || packet->sealed) - return NBN_PACKET_WRITE_ERROR; + // Write message data - int current_number_of_bits = packet->m_stream.number_of_bits; + assert((packet->buffer + sizeof(packet->buffer)) - packet_ptr >= message->header.length); - if (NBN_Message_Measure(message, &packet->m_stream, msg_serializer) < 0) - return NBN_PACKET_WRITE_ERROR; + assert(message->data != NULL || message->header.length == 0); - if ( - packet->header.messages_count >= NBN_MAX_MESSAGES_PER_PACKET || - packet->m_stream.number_of_bits > NBN_PACKET_MAX_DATA_SIZE * 8) + if (message->data) { - packet->m_stream.number_of_bits = current_number_of_bits; - - return NBN_PACKET_WRITE_NO_SPACE; + assert(message->header.length > 0); + memcpy(packet_ptr, message->data, message->header.length); } - if (NBN_Message_SerializeHeader(&message->header, (NBN_Stream *)&packet->w_stream) < 0) - return NBN_PACKET_WRITE_ERROR; - - if (NBN_Message_SerializeData(message, (NBN_Stream *)&packet->w_stream, msg_serializer) < 0) - return NBN_PACKET_WRITE_ERROR; - - packet->size = (packet->m_stream.number_of_bits - 1) / 8 + 1; + packet->size += message_size; packet->header.messages_count++; return NBN_PACKET_WRITE_OK; @@ -2645,327 +2307,87 @@ int NBN_Packet_Seal(NBN_Packet *packet, NBN_Connection *connection) if (packet->mode != NBN_PACKET_MODE_WRITE) return NBN_ERROR; - if (NBN_WriteStream_Flush(&packet->w_stream) < 0) - return NBN_ERROR; - - packet->size += NBN_PACKET_HEADER_SIZE; - - NBN_WriteStream header_w_stream; - - NBN_WriteStream_Init(&header_w_stream, packet->buffer, NBN_PACKET_HEADER_SIZE); - - if (Packet_SerializeHeader(&packet->header, (NBN_Stream *)&header_w_stream) < 0) - return NBN_ERROR; + uint8_t *packet_buffer = packet->buffer; - if (NBN_WriteStream_Flush(&header_w_stream) < 0) - return NBN_ERROR; + *((uint32_t *)packet_buffer) = packet->header.protocol_id; + packet_buffer += 4; + *((uint32_t *)packet_buffer) = packet->header.ack_bits; + packet_buffer += 4; + *((uint16_t *)packet_buffer) = packet->header.seq_number; + packet_buffer += 2; + *((uint16_t *)packet_buffer) = packet->header.ack; + packet_buffer += 2; + *packet_buffer = packet->header.messages_count; + packet->size += NBN_PACKET_HEADER_SIZE; packet->sealed = true; return 0; } -static int Packet_SerializeHeader(NBN_PacketHeader *header, NBN_Stream *stream) -{ - NBN_SerializeBytes(stream, &header->protocol_id, sizeof(header->protocol_id)); - NBN_SerializeBytes(stream, &header->seq_number, sizeof(header->seq_number)); - NBN_SerializeBytes(stream, &header->ack, sizeof(header->ack)); - NBN_SerializeBytes(stream, &header->ack_bits, sizeof(header->ack_bits)); - NBN_SerializeBytes(stream, &header->messages_count, sizeof(header->messages_count)); - - return 0; -} - #pragma endregion /* NBN_Packet */ -#pragma region NBN_Message +#pragma region NBN_Connection -int NBN_Message_SerializeHeader(NBN_MessageHeader *message_header, NBN_Stream *stream) -{ - NBN_SerializeBytes(stream, &message_header->id, sizeof(message_header->id)); - NBN_SerializeBytes(stream, &message_header->type, sizeof(message_header->type)); - NBN_SerializeBytes(stream, &message_header->channel_id, sizeof(message_header->channel_id)); +static NBN_Message *Endpoint_CreateOutgoingMessage(NBN_Endpoint *, NBN_Channel*, uint8_t, uint8_t *, uint16_t); - return 0; -} +static uint32_t Connection_BuildPacketAckBits(NBN_Connection *); +static int Connection_DecodePacketHeader(NBN_Connection *, NBN_Packet *, double); +static int Connection_AckPacket(NBN_Connection *, uint16_t, double time); +static void Connection_InitOutgoingPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry **); +static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *, uint16_t); +static bool Connection_InsertReceivedPacketEntry(NBN_Connection *, uint16_t); +static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *, uint16_t); +static bool Connection_IsPacketReceived(NBN_Connection *, uint16_t); +static int Connection_SendPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry *, double); +static int Connection_ReadNextMessageFromBuffer(NBN_Connection *, uint8_t *, NBN_Message *); +static void Connection_RecycleMessage(NBN_Channel *, NBN_Message *); +static void Connection_UpdateAveragePing(NBN_Connection *, double); +static void Connection_UpdateAveragePacketLoss(NBN_Connection *, uint16_t); +static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *, float); +static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *, double); -int NBN_Message_Measure(NBN_Message *message, NBN_MeasureStream *m_stream, NBN_MessageSerializer msg_serializer) +NBN_Connection *NBN_Connection_Create(uint32_t id, uint32_t protocol_id, NBN_Endpoint *endpoint, NBN_Driver *driver, void *driver_data) { - if (NBN_Message_SerializeHeader(&message->header, (NBN_Stream *)m_stream) < 0) - return NBN_ERROR; - - if (NBN_Message_SerializeData(message, (NBN_Stream *)m_stream, msg_serializer) < 0) - return NBN_ERROR; - - return m_stream->number_of_bits; -} + NBN_Connection *connection = (NBN_Connection*)MemoryManager_Alloc(NBN_MEM_CONNECTION); -int NBN_Message_SerializeData(NBN_Message *message, NBN_Stream *stream, NBN_MessageSerializer msg_serializer) -{ - return msg_serializer(message->data, stream); -} + connection->id = id; + connection->protocol_id = protocol_id; + connection->endpoint = endpoint; + connection->last_recv_packet_time = endpoint->time; + connection->next_packet_seq_number = 1; + connection->last_received_packet_seq_number = 0; + connection->last_flush_time = endpoint->time; + connection->last_read_packets_time = endpoint->time; + connection->downloaded_bytes = 0; + connection->is_accepted = false; + connection->is_stale = false; + connection->is_closed = false; + connection->vector_pos = -1; -#pragma endregion /* NBN_Message */ + for (int i = 0; i < NBN_MAX_CHANNELS; i++) + connection->channels[i] = NULL; -#pragma region NBN_MessageChunk + for (int i = 0; i < NBN_MAX_PACKET_ENTRIES; i++) + { + connection->packet_send_seq_buffer[i] = 0xFFFFFFFF; + connection->packet_recv_seq_buffer[i] = 0xFFFFFFFF; + } -NBN_MessageChunk *NBN_MessageChunk_Create(void) -{ - NBN_MessageChunk *chunk = (NBN_MessageChunk*)MemoryManager_Alloc(NBN_MEM_MESSAGE_CHUNK); + NBN_ConnectionStats stats = { 0 }; - chunk->outgoing_msg = NULL; + connection->stats = stats; + connection->driver = driver; + connection->driver_data = driver_data; - return chunk; + return connection; } -void NBN_MessageChunk_Destroy(NBN_MessageChunk *chunk) +void NBN_Connection_Destroy(NBN_Connection *connection) { - MemoryManager_Dealloc(chunk, NBN_MEM_MESSAGE_CHUNK); -} - -int NBN_MessageChunk_Serialize(NBN_MessageChunk *msg, NBN_Stream *stream) -{ - NBN_SerializeBytes(stream, &msg->id, 1); - NBN_SerializeBytes(stream, &msg->total, 1); - NBN_SerializeBytes(stream, msg->data, NBN_MESSAGE_CHUNK_SIZE); - - return 0; -} - -#pragma endregion /* NBN_MessageChunk */ - -#pragma region NBN_ClientClosedMessage - -NBN_ClientClosedMessage *NBN_ClientClosedMessage_Create(void) -{ - return (NBN_ClientClosedMessage*)NBN_Allocator(sizeof(NBN_ClientClosedMessage)); -} - -void NBN_ClientClosedMessage_Destroy(NBN_ClientClosedMessage *msg) -{ - NBN_Deallocator(msg); -} - -int NBN_ClientClosedMessage_Serialize(NBN_ClientClosedMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeInt(stream, msg->code, SHRT_MIN, SHRT_MAX); - - return 0; -} - -#pragma endregion /* NBN_ClientClosedMessage */ - -#pragma region NBN_ClientAcceptedMessage - -NBN_ClientAcceptedMessage *NBN_ClientAcceptedMessage_Create(void) -{ - return (NBN_ClientAcceptedMessage*)NBN_Allocator(sizeof(NBN_ClientAcceptedMessage)); -} - -void NBN_ClientAcceptedMessage_Destroy(NBN_ClientAcceptedMessage *msg) -{ - NBN_Deallocator(msg); -} - -int NBN_ClientAcceptedMessage_Serialize(NBN_ClientAcceptedMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->length, 0, NBN_SERVER_DATA_MAX_SIZE); - NBN_SerializeBytes(stream, msg->data, msg->length); - - return 0; -} - -#pragma endregion /* NBN_ClientAcceptedMessage */ - -#pragma region NBN_ByteArrayMessage - -NBN_ByteArrayMessage *NBN_ByteArrayMessage_Create(void) -{ - return (NBN_ByteArrayMessage*)MemoryManager_Alloc(NBN_MEM_BYTE_ARRAY_MESSAGE); -} - -void NBN_ByteArrayMessage_Destroy(NBN_ByteArrayMessage *msg) -{ - MemoryManager_Dealloc(msg, NBN_MEM_BYTE_ARRAY_MESSAGE); -} - -int NBN_ByteArrayMessage_Serialize(NBN_ByteArrayMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->length, 0, NBN_BYTE_ARRAY_MAX_SIZE); - NBN_SerializeBytes(stream, msg->bytes, msg->length); - - return 0; -} - -#pragma endregion /* NBN_ByteArrayMessage */ - -#pragma region NBN_DisconnectionMessage - -void *NBN_DisconnectionMessage_Create(void) -{ - return NULL; -} - -void NBN_DisconnectionMessage_Destroy(void *msg) -{ - NBN_Deallocator(msg); -} - -int NBN_DisconnectionMessage_Serialize(void *msg, NBN_Stream *stream) -{ - (void)msg; - (void)stream; - - return 0; -} - -#pragma endregion /* NBN_DisconnectionMessage */ - -#pragma region NBN_ConnectionRequestMessage - -NBN_ConnectionRequestMessage *NBN_ConnectionRequestMessage_Create(void) -{ - return (NBN_ConnectionRequestMessage *)NBN_Allocator(sizeof(NBN_ConnectionRequestMessage)); -} - -void NBN_ConnectionRequestMessage_Destroy(NBN_ConnectionRequestMessage *msg) -{ - NBN_Deallocator(msg); -} - -int NBN_ConnectionRequestMessage_Serialize(NBN_ConnectionRequestMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->length, 0, NBN_CONNECTION_DATA_MAX_SIZE); - NBN_SerializeBytes(stream, msg->data, msg->length); - - return 0; -} - -#pragma endregion /* NBN_ConnectionRequestMessage */ - -#pragma region NBN_RPC_Message - -void *NBN_RPC_Message_Create(void) -{ - return (NBN_RPC_Message *)NBN_Allocator(sizeof(NBN_RPC_Message)); -} - -void NBN_RPC_Message_Destroy(NBN_RPC_Message *msg) -{ - NBN_Deallocator(msg); -} - -int NBN_RPC_Message_Serialize(NBN_RPC_Message *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->id, 0, NBN_RPC_MAX); - NBN_SerializeUInt(stream, msg->param_count, 0, NBN_RPC_MAX_PARAM_COUNT); - - for (unsigned int i = 0; i < msg->param_count; i++) - { - NBN_RPC_Param *p = &msg->params[i]; - - NBN_SerializeUInt(stream, p->type, 0, 8); - - if (p->type == NBN_RPC_PARAM_INT) - { - NBN_SerializeBytes(stream, &p->value.i, sizeof(int)); - } - else if (p->type == NBN_RPC_PARAM_FLOAT) - { - NBN_SerializeBytes(stream, &p->value.f, sizeof(float)); - } - else if (p->type == NBN_RPC_PARAM_BOOL) - { - NBN_SerializeBytes(stream, &p->value.b, sizeof(bool)); - } - else if (p->type == NBN_RPC_PARAM_STRING) - { - int length = 0; - - if (stream->type == NBN_STREAM_WRITE || stream->type == NBN_STREAM_MEASURE) - { - int l = strlen(p->value.s); - - assert(l + 1 <= NBN_RPC_STRING_MAX_LENGTH); // make sure we have a spot for the terminating byte - assert(l > 0); - - length = l + 1; - } - - NBN_SerializeUInt(stream, length, 0, NBN_RPC_STRING_MAX_LENGTH); - NBN_SerializeString(stream, p->value.s, length); - } - } - - return 0; -} - -#pragma endregion /* NBN_RPC_Message */ - -#pragma region NBN_Connection - -static NBN_OutgoingMessage *Endpoint_CreateOutgoingMessage(NBN_Endpoint *, NBN_Channel*, uint8_t, void *); - -static uint32_t Connection_BuildPacketAckBits(NBN_Connection *); -static int Connection_DecodePacketHeader(NBN_Connection *, NBN_Packet *, double); -static int Connection_AckPacket(NBN_Connection *, uint16_t, double time); -static void Connection_InitOutgoingPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry **); -static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *, uint16_t); -static bool Connection_InsertReceivedPacketEntry(NBN_Connection *, uint16_t); -static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *, uint16_t); -static bool Connection_IsPacketReceived(NBN_Connection *, uint16_t); -static int Connection_SendPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry *, double); -static int Connection_ReadNextMessageFromStream(NBN_Connection *, NBN_ReadStream *, NBN_Message *); -static int Connection_ReadNextMessageFromPacket(NBN_Connection *, NBN_Packet *, NBN_Message *); -static void Connection_RecycleMessage(NBN_Channel *, NBN_Message *); -static void Connection_UpdateAveragePing(NBN_Connection *, double); -static void Connection_UpdateAveragePacketLoss(NBN_Connection *, uint16_t); -static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *, float); -static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *, double); -static NBN_RPC_Message *Connection_BuildRPC(NBN_Connection *, NBN_RPC *, va_list); -static void Connection_HandleReceivedRPC(NBN_ConnectionHandle, NBN_Endpoint *, NBN_RPC_Message *); - -NBN_Connection *NBN_Connection_Create(uint32_t id, uint32_t protocol_id, NBN_Endpoint *endpoint, NBN_Driver *driver, void *driver_data) -{ - NBN_Connection *connection = (NBN_Connection*)MemoryManager_Alloc(NBN_MEM_CONNECTION); - - connection->id = id; - connection->protocol_id = protocol_id; - connection->endpoint = endpoint; - connection->last_recv_packet_time = endpoint->time; - connection->next_packet_seq_number = 1; - connection->last_received_packet_seq_number = 0; - connection->last_flush_time = endpoint->time; - connection->last_read_packets_time = endpoint->time; - connection->downloaded_bytes = 0; - connection->is_accepted = false; - connection->is_stale = false; - connection->is_closed = false; - connection->vector_pos = -1; - - for (int i = 0; i < NBN_MAX_CHANNELS; i++) - connection->channels[i] = NULL; - - for (int i = 0; i < NBN_MAX_PACKET_ENTRIES; i++) - { - connection->packet_send_seq_buffer[i] = 0xFFFFFFFF; - connection->packet_recv_seq_buffer[i] = 0xFFFFFFFF; - } - - NBN_ConnectionStats stats = { 0 }; - - connection->stats = stats; - connection->driver = driver; - connection->driver_data = driver_data; - - return connection; -} - -void NBN_Connection_Destroy(NBN_Connection *connection) -{ - for (int i = 0; i < NBN_MAX_CHANNELS; i++) - { - NBN_Channel *channel = connection->channels[i]; + for (int i = 0; i < NBN_MAX_CHANNELS; i++) + { + NBN_Channel *channel = connection->channels[i]; if (channel) { @@ -2994,28 +2416,34 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Connection *connection, NBN_Packet if (SEQUENCE_NUMBER_GT(packet->header.seq_number, connection->last_received_packet_seq_number)) connection->last_received_packet_seq_number = packet->header.seq_number; + uint8_t *packet_buffer = packet->buffer; + for (int i = 0; i < packet->header.messages_count; i++) { - NBN_Message message; + NBN_Message message = {0}; + int length; - message.outgoing_msg = NULL; + // TODO: make sure we can't read past the packet buffer - if (Connection_ReadNextMessageFromPacket(connection, packet, &message) < 0) + if ((length = Connection_ReadNextMessageFromBuffer(connection, packet_buffer, &message)) < 0) { NBN_LogError("Failed to read message from packet"); return NBN_ERROR; } + packet_buffer += (NBN_MESSAGE_HEADER_SIZE + length); + NBN_Channel *channel = connection->channels[message.header.channel_id]; + assert(channel); + if (channel->AddReceivedMessage(channel, &message)) { NBN_LogTrace("Received message %d on channel %d : added to recv queue", message.header.id, channel->id); #ifdef NBN_DEBUG - if (connection->OnMessageAddedToRecvQueue) - connection->OnMessageAddedToRecvQueue(connection, &message); + if (connection->OnMessageAddedToRecvQueue) connection->OnMessageAddedToRecvQueue(connection, &message); #endif } else @@ -3029,24 +2457,6 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Connection *connection, NBN_Packet return 0; } -int NBN_Connection_EnqueueOutgoingMessage(NBN_Connection *connection, NBN_Channel *channel, NBN_Message *message) -{ - assert(!connection->is_closed || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); - assert(!connection->is_stale); - - NBN_LogTrace("Enqueue message of type %d on channel %d", message->header.type, channel->id); - - if (!channel->AddOutgoingMessage(channel, message)) - { - NBN_LogError("Failed to enqueue outgoing message of type %d on channel %d", - message->header.type, message->header.channel_id); - - return NBN_ERROR; - } - - return 0; -} - int NBN_Connection_FlushSendQueue(NBN_Connection *connection, double time) { NBN_LogTrace("Flushing the send queue"); @@ -3077,11 +2487,7 @@ int NBN_Connection_FlushSendQueue(NBN_Connection *connection, double time) ) { bool message_sent = false; - NBN_MessageSerializer msg_serializer = connection->endpoint->message_serializers[message->header.type]; - - assert(msg_serializer); - - int ret = NBN_Packet_WriteMessage(&packet, message, msg_serializer); + int ret = NBN_Packet_WriteMessage(&packet, message); if (ret == NBN_PACKET_WRITE_OK) { @@ -3101,7 +2507,7 @@ int NBN_Connection_FlushSendQueue(NBN_Connection *connection, double time) Connection_InitOutgoingPacket(connection, &packet, &packet_entry); - int ret = NBN_Packet_WriteMessage(&packet, message, msg_serializer); + int ret = NBN_Packet_WriteMessage(&packet, message); if (ret != NBN_PACKET_WRITE_OK) { @@ -3383,34 +2789,18 @@ static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, } } -static int Connection_ReadNextMessageFromStream( - NBN_Connection *connection, NBN_ReadStream *r_stream, NBN_Message *message) +static int Connection_ReadNextMessageFromBuffer(NBN_Connection *connection, uint8_t *buffer, NBN_Message *message) { - if (NBN_Message_SerializeHeader(&message->header, (NBN_Stream *)r_stream) < 0) - { - NBN_LogError("Failed to read message header"); + // TODO: endianess - return NBN_ERROR; - } - - uint8_t msg_type = message->header.type; - NBN_MessageBuilder msg_builder = connection->endpoint->message_builders[msg_type]; - - if (msg_builder == NULL) - { - NBN_LogError("No message builder is registered for messages of type %d", msg_type); - - return NBN_ERROR; - } - - NBN_MessageSerializer msg_serializer = connection->endpoint->message_serializers[msg_type]; - - if (msg_serializer == NULL) - { - NBN_LogError("No message serializer attached to message of type %d", msg_type); - - return NBN_ERROR; - } + message->header.id = *((uint16_t *)buffer); + buffer += 2; + message->header.length = *((uint16_t *)buffer); + buffer += 2; + message->header.type = *buffer; + buffer++; + message->header.channel_id = *buffer; + buffer++; NBN_Channel *channel = connection->channels[message->header.channel_id]; @@ -3421,31 +2811,26 @@ static int Connection_ReadNextMessageFromStream( return NBN_ERROR; } - message->data = msg_builder(); - - if (msg_serializer(message->data, (NBN_Stream *)r_stream) < 0) - { - NBN_LogError("Failed to read message body"); + // TODO: user defined message allocation strategy + message->data = NBN_Allocator(message->header.length); - return NBN_ERROR; - } + memcpy(message->data, buffer, message->header.length); - return 0; -} - -static int Connection_ReadNextMessageFromPacket(NBN_Connection *connection, NBN_Packet *packet, NBN_Message *message) -{ - return Connection_ReadNextMessageFromStream(connection, &packet->r_stream, message); + return message->header.length; } static void Connection_RecycleMessage(NBN_Channel *channel, NBN_Message *message) { // for incoming messages : message->outgoing_msg == NULL - assert(message->outgoing_msg == NULL || message->outgoing_msg->ref_count > 0); + assert(message->ref_count > 0); - if (message->outgoing_msg == NULL || --message->outgoing_msg->ref_count == 0) + message->ref_count--; + + if (message->ref_count == 0) { - if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) + // TODO + + /*if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) { NBN_MessageChunk *chunk = (NBN_MessageChunk*)message->data; @@ -3455,19 +2840,13 @@ static void Connection_RecycleMessage(NBN_Channel *channel, NBN_Message *message if (--chunk->outgoing_msg->ref_count == 0) { - NBN_MessageDestructor msg_destructor = - channel->connection->endpoint->message_destructors[chunk->outgoing_msg->type]; - - if (msg_destructor) - msg_destructor(chunk->outgoing_msg->data); + // TODO } } - } + }*/ - NBN_MessageDestructor msg_destructor = channel->connection->endpoint->message_destructors[message->header.type]; - - if (msg_destructor) - msg_destructor(message->data); + // TODO: user defined message allocation strategy + NBN_Deallocator(message->data); } } @@ -3528,79 +2907,6 @@ static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *connection connection->downloaded_bytes = 0; } -static NBN_RPC_Message *Connection_BuildRPC(NBN_Connection *connection, NBN_RPC *rpc, va_list args) -{ - NBN_RPC_Message *msg = (NBN_RPC_Message *) NBN_RPC_Message_Create(); - - assert(msg != NULL); - - msg->id = rpc->id; - msg->param_count = rpc->signature.param_count; - - for (unsigned int i = 0; i < rpc->signature.param_count; i++) - { - NBN_RPC_ParamType param_type = rpc->signature.params[i]; - - msg->params[i].type = param_type; - - if (param_type == NBN_RPC_PARAM_INT) - { - msg->params[i].value.i = va_arg(args, int); - } - else if (param_type == NBN_RPC_PARAM_FLOAT) - { - msg->params[i].value.f = va_arg(args, double); - } - else if (param_type == NBN_RPC_PARAM_BOOL) - { - msg->params[i].value.b = va_arg(args, int); - } - else if (param_type == NBN_RPC_PARAM_STRING) - { - char *str = va_arg(args, char *); - - strncpy(msg->params[i].value.s, str, NBN_RPC_STRING_MAX_LENGTH); - } - else - { - NBN_LogError("Calling RPC %d with invalid parameters on connection %d", rpc->id, connection->id); - NBN_Abort(); - - return NULL; - } - } - - return msg; -} - -static void Connection_HandleReceivedRPC(NBN_ConnectionHandle connection, NBN_Endpoint *endpoint, NBN_RPC_Message *msg) -{ - if (msg->id < 0 || msg->id > NBN_RPC_MAX - 1) - { - NBN_LogError("Received an invalid RPC"); - - return; - } - - NBN_RPC *rpc = &endpoint->rpcs[msg->id]; - - if (rpc->id != msg->id) - { - NBN_LogError("Received an invalid RPC"); - - return; - } - - if (!rpc->func) - { - NBN_LogError("Received RPC does not have an attached function"); - - return; - } - - rpc->func(msg->param_count, msg->params, connection); -} - #pragma endregion /* NBN_Connection */ #pragma region NBN_Channel @@ -3682,10 +2988,11 @@ bool NBN_Channel_AddChunk(NBN_Channel *channel, NBN_Message *chunk_msg) return false; } -int NBN_Channel_ReconstructMessageFromChunks( - NBN_Channel *channel, NBN_Connection *connection, NBN_Message *message) +int NBN_Channel_ReconstructMessageFromChunks(NBN_Channel *channel, NBN_Connection *connection, NBN_Message *message) { - unsigned int message_size = channel->chunk_count * NBN_MESSAGE_CHUNK_SIZE; + // TODO + + /*unsigned int message_size = channel->chunk_count * NBN_MESSAGE_CHUNK_SIZE; if (message_size > channel->read_chunk_buffer_size) NBN_Channel_ResizeReadChunkBuffer(channel, message_size); @@ -3713,7 +3020,7 @@ int NBN_Channel_ReconstructMessageFromChunks( if (Connection_ReadNextMessageFromStream(connection, &r_stream, message) < 0) return NBN_ERROR; - NBN_LogTrace("Reconstructed message %d of type %d", message->header.id, message->header.type); + NBN_LogTrace("Reconstructed message %d of type %d", message->header.id, message->header.type);*/ return 0; } @@ -4095,13 +3402,11 @@ static void Endpoint_RegisterMessageBuilder(NBN_Endpoint *, NBN_MessageBuilder, static void Endpoint_RegisterMessageDestructor(NBN_Endpoint *, NBN_MessageDestructor, uint8_t); static void Endpoint_RegisterMessageSerializer(NBN_Endpoint *, NBN_MessageSerializer, uint8_t); static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, uint32_t, uint32_t, int, void *); -static int Endpoint_RegisterRPC(NBN_Endpoint *, unsigned int id, NBN_RPC_Signature, NBN_RPC_Func); static uint32_t Endpoint_BuildProtocolId(const char *); static void Endpoint_RegisterChannel(NBN_Endpoint *, uint8_t, NBN_ChannelBuilder, NBN_ChannelDestructor); static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *, NBN_Packet *, NBN_Connection *); -static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN_OutgoingMessage *, uint8_t); -static int Endpoint_SplitMessageIntoChunks( - NBN_Message *, NBN_OutgoingMessage *, NBN_Channel *, NBN_MessageSerializer, unsigned int, NBN_MessageChunk **); +static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN_Message *); +// static int Endpoint_SplitMessageIntoChunks(NBN_Message *, NBN_OutgoingMessage *, NBN_Channel *, NBN_MessageSerializer, unsigned int, NBN_MessageChunk **); static void Endpoint_UpdateTime(NBN_Endpoint *); static void Endpoint_Init(NBN_Endpoint *endpoint, bool is_server) @@ -4116,19 +3421,6 @@ static void Endpoint_Init(NBN_Endpoint *endpoint, bool is_server) endpoint->channel_destructors[i] = NULL; } - for (int i = 0; i < NBN_MAX_MESSAGE_TYPES; i++) - { - endpoint->message_builders[i] = NULL; - endpoint->message_destructors[i] = NULL; - endpoint->message_serializers[i] = NULL; - } - - NBN_RPC rpc = {.id = UINT_MAX}; - for (int i = 0; i < NBN_RPC_MAX; i++) - { - endpoint->rpcs[i] = rpc; - } - NBN_EventQueue_Init(&endpoint->event_queue); /* Register library reserved reliable channel */ @@ -4142,62 +3434,6 @@ static void Endpoint_Init(NBN_Endpoint *endpoint, bool is_server) Endpoint_RegisterChannel(endpoint, i, (NBN_ChannelBuilder)NBN_ReliableOrderedChannel_Create, (NBN_ChannelDestructor)NBN_Channel_Destroy); } - /* Register NBN_MessageChunk library message */ - Endpoint_RegisterMessageBuilder( - endpoint, (NBN_MessageBuilder)NBN_MessageChunk_Create, NBN_MESSAGE_CHUNK_TYPE); - Endpoint_RegisterMessageSerializer( - endpoint, (NBN_MessageSerializer)NBN_MessageChunk_Serialize, NBN_MESSAGE_CHUNK_TYPE); - Endpoint_RegisterMessageDestructor( - endpoint, (NBN_MessageDestructor)NBN_MessageChunk_Destroy, NBN_MESSAGE_CHUNK_TYPE); - - /* Register NBN_ClientClosedMessage library message */ - Endpoint_RegisterMessageBuilder( - endpoint, (NBN_MessageBuilder)NBN_ClientClosedMessage_Create, NBN_CLIENT_CLOSED_MESSAGE_TYPE); - Endpoint_RegisterMessageSerializer( - endpoint, (NBN_MessageSerializer)NBN_ClientClosedMessage_Serialize, NBN_CLIENT_CLOSED_MESSAGE_TYPE); - Endpoint_RegisterMessageDestructor( - endpoint, (NBN_MessageDestructor)NBN_ClientClosedMessage_Destroy, NBN_CLIENT_CLOSED_MESSAGE_TYPE); - - /* Register NBN_ClientAcceptedMessage library message */ - Endpoint_RegisterMessageBuilder( - endpoint, (NBN_MessageBuilder)NBN_ClientAcceptedMessage_Create, NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); - Endpoint_RegisterMessageSerializer( - endpoint, (NBN_MessageSerializer)NBN_ClientAcceptedMessage_Serialize, NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); - Endpoint_RegisterMessageDestructor( - endpoint, (NBN_MessageDestructor)NBN_ClientAcceptedMessage_Destroy, NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); - - /* Register NBN_ByteArrayMessage library message */ - Endpoint_RegisterMessageBuilder( - endpoint, (NBN_MessageBuilder)NBN_ByteArrayMessage_Create, NBN_BYTE_ARRAY_MESSAGE_TYPE); - Endpoint_RegisterMessageSerializer( - endpoint, (NBN_MessageSerializer)NBN_ByteArrayMessage_Serialize, NBN_BYTE_ARRAY_MESSAGE_TYPE); - Endpoint_RegisterMessageDestructor( - endpoint, (NBN_MessageDestructor)NBN_ByteArrayMessage_Destroy, NBN_BYTE_ARRAY_MESSAGE_TYPE); - - /* Register NBN_DisconnectionMessage library message */ - Endpoint_RegisterMessageBuilder( - endpoint, (NBN_MessageBuilder)NBN_DisconnectionMessage_Create, NBN_DISCONNECTION_MESSAGE_TYPE); - Endpoint_RegisterMessageSerializer( - endpoint, (NBN_MessageSerializer)NBN_DisconnectionMessage_Serialize, NBN_DISCONNECTION_MESSAGE_TYPE); - Endpoint_RegisterMessageDestructor( - endpoint, (NBN_MessageDestructor)NBN_DisconnectionMessage_Destroy, NBN_DISCONNECTION_MESSAGE_TYPE); - - /* Register NBN_ConnectionRequestMessage library message */ - Endpoint_RegisterMessageBuilder( - endpoint, (NBN_MessageBuilder)NBN_ConnectionRequestMessage_Create, NBN_CONNECTION_REQUEST_MESSAGE_TYPE); - Endpoint_RegisterMessageSerializer( - endpoint, (NBN_MessageSerializer)NBN_ConnectionRequestMessage_Serialize, NBN_CONNECTION_REQUEST_MESSAGE_TYPE); - Endpoint_RegisterMessageDestructor( - endpoint, (NBN_MessageDestructor)NBN_ConnectionRequestMessage_Destroy, NBN_CONNECTION_REQUEST_MESSAGE_TYPE); - - /* Register NBN_RPC_Message library message */ - Endpoint_RegisterMessageBuilder( - endpoint, (NBN_MessageBuilder)NBN_RPC_Message_Create, NBN_RPC_MESSAGE_TYPE); - Endpoint_RegisterMessageSerializer( - endpoint, (NBN_MessageSerializer)NBN_RPC_Message_Serialize, NBN_RPC_MESSAGE_TYPE); - Endpoint_RegisterMessageDestructor( - endpoint, (NBN_MessageDestructor)NBN_RPC_Message_Destroy, NBN_RPC_MESSAGE_TYPE); - #ifdef NBN_DEBUG endpoint->OnMessageAddedToRecvQueue = NULL; #endif @@ -4221,22 +3457,6 @@ static void Endpoint_Deinit(NBN_Endpoint *endpoint) MemoryManager_Deinit(); } -static void Endpoint_RegisterMessageBuilder(NBN_Endpoint *endpoint, NBN_MessageBuilder msg_builder, uint8_t msg_type) -{ - endpoint->message_builders[msg_type] = msg_builder; -} - -static void Endpoint_RegisterMessageDestructor(NBN_Endpoint *endpoint, NBN_MessageDestructor msg_destructor, uint8_t msg_type) -{ - endpoint->message_destructors[msg_type] = msg_destructor; -} - -static void Endpoint_RegisterMessageSerializer( - NBN_Endpoint *endpoint, NBN_MessageSerializer msg_serializer, uint8_t msg_type) -{ - endpoint->message_serializers[msg_type] = msg_serializer; -} - static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_t id, uint32_t protocol_id, int driver_id, void *driver_data) { NBN_Driver *driver = &nbn_drivers[driver_id]; @@ -4270,31 +3490,6 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_ return connection; } -static int Endpoint_RegisterRPC(NBN_Endpoint *endpoint, unsigned int id, NBN_RPC_Signature signature, NBN_RPC_Func func) -{ - if (id < 0 || id >= NBN_RPC_MAX) - { - NBN_LogError("Failed to register RPC, invalid ID"); - - return NBN_ERROR; - } - - if (signature.param_count > NBN_RPC_MAX_PARAM_COUNT) - { - NBN_LogError("Failed to register RPC %d, too many parameters", id); - - return NBN_ERROR; - } - - NBN_RPC rpc = { .id = id, .signature = signature, .func = func }; - - endpoint->rpcs[id] = rpc; - - NBN_LogDebug("Registered RPC (id: %d, parameter count: %d)", id, signature.param_count); - - return 0; -} - static uint32_t Endpoint_BuildProtocolId(const char *protocol_name) { uint32_t protocol_id = 2166136261; @@ -4336,66 +3531,42 @@ static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *pa return 0; } -static NBN_OutgoingMessage *Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, NBN_Channel *channel, uint8_t msg_type, void *data) +static NBN_Message *Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, NBN_Channel *channel, uint8_t type, uint8_t *data, uint16_t length) { assert(channel); - if (endpoint->message_serializers[msg_type] == NULL) - { - NBN_LogError("No message serializer is registered for messages of type %d", msg_type); - NBN_Abort(); - } + NBN_Message *message = &channel->outgoing_message_pool[channel->next_outgoing_message_pool_slot]; - NBN_OutgoingMessage *outgoing_message = &channel->outgoing_message_pool[channel->next_outgoing_message_pool_slot]; - if (outgoing_message->ref_count != 0) + if (message->ref_count != 0) { NBN_LogError("Outgoing message pool has run out of space"); return NULL; } - outgoing_message->type = msg_type; - outgoing_message->data = data; - outgoing_message->ref_count = 0; + message->header = (NBN_MessageHeader){-1, length, type, channel->id}; + message->sender = NULL; + message->data = data; + message->ref_count = 0; channel->next_outgoing_message_pool_slot = (channel->next_outgoing_message_pool_slot + 1) % NBN_CHANNEL_OUTGOING_MESSAGE_POOL_SIZE; - return outgoing_message; + return message; } -static int Endpoint_EnqueueOutgoingMessage( - NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_OutgoingMessage *outgoing_msg, uint8_t channel_id) +static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Message *message) { - NBN_Channel *channel = connection->channels[channel_id]; - - if (channel == NULL) - { - NBN_LogError("Channel %d does not exist", channel_id); - - return NBN_ERROR; - } - - NBN_MessageSerializer msg_serializer = endpoint->message_serializers[outgoing_msg->type]; - - assert(msg_serializer); - - NBN_Message message = { - {0, outgoing_msg->type, channel_id}, - NULL, - outgoing_msg, - outgoing_msg->data}; - - NBN_MeasureStream m_stream; + assert(!connection->is_closed || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); + assert(!connection->is_stale); - NBN_MeasureStream_Init(&m_stream); + NBN_Channel *channel = connection->channels[message->header.channel_id]; - unsigned int message_size = (NBN_Message_Measure(&message, &m_stream, msg_serializer) - 1) / 8 + 1; + assert(channel); - if (message_size > NBN_PACKET_MAX_DATA_SIZE) + if (message->header.length > NBN_PACKET_MAX_DATA_SIZE) { - NBN_MessageChunk *chunks[NBN_CHANNEL_CHUNKS_BUFFER_SIZE]; - int chunk_count = Endpoint_SplitMessageIntoChunks( - &message, outgoing_msg, channel, msg_serializer, message_size, chunks); + /*NBN_MessageChunk *chunks[NBN_CHANNEL_CHUNKS_BUFFER_SIZE]; + int chunk_count = Endpoint_SplitMessageIntoChunks(&message, outgoing_msg, channel, msg_serializer, message_size, chunks); assert(chunk_count <= NBN_CHANNEL_CHUNKS_BUFFER_SIZE); @@ -4413,8 +3584,7 @@ static int Endpoint_EnqueueOutgoingMessage( for (int i = 0; i < chunk_count; i++) { - NBN_OutgoingMessage *chunk_outgoing_msg = Endpoint_CreateOutgoingMessage( - endpoint, channel, NBN_MESSAGE_CHUNK_TYPE, chunks[i]); + NBN_OutgoingMessage *chunk_outgoing_msg = Endpoint_CreateOutgoingMessage(endpoint, channel, NBN_MESSAGE_CHUNK_TYPE, chunks[i]); if (chunk_outgoing_msg == NULL) return NBN_ERROR; @@ -4425,20 +3595,32 @@ static int Endpoint_EnqueueOutgoingMessage( return NBN_ERROR; } - } + }*/ - return 0; + // TODO: fix chunks + assert(false); } else - { - assert(outgoing_msg->ref_count == 0); - outgoing_msg->ref_count = 1; + { + assert(message->ref_count == 0); + + message->ref_count = 1; + + NBN_LogTrace("Enqueue message of type %d on channel %d", message->header.type, channel->id); - return NBN_Connection_EnqueueOutgoingMessage(connection, channel, &message); + if (!channel->AddOutgoingMessage(channel, message)) + { + NBN_LogError("Failed to enqueue outgoing message of type %d on channel %d", message->header.type, message->header.channel_id); + + return NBN_ERROR; + } } + + return 0; } -static int Endpoint_SplitMessageIntoChunks( +// TODO +/*static int Endpoint_SplitMessageIntoChunks( NBN_Message *message, NBN_OutgoingMessage *outgoing_msg, NBN_Channel *channel, @@ -4446,6 +3628,7 @@ static int Endpoint_SplitMessageIntoChunks( unsigned int message_size, NBN_MessageChunk **chunks) { + unsigned int chunk_count = ((message_size - 1) / NBN_MESSAGE_CHUNK_SIZE) + 1; NBN_LogTrace("Split message into %d chunks (message size: %d)", chunk_count, message_size); @@ -4492,7 +3675,8 @@ static int Endpoint_SplitMessageIntoChunks( } return chunk_count; -} + return 0; +}*/ static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) { @@ -4558,6 +3742,15 @@ int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data) #pragma endregion /* Network driver */ +#pragma region Library reserved messages + +static void SerializeClientClosedConnectionMessage(NBN_Stream *stream, int code) +{ + NBN_SerializeUInt(stream, code, SHRT_MIN, SHRT_MAX); +} + +#pragma endregion /* Library reserved messages */ + #pragma region NBN_GameClient NBN_GameClient nbn_game_client; @@ -4604,16 +3797,7 @@ int NBN_GameClient_StartEx(const char *protocol_name, const char *host, uint16_t } } - NBN_ConnectionRequestMessage *msg = NBN_ConnectionRequestMessage_Create(); - - msg->length = length; - - if (data) - { - memcpy(msg->data, data, NBN_CONNECTION_DATA_MAX_SIZE); - } - - if (NBN_GameClient_SendMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, msg) < 0) + if (NBN_GameClient_SendMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, data, length) < 0) return NBN_ERROR; NBN_LogInfo("Started"); @@ -4632,7 +3816,7 @@ void NBN_GameClient_Stop(void) { NBN_LogInfo("Disconnecting..."); - if (NBN_GameClient_SendMessage(NBN_DISCONNECTION_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, NULL) < 0) + if (NBN_GameClient_SendMessage(NBN_DISCONNECTION_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, NULL, 0) < 0) { NBN_LogError("Failed to send disconnection message"); } @@ -4679,23 +3863,6 @@ unsigned int NBN_GameClient_ReadServerData(uint8_t *data) return nbn_game_client.server_data_len; } -void NBN_GameClient_RegisterMessage( - uint8_t msg_type, - NBN_MessageBuilder msg_builder, - NBN_MessageDestructor msg_destructor, - NBN_MessageSerializer msg_serializer) -{ - if (NBN_IsReservedMessage(msg_type)) - { - NBN_LogError("Message type %d is reserved by the library", msg_type); - NBN_Abort(); - } - - Endpoint_RegisterMessageBuilder(&nbn_game_client.endpoint, msg_builder, msg_type); - Endpoint_RegisterMessageDestructor(&nbn_game_client.endpoint, msg_destructor, msg_type); - Endpoint_RegisterMessageSerializer(&nbn_game_client.endpoint, msg_serializer, msg_type); -} - int NBN_GameClient_Poll(void) { Endpoint_UpdateTime(&nbn_game_client.endpoint); @@ -4773,24 +3940,23 @@ int NBN_GameClient_SendPackets(void) return NBN_Connection_FlushSendQueue(nbn_game_client.server_connection, nbn_game_client.endpoint.time); } -int NBN_GameClient_SendMessage(uint8_t msg_type, uint8_t channel_id, void *msg_data) +int NBN_GameClient_SendMessage(uint8_t type, uint8_t channel_id, uint8_t *data, uint16_t length) { - NBN_OutgoingMessage *outgoing_msg = Endpoint_CreateOutgoingMessage( + NBN_Message *message = Endpoint_CreateOutgoingMessage( &nbn_game_client.endpoint, nbn_game_client.server_connection->channels[channel_id], - msg_type, - msg_data); + type, + data, + length); - - if (outgoing_msg == NULL) + if (message == NULL) { NBN_LogError("Failed to create outgoing message"); return NBN_ERROR; } - if (Endpoint_EnqueueOutgoingMessage( - &nbn_game_client.endpoint, nbn_game_client.server_connection, outgoing_msg, channel_id) < 0) + if (Endpoint_EnqueueOutgoingMessage(&nbn_game_client.endpoint, nbn_game_client.server_connection, message) < 0) { NBN_LogError("Failed to create outgoing message"); @@ -4800,42 +3966,15 @@ int NBN_GameClient_SendMessage(uint8_t msg_type, uint8_t channel_id, void *msg_d return 0; } -int NBN_GameClient_SendByteArray(uint8_t *bytes, unsigned int length, uint8_t channel_id) -{ - if (length > NBN_BYTE_ARRAY_MAX_SIZE) - { - NBN_LogError("Byte array cannot exceed %d bytes", NBN_BYTE_ARRAY_MAX_SIZE); - - return NBN_ERROR; - } - - NBN_ByteArrayMessage *msg = NBN_ByteArrayMessage_Create(); - - memcpy(msg->bytes, bytes, length); - - msg->length = length; - - return NBN_GameClient_SendMessage(NBN_BYTE_ARRAY_MESSAGE_TYPE, channel_id, msg); -} - -int NBN_GameClient_SendUnreliableMessage(uint8_t msg_type, void *msg_data) -{ - return NBN_GameClient_SendMessage(msg_type, NBN_CHANNEL_RESERVED_UNRELIABLE, msg_data); -} - -int NBN_GameClient_SendReliableMessage(uint8_t msg_type, void *msg_data) -{ - return NBN_GameClient_SendMessage(msg_type, NBN_CHANNEL_RESERVED_RELIABLE, msg_data); -} -int NBN_GameClient_SendUnreliableByteArray(uint8_t *bytes, unsigned int length) +int NBN_GameClient_SendUnreliableMessage(uint8_t type, uint8_t *data, uint16_t length) { - return NBN_GameClient_SendByteArray(bytes, length, NBN_CHANNEL_RESERVED_UNRELIABLE); + return NBN_GameClient_SendMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE, data, length); } -int NBN_GameClient_SendReliableByteArray(uint8_t *bytes, unsigned int length) +int NBN_GameClient_SendReliableMessage(uint8_t type, uint8_t *data, uint16_t length) { - return NBN_GameClient_SendByteArray(bytes, length, NBN_CHANNEL_RESERVED_RELIABLE); + return NBN_GameClient_SendMessage(type, NBN_CHANNEL_RESERVED_RELIABLE, data, length); } NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *driver_data, uint32_t protocol_id) @@ -4873,47 +4012,6 @@ bool NBN_GameClient_IsConnected(void) return nbn_game_client.is_connected; } -int NBN_GameClient_RegisterRPC(unsigned int id, NBN_RPC_Signature signature, NBN_RPC_Func func) -{ - return Endpoint_RegisterRPC(&nbn_game_client.endpoint, id, signature, func); -} - -int NBN_GameClient_CallRPC(unsigned int id, ...) -{ - NBN_RPC rpc = nbn_game_client.endpoint.rpcs[id]; - - if (rpc.id < 0 || rpc.id != id) - { - NBN_LogError("Cannot call invalid RPC (ID: %d)", id); - - return NBN_ERROR; - } - - va_list args; - - va_start(args, id); - - NBN_RPC_Message *rpc_msg = Connection_BuildRPC(nbn_game_client.server_connection, &rpc, args); - - assert(rpc_msg); - - if (NBN_GameClient_SendMessage(NBN_RPC_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, rpc_msg) < 0) - { - NBN_LogError("Failed to send RPC message"); - - goto rpc_error; - } - - va_end(args); - - return 0; - -rpc_error: - va_end(args); - - return NBN_ERROR; -} - #ifdef NBN_DEBUG void NBN_GameClient_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, void *cb) @@ -5031,7 +4129,7 @@ static void ClientDriver_OnPacketReceived(NBN_Packet *packet) NBN_GameServer nbn_game_server; -static int GameServer_SendMessageTo(NBN_Connection *client, uint8_t msg_type, uint8_t channel_id, void *msg_data); +static int GameServer_SendMessageTo(NBN_Connection *client, uint8_t msg_type, uint8_t channel_id, uint8_t *data, uint16_t length); static int GameServer_AddClient(NBN_Connection *); static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection); static void GameServer_AddClientToClosedList(NBN_Connection *client); @@ -5129,23 +4227,6 @@ void NBN_GameServer_Stop(void) NBN_LogInfo("Stopped"); } -void NBN_GameServer_RegisterMessage( - uint8_t msg_type, - NBN_MessageBuilder msg_builder, - NBN_MessageDestructor msg_destructor, - NBN_MessageSerializer msg_serializer) -{ - if (NBN_IsReservedMessage(msg_type)) - { - NBN_LogError("Message type %d is reserved by the library", msg_type); - NBN_Abort(); - } - - Endpoint_RegisterMessageBuilder(&nbn_game_server.endpoint, msg_builder, msg_type); - Endpoint_RegisterMessageDestructor(&nbn_game_server.endpoint, msg_destructor, msg_type); - Endpoint_RegisterMessageSerializer(&nbn_game_server.endpoint, msg_serializer, msg_type); -} - int NBN_GameServer_Poll(void) { Endpoint_UpdateTime(&nbn_game_server.endpoint); @@ -5274,32 +4355,6 @@ int NBN_GameServer_CloseClient(NBN_ConnectionHandle connection_handle) return GameServer_CloseClientWithCode(client, -1, false); } -int NBN_GameServer_SendByteArrayTo(NBN_ConnectionHandle connection_handle, uint8_t *bytes, unsigned int length, uint8_t channel_id) -{ - NBN_Connection *client = NBN_ConnectionTable_Get(nbn_game_server.clients_table, connection_handle); - - if (client == NULL) - { - NBN_LogError("Client %d does not exist", connection_handle); - return NBN_ERROR; - } - - if (length > NBN_BYTE_ARRAY_MAX_SIZE) - { - NBN_LogError("Byte array cannot exceed %d bytes", NBN_BYTE_ARRAY_MAX_SIZE); - - return NBN_ERROR; - } - - NBN_ByteArrayMessage *msg = NBN_ByteArrayMessage_Create(); - - memcpy(msg->bytes, bytes, length); - - msg->length = length; - - return NBN_GameServer_SendMessageTo(connection_handle, NBN_BYTE_ARRAY_MESSAGE_TYPE, channel_id, msg); -} - int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, uint8_t channel_id, void *msg_data) { NBN_Connection *client = NBN_ConnectionTable_Get(nbn_game_server.clients_table, connection_handle); @@ -5323,16 +4378,6 @@ int NBN_GameServer_SendReliableMessageTo(NBN_ConnectionHandle connection_handle, return NBN_GameServer_SendMessageTo(connection_handle, msg_type, NBN_CHANNEL_RESERVED_RELIABLE, msg_data); } -int NBN_GameServer_SendUnreliableByteArrayTo(NBN_ConnectionHandle connection_handle, uint8_t *bytes, unsigned int length) -{ - return NBN_GameServer_SendByteArrayTo(connection_handle, bytes, length, NBN_CHANNEL_RESERVED_UNRELIABLE); -} - -int NBN_GameServer_SendReliableByteArrayTo(NBN_ConnectionHandle connection_handle, uint8_t *bytes, unsigned int length) -{ - return NBN_GameServer_SendByteArrayTo(connection_handle, bytes, length, NBN_CHANNEL_RESERVED_RELIABLE); -} - int NBN_GameServer_AcceptIncomingConnection(void) { return NBN_GameServer_AcceptIncomingConnectionWithData(NULL, 0); @@ -5418,56 +4463,6 @@ NBN_GameServerStats NBN_GameServer_GetStats(void) return nbn_game_server.stats; } -int NBN_GameServer_RegisterRPC(unsigned int id, NBN_RPC_Signature signature, NBN_RPC_Func func) -{ - return Endpoint_RegisterRPC(&nbn_game_server.endpoint, id, signature, func); -} - -int NBN_GameServer_CallRPC(unsigned int id, NBN_ConnectionHandle connection_handle, ...) -{ - NBN_Connection *client = NBN_ConnectionTable_Get(nbn_game_server.clients_table, connection_handle); - - if (client == NULL) - { - NBN_LogError("Client %d does not exist", connection_handle); - - return NBN_ERROR; - } - - NBN_RPC rpc = nbn_game_server.endpoint.rpcs[id]; - - if (rpc.id < 0 || rpc.id != id) - { - NBN_LogError("Cannot call invalid RPC (ID: %d)", id); - - return NBN_ERROR; - } - - va_list args; - - va_start(args, connection_handle); - - NBN_RPC_Message *rpc_msg = Connection_BuildRPC(client, &rpc, args); - - assert(rpc_msg); - - if (GameServer_SendMessageTo(client, NBN_RPC_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, rpc_msg) < 0) - { - NBN_LogError("Failed to send RPC message"); - - goto rpc_error; - } - - va_end(args); - - return 0; - -rpc_error: - va_end(args); - - return NBN_ERROR; -} - #ifdef NBN_DEBUG void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, void *cb) @@ -5482,11 +4477,12 @@ void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, #endif /* NBN_DEBUG */ -static int GameServer_SendMessageTo(NBN_Connection *client, uint8_t msg_type, uint8_t channel_id, void *msg_data) +static int GameServer_SendMessageTo(NBN_Connection *client, uint8_t type, uint8_t channel_id, uint8_t *data, uint16_t length) { - NBN_OutgoingMessage *outgoing_msg = Endpoint_CreateOutgoingMessage(&nbn_game_server.endpoint, client->channels[channel_id], msg_type, msg_data); + NBN_Channel *channel = client->channels[channel_id]; + NBN_OutgoingMessage *message = Endpoint_CreateOutgoingMessage(&nbn_game_server.endpoint, channel, type, data, length); - if (outgoing_msg == NULL) + if (message == NULL) { NBN_LogError("Failed to create outgoing message"); @@ -5496,7 +4492,7 @@ static int GameServer_SendMessageTo(NBN_Connection *client, uint8_t msg_type, ui /* The only message type we can send to an unaccepted client is a NBN_ClientAcceptedMessage message */ assert(client->is_accepted || outgoing_msg->type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); - if (Endpoint_EnqueueOutgoingMessage(&nbn_game_server.endpoint, client, outgoing_msg, channel_id) < 0) + if (Endpoint_EnqueueOutgoingMessage(&nbn_game_server.endpoint, client, message) < 0) { NBN_LogError("Failed to create outgoing message for client %d", client->id); @@ -5562,11 +4558,14 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool { NBN_LogDebug("Send close message for client %d (code: %d)", client->id, code); - NBN_ClientClosedMessage *msg = NBN_ClientClosedMessage_Create(); + NBN_MeasureStream m_stream; - msg->code = code; + NBN_MeasureStream_Init(&m_stream); + SerializeClientClosedConnectionMessage(&m_stream, 0); + unsigned int length = NBN_MeasureStream_CountBytes(&m_stream); + uint8_t *msg = NBN_Allocator(length); // TODO: pooling - GameServer_SendMessageTo(client, NBN_CLIENT_CLOSED_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, msg); + GameServer_SendMessageTo(client, NBN_CLIENT_CLOSED_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, msg, length); } return 0; @@ -5774,13 +4773,7 @@ static int GameServer_HandleMessageReceivedEvent(void) int ret = NBN_CLIENT_MESSAGE_RECEIVED; - if (message_info.type == NBN_RPC_MESSAGE_TYPE) - { - ret = NBN_NO_EVENT; - - Connection_HandleReceivedRPC(message_info.sender, &nbn_game_server.endpoint, (NBN_RPC_Message *)message_info.data); - } - else if (message_info.type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE) + if (message_info.type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE) { ret = NBN_NO_EVENT; From 76a675a9a8fefa380f264b9c4f9af1c4b228ca88 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 11 Sep 2024 18:00:44 +0200 Subject: [PATCH 02/85] wip --- examples/echo/CMakeLists.txt | 2 + nbnet.h | 1369 ++++++++++++---------------------- net_drivers/udp.h | 36 +- 3 files changed, 487 insertions(+), 920 deletions(-) diff --git a/examples/echo/CMakeLists.txt b/examples/echo/CMakeLists.txt index dc242fa..78aaf46 100644 --- a/examples/echo/CMakeLists.txt +++ b/examples/echo/CMakeLists.txt @@ -6,6 +6,8 @@ option(CPP_COMPILE OFF) option(ENABLE_TLS OFF) option(WEBRTC_NATIVE OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # allow to compile as cpp if (CPP_COMPILE) file(GLOB_RECURSE CFILES "${CMAKE_SOURCE_DIR}/*.c") diff --git a/nbnet.h b/nbnet.h index a53900c..bec463f 100644 --- a/nbnet.h +++ b/nbnet.h @@ -67,7 +67,37 @@ #ifndef NBN_Abort #define NBN_Abort abort -#endif /* NBN_Abort */ +#endif + +#ifndef NBN_Assert +#define NBN_Assert(cond) \ +{ \ + if (!(cond)) { \ + NBN_LogError(#cond); \ + NBN_Abort(); \ + } \ +} +#endif + +#ifndef NBN_LogError +#define NBN_LogError(...) do {} while (0) +#endif + +#ifndef NBN_LogInfo +#define NBN_LogInfo(...) do {} while (0) +#endif + +#ifndef NBN_LogDebug +#define NBN_LogDebug(...) do {} while (0) +#endif + +#ifndef NBN_LogWarning +#define NBN_LogWarning(...) do {} while (0) +#endif + +#ifndef NBN_LogTrace +#define NBN_LogTrace(...) do {} while (0) +#endif #define NBN_ERROR -1 @@ -141,166 +171,39 @@ extern NBN_MemoryManager nbn_mem_manager; #pragma region Serialization -typedef uint32_t Word; - -#define WORD_BYTES (sizeof(Word)) -#define WORD_BITS (WORD_BYTES * 8) -#define BITS_REQUIRED(min, max) (min == max) ? 0 : GetRequiredNumberOfBitsFor(max - min) - #define B_MASK(n) (1u << (n)) #define B_SET(mask, n) (mask |= B_MASK(n)) #define B_UNSET(mask, n) (mask &= ~B_MASK(n)) #define B_IS_SET(mask, n) ((B_MASK(n) & mask) == B_MASK(n)) #define B_IS_UNSET(mask, n) ((B_MASK(n) & mask) == 0) -#define ASSERT_VALUE_IN_RANGE(v, min, max) assert(v >= min && v <= max) -#define ASSERTED_SERIALIZE(stream, v, min, max, func) \ -{ \ - if (stream->type == NBN_STREAM_WRITE) \ - ASSERT_VALUE_IN_RANGE(v, min, max); \ - if ((func) < 0) \ - NBN_Abort(); \ - if (stream->type == NBN_STREAM_READ) \ - ASSERT_VALUE_IN_RANGE(v, min, max); \ -} - -#define NBN_SerializeUInt(stream, v, min, max) \ - ASSERTED_SERIALIZE((stream), v, min, max, (stream)->serialize_uint_func((stream), (unsigned int *)&(v), min, max)) -#define NBN_SerializeUInt64(stream, v) (stream)->serialize_uint64_func((stream), (uint64_t *)&(v)) -#define NBN_SerializeInt(stream, v, min, max) \ - ASSERTED_SERIALIZE((stream), v, min, max, (stream)->serialize_int_func((stream), &(v), min, max)) -#define NBN_SerializeFloat(stream, v, min, max, precision) \ - ASSERTED_SERIALIZE((stream), v, min, max, (stream)->serialize_float_func((stream), &(v), min, max, precision)) -#define NBN_SerializeBool(stream, v) ASSERTED_SERIALIZE((stream), v, 0, 1, (stream)->serialize_bool_func((stream), &(v))) -#define NBN_SerializeString(stream, v, length) NBN_SerializeBytes((stream), v, length) -#define NBN_SerializeBytes(stream, v, length) (stream)->serialize_bytes_func((stream), (uint8_t *)v, length) -#define NBN_SerializePadding(stream) (stream)->serialize_padding_func(stream) - -#pragma region NBN_BitReader - -typedef struct NBN_BitReader -{ - unsigned int size; - uint8_t *buffer; - uint64_t scratch; - unsigned int scratch_bits_count; - unsigned int byte_cursor; -} NBN_BitReader; - -void NBN_BitReader_Init(NBN_BitReader *, uint8_t *, unsigned int); -int NBN_BitReader_Read(NBN_BitReader *, Word *, unsigned int); - -#pragma endregion /* NBN_BitReader */ - -#pragma region NBN_BitWriter - -typedef struct NBN_BitWriter +typedef struct NBN_Writer { - unsigned int size; uint8_t *buffer; - uint64_t scratch; - unsigned int scratch_bits_count; - unsigned int byte_cursor; -} NBN_BitWriter; - -void NBN_BitWriter_Init(NBN_BitWriter*, uint8_t *, unsigned int); -int NBN_BitWriter_Write(NBN_BitWriter *, Word, unsigned int); -int NBN_BitWriter_Flush(NBN_BitWriter *); - -#pragma endregion /* NBN_BitWriter */ - -#pragma region NBN_Stream - -typedef struct NBN_Stream NBN_Stream; - -typedef int (*NBN_Stream_SerializeUInt)(NBN_Stream *, unsigned int *, unsigned int, unsigned int); -typedef int (*NBN_Stream_SerializeUInt64)(NBN_Stream *, uint64_t *); -typedef int (*NBN_Stream_SerializeInt)(NBN_Stream *, int *, int, int); -typedef int (*NBN_Stream_SerializeFloat)(NBN_Stream *, float *, float, float, int); -typedef int (*NBN_Stream_SerializeBool)(NBN_Stream *, bool *); -typedef int (*NBN_Stream_SerializePadding)(NBN_Stream *); -typedef int (*NBN_Stream_SerializeBytes)(NBN_Stream *, uint8_t *, unsigned int); - -typedef enum NBN_StreamType -{ - NBN_STREAM_WRITE, - NBN_STREAM_READ, - NBN_STREAM_MEASURE -} NBN_StreamType; - -struct NBN_Stream -{ - NBN_StreamType type; - NBN_Stream_SerializeUInt serialize_uint_func; - NBN_Stream_SerializeUInt64 serialize_uint64_func; - NBN_Stream_SerializeInt serialize_int_func; - NBN_Stream_SerializeFloat serialize_float_func; - NBN_Stream_SerializeBool serialize_bool_func; - NBN_Stream_SerializePadding serialize_padding_func; - NBN_Stream_SerializeBytes serialize_bytes_func; -}; - -#pragma endregion /* NBN_Stream */ - -#pragma region NBN_ReadStream - -typedef struct NBN_ReadStream -{ - NBN_Stream base; - NBN_BitReader bit_reader; -} NBN_ReadStream; - -void NBN_ReadStream_Init(NBN_ReadStream *, uint8_t *, unsigned int); -int NBN_ReadStream_SerializeUint(NBN_ReadStream *, unsigned int *, unsigned int, unsigned int); -int NBN_ReadStream_SerializeUint64(NBN_ReadStream *read_stream, uint64_t *value); -int NBN_ReadStream_SerializeInt(NBN_ReadStream *, int *, int, int); -int NBN_ReadStream_SerializeFloat(NBN_ReadStream *, float *, float, float, int); -int NBN_ReadStream_SerializeBool(NBN_ReadStream *, bool *); -int NBN_ReadStream_SerializePadding(NBN_ReadStream *); -int NBN_ReadStream_SerializeBytes(NBN_ReadStream *, uint8_t *, unsigned int); - -#pragma endregion /* NBN_ReadStream */ - -#pragma region NBN_WriteStream + unsigned int length; + unsigned int position; +} NBN_Writer; -typedef struct NBN_WriteStream +typedef struct NBN_Reader { - NBN_Stream base; - NBN_BitWriter bit_writer; -} NBN_WriteStream; - -void NBN_WriteStream_Init(NBN_WriteStream *, uint8_t *, unsigned int); -int NBN_WriteStream_SerializeUint(NBN_WriteStream *, unsigned int *, unsigned int, unsigned int); -int NBN_WriteStream_SerializeUint64(NBN_WriteStream *write_stream, uint64_t *value); -int NBN_WriteStream_SerializeInt(NBN_WriteStream *, int *, int, int); -int NBN_WriteStream_SerializeFloat(NBN_WriteStream *, float *, float, float, int); -int NBN_WriteStream_SerializeBool(NBN_WriteStream *, bool *); -int NBN_WriteStream_SerializePadding(NBN_WriteStream *); -int NBN_WriteStream_SerializeBytes(NBN_WriteStream *, uint8_t *, unsigned int); -int NBN_WriteStream_Flush(NBN_WriteStream *); - -#pragma endregion /* NBN_WriteStream */ - -#pragma region NBN_MeasureStream - -typedef struct NBN_MeasureStream -{ - NBN_Stream base; - unsigned int number_of_bits; -} NBN_MeasureStream; - -void NBN_MeasureStream_Init(NBN_MeasureStream *); -int NBN_MeasureStream_SerializeUint(NBN_MeasureStream *, unsigned int *, unsigned int, unsigned int); -int NBN_MeasureStream_SerializeUint64(NBN_MeasureStream *measure_stream, unsigned int *value); -int NBN_MeasureStream_SerializeInt(NBN_MeasureStream *, int *, int, int); -int NBN_MeasureStream_SerializeFloat(NBN_MeasureStream *, float *, float, float, int); -int NBN_MeasureStream_SerializeBool(NBN_MeasureStream *, bool *); -int NBN_MeasureStream_SerializePadding(NBN_MeasureStream *); -int NBN_MeasureStream_SerializeBytes(NBN_MeasureStream *, uint8_t *, unsigned int); -void NBN_MeasureStream_Reset(NBN_MeasureStream *); -unsigned int NBN_MeasureStream_CountBytes(NBN_MeasureStream *); - -#pragma endregion /* NBN_MeasureStream */ + uint8_t *buffer; + unsigned int length; + unsigned int position; +} NBN_Reader; + +void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length); +void NBN_Writer_WriteInt32(NBN_Writer *writer, int32_t value); +void NBN_Writer_WriteUInt16(NBN_Writer *writer, uint16_t value); +void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value); +void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value); +void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int length); + +void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length); +int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value); +int NBN_Reader_ReadUInt16(NBN_Reader *reader, uint16_t *value); +int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value); +int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value); +int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length); #pragma endregion /* Serialization */ @@ -312,10 +215,6 @@ unsigned int NBN_MeasureStream_CountBytes(NBN_MeasureStream *); #define NBN_MESSAGE_RESEND_DELAY 0.1 /* Number of seconds before a message is resent (reliable messages redundancy) */ #define NBN_MESSAGE_HEADER_SIZE 6 /* See NBN_MessageHeader struct */ -typedef int (*NBN_MessageSerializer)(void *, NBN_Stream *); -typedef void *(*NBN_MessageBuilder)(void); -typedef void (*NBN_MessageDestructor)(void *); - // IMPORTANT: make sure you update NBN_MESSAGE_HEADER_SIZE if you modify NBN_MessageHeader struct typedef struct NBN_MessageHeader { @@ -338,15 +237,18 @@ typedef struct NBN_Message */ typedef struct NBN_MessageInfo { - /** User defined message's type */ + /** User defined message type */ uint8_t type; /** Channel the message was received on */ uint8_t channel_id; - /** Message's data */ + /** Message data */ uint8_t *data; + /** Length of message data in bytes */ + uint16_t length; + /** * The message's sender. * @@ -410,6 +312,7 @@ typedef struct NBN_Packet void NBN_Packet_InitWrite(NBN_Packet *, uint32_t, uint16_t, uint16_t, uint32_t); int NBN_Packet_WriteMessage(NBN_Packet *, NBN_Message *); int NBN_Packet_Seal(NBN_Packet *, NBN_Connection *); +int NBN_Packet_InitRead(NBN_Packet *, NBN_Connection *, unsigned int); #pragma endregion /* NBN_Packet */ @@ -463,7 +366,7 @@ typedef struct NBN_MessageSlot struct NBN_Channel { uint8_t id; - uint8_t *write_chunk_buffer; + // uint8_t *write_chunk_buffer; uint16_t next_outgoing_message_id; uint16_t next_recv_message_id; unsigned int next_outgoing_message_pool_slot; @@ -473,12 +376,12 @@ struct NBN_Channel unsigned int read_chunk_buffer_size; unsigned int next_outgoing_chunked_message; int last_received_chunk_id; - uint8_t *read_chunk_buffer; + // uint8_t *read_chunk_buffer; NBN_ChannelDestructor destructor; NBN_Connection *connection; NBN_MessageSlot outgoing_message_slot_buffer[NBN_CHANNEL_BUFFER_SIZE]; NBN_MessageSlot recved_message_slot_buffer[NBN_CHANNEL_BUFFER_SIZE]; - NBN_MessageChunk *recv_chunk_buffer[NBN_CHANNEL_CHUNKS_BUFFER_SIZE]; + // NBN_MessageChunk *recv_chunk_buffer[NBN_CHANNEL_CHUNKS_BUFFER_SIZE]; NBN_Message outgoing_message_pool[NBN_CHANNEL_OUTGOING_MESSAGE_POOL_SIZE]; bool (*AddReceivedMessage)(NBN_Channel *, NBN_Message *); @@ -490,10 +393,10 @@ struct NBN_Channel }; void NBN_Channel_Destroy(NBN_Channel *); -bool NBN_Channel_AddChunk(NBN_Channel *, NBN_Message *); +// bool NBN_Channel_AddChunk(NBN_Channel *, NBN_Message *); int NBN_Channel_ReconstructMessageFromChunks(NBN_Channel *, NBN_Connection *, NBN_Message *); -void NBN_Channel_ResizeWriteChunkBuffer(NBN_Channel *, unsigned int); -void NBN_Channel_ResizeReadChunkBuffer(NBN_Channel *, unsigned int); +/*void NBN_Channel_ResizeWriteChunkBuffer(NBN_Channel *, unsigned int);*/ +/*void NBN_Channel_ResizeReadChunkBuffer(NBN_Channel *, unsigned int);*/ void NBN_Channel_UpdateMessageLastSendTime(NBN_Channel *, NBN_Message *, double); /* @@ -623,7 +526,7 @@ struct NBN_ConnectionListNode NBN_Connection *NBN_Connection_Create(uint32_t, uint32_t, NBN_Endpoint *, NBN_Driver *, void *); void NBN_Connection_Destroy(NBN_Connection *); int NBN_Connection_ProcessReceivedPacket(NBN_Connection *, NBN_Packet *, double); -int NBN_Connection_FlushSendQueue(NBN_Connection *, double); +int NBN_Connection_FlushChannels(NBN_Connection *, double); int NBN_Connection_InitChannel(NBN_Connection *, NBN_Channel *); bool NBN_Connection_CheckIfStale(NBN_Connection *, double); @@ -1313,7 +1216,7 @@ static void NBN_ConnectionVector_Destroy(NBN_ConnectionVector *vector) static void NBN_ConnectionVector_Add(NBN_ConnectionVector *vector, NBN_Connection *conn) { - assert(conn->vector_pos == -1); + NBN_Assert(conn->vector_pos == -1); if (vector->count >= vector->capacity) { @@ -1538,7 +1441,7 @@ static void NBN_ConnectionTable_Grow(NBN_ConnectionTable *table, unsigned int ne // rehash - assert(old_connections_array || old_capacity == 0); + NBN_Assert(old_connections_array || old_capacity == 0); for (unsigned int i = 0; i < old_capacity; i++) { @@ -1586,7 +1489,7 @@ static void MemoryManager_Init(void) #else NBN_LogDebug("MemoryManager_Init with pooling!"); - MemPool_Init(&nbn_mem_manager.mem_pools[NBN_MEM_MESSAGE_CHUNK], sizeof(NBN_MessageChunk), 256); + // MemPool_Init(&nbn_mem_manager.mem_pools[NBN_MEM_MESSAGE_CHUNK], sizeof(NBN_MessageChunk), 256); MemPool_Init(&nbn_mem_manager.mem_pools[NBN_MEM_CONNECTION], sizeof(NBN_Connection), 16); #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) @@ -1693,549 +1596,114 @@ static void MemPool_Grow(NBN_MemPool *pool, unsigned int block_count) #pragma region Serialization -static unsigned int GetRequiredNumberOfBitsFor(unsigned int v) +void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length) { - unsigned int a = v | (v >> 1); - unsigned int b = a | (a >> 2); - unsigned int c = b | (b >> 4); - unsigned int d = c | (c >> 8); - unsigned int e = d | (d >> 16); - unsigned int f = e >> 1; - - unsigned int i = f - ((f >> 1) & 0x55555555); - unsigned int j = (((i >> 2) & 0x33333333) + (i & 0x33333333)); - unsigned int k = (((j >> 4) + j) & 0x0f0f0f0f); - unsigned int l = k + (k >> 8); - unsigned int m = l + (l >> 16); - - return (m & 0x0000003f) + 1; + writer->buffer = buffer; + writer->length = length; + writer->position = 0; } -#pragma region NBN_BitReader - -static void BitReader_ReadFromBuffer(NBN_BitReader *); - -void NBN_BitReader_Init(NBN_BitReader *bit_reader, uint8_t *buffer, unsigned int size) +void NBN_Writer_WriteInt32(NBN_Writer *writer, int32_t value) { - bit_reader->size = size; - bit_reader->buffer = buffer; - bit_reader->scratch = 0; - bit_reader->scratch_bits_count = 0; - bit_reader->byte_cursor = 0; + NBN_Writer_WriteUInt32(writer, value); } -int NBN_BitReader_Read(NBN_BitReader *bit_reader, Word *word, unsigned int number_of_bits) +void NBN_Writer_WriteUInt16(NBN_Writer *writer, uint16_t value) { - *word = 0; + NBN_Assert(writer->position + 2 <= writer->length); - if (number_of_bits > bit_reader->scratch_bits_count) - { - unsigned int needed_bytes = (number_of_bits - bit_reader->scratch_bits_count - 1) / 8 + 1; - - if (bit_reader->byte_cursor + needed_bytes > bit_reader->size) - return NBN_ERROR; - - BitReader_ReadFromBuffer(bit_reader); - } - - *word |= (bit_reader->scratch & (((uint64_t)1 << number_of_bits) - 1)); - bit_reader->scratch >>= number_of_bits; - bit_reader->scratch_bits_count -= number_of_bits; - - return 0; + *((uint16_t *)(writer->buffer + writer->position)) = htons(value); + writer->position += 2; } -static void BitReader_ReadFromBuffer(NBN_BitReader *bit_reader) +void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value) { - unsigned int bytes_count = MIN(bit_reader->size - bit_reader->byte_cursor, WORD_BYTES); - Word word = 0; - - memcpy(&word, bit_reader->buffer + bit_reader->byte_cursor, bytes_count); + NBN_Assert(writer->position + 4 <= writer->length); - bit_reader->scratch |= (uint64_t)word << bit_reader->scratch_bits_count; - bit_reader->scratch_bits_count += bytes_count * 8; - bit_reader->byte_cursor += bytes_count; + *((uint32_t *)(writer->buffer + writer->position)) = htonl(value); + writer->position += 4; } -#pragma endregion /* NBN_BitReader */ - -#pragma region NBN_BitWriter - -static int BitWriter_FlushScratchBits(NBN_BitWriter *, unsigned int); - -void NBN_BitWriter_Init(NBN_BitWriter *bit_writer, uint8_t *buffer, unsigned int size) +void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value) { - bit_writer->size = size; - bit_writer->buffer = buffer; - bit_writer->scratch = 0; - bit_writer->scratch_bits_count = 0; - bit_writer->byte_cursor = 0; -} + NBN_Assert(writer->position + 1 <= writer->length); -int NBN_BitWriter_Write(NBN_BitWriter *bit_writer, Word value, unsigned int number_of_bits) -{ - bit_writer->scratch |= ((uint64_t)value << bit_writer->scratch_bits_count); - - if ((bit_writer->scratch_bits_count += number_of_bits) >= WORD_BITS) - return BitWriter_FlushScratchBits(bit_writer, WORD_BITS); - - return 0; -} - -int NBN_BitWriter_Flush(NBN_BitWriter *bit_writer) -{ - return BitWriter_FlushScratchBits(bit_writer, bit_writer->scratch_bits_count); + writer->buffer[writer->position] = value; + writer->position++; } -static int BitWriter_FlushScratchBits(NBN_BitWriter *bit_writer, unsigned int number_of_bits) +void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int length) { - if (bit_writer->scratch_bits_count < 1) - return 0; - - unsigned int bytes_count = (number_of_bits - 1) / 8 + 1; + NBN_Assert(writer->position + length <= writer->length); - assert(bytes_count <= WORD_BYTES); - - if (bit_writer->byte_cursor + bytes_count > bit_writer->size) - return NBN_ERROR; - - Word word = 0 | (bit_writer->scratch & (((uint64_t)1 << number_of_bits) - 1)); - - memcpy(bit_writer->buffer + bit_writer->byte_cursor, &word, bytes_count); - - bit_writer->scratch >>= number_of_bits; - bit_writer->scratch_bits_count -= number_of_bits; - bit_writer->byte_cursor += bytes_count; - - return 0; + memcpy(writer->buffer + writer->position, bytes, length); + writer->position += length; } -#pragma endregion /* NBN_BitWriter */ - -#pragma region NBN_ReadStream - -void NBN_ReadStream_Init(NBN_ReadStream *read_stream, uint8_t *buffer, unsigned int size) +void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length) { - read_stream->base.type = NBN_STREAM_READ; - read_stream->base.serialize_uint_func = (NBN_Stream_SerializeUInt)NBN_ReadStream_SerializeUint; - read_stream->base.serialize_uint64_func = (NBN_Stream_SerializeUInt64)NBN_ReadStream_SerializeUint64; - read_stream->base.serialize_int_func = (NBN_Stream_SerializeInt)NBN_ReadStream_SerializeInt; - read_stream->base.serialize_float_func = (NBN_Stream_SerializeFloat)NBN_ReadStream_SerializeFloat; - read_stream->base.serialize_bool_func = (NBN_Stream_SerializeBool)NBN_ReadStream_SerializeBool; - read_stream->base.serialize_padding_func = (NBN_Stream_SerializePadding)NBN_ReadStream_SerializePadding; - read_stream->base.serialize_bytes_func = (NBN_Stream_SerializeBytes)NBN_ReadStream_SerializeBytes; - - NBN_BitReader_Init(&read_stream->bit_reader, buffer, size); + reader->buffer = buffer; + reader->length = length; + reader->position = 0; } -int NBN_ReadStream_SerializeUint(NBN_ReadStream *read_stream, unsigned int *value, unsigned int min, unsigned int max) +int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value) { - assert(min <= max); - - if (NBN_BitReader_Read(&read_stream->bit_reader, value, BITS_REQUIRED(min, max)) < 0) - return NBN_ERROR; - - *value += min; - -#ifdef NBN_DEBUG - assert(*value >= min && *value <= max); -#else - if (*value < min || *value > max) - return NBN_ERROR; -#endif - - return 0; + return NBN_Reader_ReadUInt32(reader, (uint32_t *)value); } -int NBN_ReadStream_SerializeUint64(NBN_ReadStream *read_stream, uint64_t *value) +int NBN_Reader_ReadUInt16(NBN_Reader *reader, uint16_t *value) { - union uint64_to_bytes + if (reader->position + 2 > reader->length) { - uint64_t v; - uint8_t bytes[8]; - } u; - - if (NBN_ReadStream_SerializeBytes(read_stream, u.bytes, 8) < 0) - return NBN_ERROR; - - *value = u.v; - - return 0; -} - -int NBN_ReadStream_SerializeInt(NBN_ReadStream *read_stream, int *value, int min, int max) -{ - assert(min <= max); - - bool isNegative = 0; - unsigned int abs_min = MIN(abs(min), abs(max)); - unsigned int abs_max = MAX(abs(min), abs(max)); - - *value = abs(*value); - - if (NBN_ReadStream_SerializeBool(read_stream, &isNegative) < 0) - return NBN_ERROR; - - if (NBN_ReadStream_SerializeUint(read_stream, (unsigned int *)value, (min < 0 && max > 0) ? 0 : abs_min, abs_max) < 0) - return NBN_ERROR; - - if (isNegative) - *value *= -1; - - return 0; -} - -int NBN_ReadStream_SerializeFloat(NBN_ReadStream *read_stream, float *value, float min, float max, int precision) -{ - assert(min <= max); - - unsigned int mult = pow(10, precision); - int i_min = min * mult; - int i_max = max * mult; - int i_val = 0; - - if (NBN_ReadStream_SerializeInt(read_stream, &i_val, i_min, i_max) < 0) - return NBN_ERROR; - - *value = (float)i_val / mult; - - return 0; -} - -int NBN_ReadStream_SerializeBool(NBN_ReadStream *read_stream, bool *value) -{ - Word v; - - if (NBN_BitReader_Read(&read_stream->bit_reader, &v, 1) < 0) - return NBN_ERROR; - - if (v < 0 || v > 1) return NBN_ERROR; + } - *value = v; + *value = *((uint16_t *)(reader->buffer + reader->position)); + reader->position += 2; return 0; } -int NBN_ReadStream_SerializePadding(NBN_ReadStream *read_stream) +int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value) { - // if we are at the beginning of a new byte, no need to pad - if (read_stream->bit_reader.scratch_bits_count % 8 == 0) - return 0; - - Word value; - unsigned int padding = read_stream->bit_reader.scratch_bits_count % 8; - int ret = NBN_BitReader_Read(&read_stream->bit_reader, &value, padding); - -#ifdef NBN_DEBUG - assert(value == 0); -#else - if (value != 0) - return NBN_ERROR; -#endif - - return ret; -} - -int NBN_ReadStream_SerializeBytes(NBN_ReadStream *read_stream, uint8_t *bytes, unsigned int length) -{ - if (length == 0) - return NBN_ERROR; - - if (NBN_ReadStream_SerializePadding(read_stream) < 0) + if (reader->position + 4 > reader->length) { return NBN_ERROR; } - NBN_BitReader *bit_reader = &read_stream->bit_reader; - - // make sure we are at the start of a new byte after applying padding - assert(bit_reader->scratch_bits_count % 8 == 0); - - if (length * 8 <= bit_reader->scratch_bits_count) - { - // the byte array is fully contained inside the read word - - Word word; - - if (NBN_BitReader_Read(bit_reader, &word, length * 8) < 0) - { - return NBN_ERROR; - } - - memcpy(bytes, &word, length); - } - else - { - // reading is done word by word and in this case, the start of byte array has already been read - // therefore we need to "roll back" the byte cursor so it is at the very begining of the byte array - // inside the buffer - - bit_reader->byte_cursor -= (bit_reader->scratch_bits_count / 8); - bit_reader->scratch_bits_count = 0; - bit_reader->scratch = 0; - - memcpy(bytes, bit_reader->buffer + bit_reader->byte_cursor, length); - - bit_reader->byte_cursor += length; - } + *value = *((uint32_t *)(reader->buffer + reader->position)); + reader->position += 4; return 0; } -#pragma endregion /* NBN_ReadStream */ - -#pragma region NBN_WriteStream - -void NBN_WriteStream_Init(NBN_WriteStream *write_stream, uint8_t *buffer, unsigned int size) +int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value) { - write_stream->base.type = NBN_STREAM_WRITE; - write_stream->base.serialize_uint_func = (NBN_Stream_SerializeUInt)NBN_WriteStream_SerializeUint; - write_stream->base.serialize_uint64_func = (NBN_Stream_SerializeUInt64)NBN_WriteStream_SerializeUint64; - write_stream->base.serialize_int_func = (NBN_Stream_SerializeInt)NBN_WriteStream_SerializeInt; - write_stream->base.serialize_float_func = (NBN_Stream_SerializeFloat)NBN_WriteStream_SerializeFloat; - write_stream->base.serialize_bool_func = (NBN_Stream_SerializeBool)NBN_WriteStream_SerializeBool; - write_stream->base.serialize_padding_func = (NBN_Stream_SerializePadding)NBN_WriteStream_SerializePadding; - write_stream->base.serialize_bytes_func = (NBN_Stream_SerializeBytes)NBN_WriteStream_SerializeBytes; - - NBN_BitWriter_Init(&write_stream->bit_writer, buffer, size); -} - -int NBN_WriteStream_SerializeUint( - NBN_WriteStream *write_stream, unsigned int *value, unsigned int min, unsigned int max) -{ - assert(min <= max); - assert(*value >= min && *value <= max); - - if (NBN_BitWriter_Write(&write_stream->bit_writer, *value - min, BITS_REQUIRED(min, max)) < 0) - return NBN_ERROR; - - return 0; -} - -int NBN_WriteStream_SerializeUint64(NBN_WriteStream *write_stream, uint64_t *value) -{ - union uint64_to_bytes + if (reader->position + 1 > reader->length) { - uint64_t v; - uint8_t bytes[8]; - } u; - - u.v = *value; - - return NBN_WriteStream_SerializeBytes(write_stream, u.bytes, 8); -} - -int NBN_WriteStream_SerializeInt(NBN_WriteStream *write_stream, int *value, int min, int max) -{ - assert(min <= max); - - unsigned int isNegative = 0; - unsigned int abs_min = MIN(abs(min), abs(max)); - unsigned int abs_max = MAX(abs(min), abs(max)); - - isNegative = *value < 0; - *value = abs(*value); - - if (NBN_WriteStream_SerializeUint(write_stream, &isNegative, 0, 1) < 0) return NBN_ERROR; + } - if (NBN_WriteStream_SerializeUint( - write_stream, (unsigned int *)value, (min < 0 && max > 0) ? 0 : abs_min, abs_max) < 0) - return NBN_ERROR; - - if (isNegative) - *value *= -1; - - return 0; -} - -int NBN_WriteStream_SerializeFloat(NBN_WriteStream *write_stream, float *value, float min, float max, int precision) -{ - assert(min <= max); - - unsigned int mult = pow(10, precision); - int i_min = min * mult; - int i_max = max * mult; - int i_val = *value * mult; - - if (NBN_WriteStream_SerializeInt(write_stream, &i_val, i_min, i_max) < 0) - return NBN_ERROR; + *value = reader->buffer[reader->position]; + reader->position++; return 0; } -int NBN_WriteStream_SerializeBool(NBN_WriteStream *write_stream, bool *value) +int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length) { - int v = *value; - - assert(v >= 0 && v <= 1); - - if (NBN_BitWriter_Write(&write_stream->bit_writer, v, 1) < 0) - return NBN_ERROR; - - return 0; -} - -int NBN_WriteStream_SerializePadding(NBN_WriteStream *write_stream) -{ - // if we are at the beginning of a new byte, no need to pad - if (write_stream->bit_writer.scratch_bits_count % 8 == 0) - return 0; - - unsigned int padding = 8 - (write_stream->bit_writer.scratch_bits_count % 8); - - return NBN_BitWriter_Write(&write_stream->bit_writer, 0, padding); -} - -int NBN_WriteStream_SerializeBytes(NBN_WriteStream *write_stream, uint8_t *bytes, unsigned int length) -{ - if (length == 0) - return NBN_ERROR; - - if (NBN_WriteStream_SerializePadding(write_stream) < 0) - return NBN_ERROR; - - NBN_BitWriter *bit_writer = &write_stream->bit_writer; - - // make sure we are at the start of a new byte after applying padding - assert(bit_writer->scratch_bits_count % 8 == 0); - - if (NBN_WriteStream_Flush(write_stream) < 0) - return NBN_ERROR; - - // make sure everything has been flushed to the buffer before writing the byte array - assert(bit_writer->scratch_bits_count == 0); - - if (bit_writer->byte_cursor + length > bit_writer->size) + if (reader->position + length > reader->length) + { return NBN_ERROR; + } - memcpy(bit_writer->buffer + bit_writer->byte_cursor, bytes, length); - - bit_writer->byte_cursor += length; + memcpy(bytes, reader->buffer + reader->position, length); + reader->position += length; return 0; } -int NBN_WriteStream_Flush(NBN_WriteStream *write_stream) -{ - return NBN_BitWriter_Flush(&write_stream->bit_writer); -} - -#pragma endregion /* NBN_WriteStream */ - -#pragma region NBN_MeasureStream - -void NBN_MeasureStream_Init(NBN_MeasureStream *measure_stream) -{ - measure_stream->base.type = NBN_STREAM_MEASURE; - measure_stream->base.serialize_uint_func = (NBN_Stream_SerializeUInt)NBN_MeasureStream_SerializeUint; - measure_stream->base.serialize_uint64_func = (NBN_Stream_SerializeUInt64)NBN_MeasureStream_SerializeUint64; - measure_stream->base.serialize_int_func = (NBN_Stream_SerializeInt)NBN_MeasureStream_SerializeInt; - measure_stream->base.serialize_float_func = (NBN_Stream_SerializeFloat)NBN_MeasureStream_SerializeFloat; - measure_stream->base.serialize_bool_func = (NBN_Stream_SerializeBool)NBN_MeasureStream_SerializeBool; - measure_stream->base.serialize_padding_func = (NBN_Stream_SerializePadding)NBN_MeasureStream_SerializePadding; - measure_stream->base.serialize_bytes_func = (NBN_Stream_SerializeBytes)NBN_MeasureStream_SerializeBytes; - - measure_stream->number_of_bits = 0; -} - -int NBN_MeasureStream_SerializeUint(NBN_MeasureStream *measure_stream, unsigned int *value, unsigned int min, unsigned int max) -{ - (void)*value; - - assert(min <= max); - // assert(*value >= min && *value <= max); - - unsigned int number_of_bits = BITS_REQUIRED(min, max); - - measure_stream->number_of_bits += number_of_bits; - - return number_of_bits; -} - -int NBN_MeasureStream_SerializeUint64(NBN_MeasureStream *measure_stream, unsigned int *value) -{ - (void)value; - - return NBN_MeasureStream_SerializeBytes(measure_stream, NULL, 8); -} - -int NBN_MeasureStream_SerializeInt(NBN_MeasureStream *measure_stream, int *value, int min, int max) -{ - assert(min <= max); - assert(*value >= min && *value <= max); - - unsigned int abs_min = MIN(abs(min), abs(max)); - unsigned int abs_max = MAX(abs(min), abs(max)); - unsigned int abs_value = abs(*value); - unsigned number_of_bits = NBN_MeasureStream_SerializeUint(measure_stream, &abs_value, (min < 0 && max > 0) ? 0 : abs_min, abs_max); - - measure_stream->number_of_bits++; // +1 for int sign - - return number_of_bits + 1; -} - -int NBN_MeasureStream_SerializeFloat( - NBN_MeasureStream *measure_stream, float *value, float min, float max, int precision) -{ - assert(min <= max); - assert(*value >= min && *value <= max); - - unsigned int mult = pow(10, precision); - int i_min = min * mult; - int i_max = max * mult; - int i_val = *value * mult; - - return NBN_MeasureStream_SerializeInt(measure_stream, &i_val, i_min, i_max); -} - -int NBN_MeasureStream_SerializeBool(NBN_MeasureStream *measure_stream, bool *value) -{ - (void)value; - - measure_stream->number_of_bits++; - - return 1; -} - -int NBN_MeasureStream_SerializePadding(NBN_MeasureStream *measure_stream) -{ - if (measure_stream->number_of_bits % 8 == 0) - return 0; - - unsigned int padding = 8 - (measure_stream->number_of_bits % 8); - - measure_stream->number_of_bits += padding; - - return padding; -} - -int NBN_MeasureStream_SerializeBytes(NBN_MeasureStream *measure_stream, uint8_t *bytes, unsigned int length) -{ - (void)bytes; - - NBN_MeasureStream_SerializePadding(measure_stream); - - unsigned int bits = length * 8; - - measure_stream->number_of_bits += bits; - - return bits; -} - -void NBN_MeasureStream_Reset(NBN_MeasureStream *measure_stream) -{ - measure_stream->number_of_bits = 0; -} - -unsigned int NBN_MeasureStream_CountBytes(NBN_MeasureStream *measure_stream) -{ - return (measure_stream->number_of_bits + 8) / 8; -} - -#pragma endregion /* NBN_MeasureStream */ - #pragma endregion /* Serialization */ #pragma region NBN_Packet @@ -2250,53 +1718,46 @@ void NBN_Packet_InitWrite(NBN_Packet *packet, uint32_t protocol_id, uint16_t seq packet->mode = NBN_PACKET_MODE_WRITE; packet->sender = NULL; - packet->size = 0; + packet->size = NBN_PACKET_HEADER_SIZE; packet->sealed = false; } int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_Message *message) { - if (packet->mode != NBN_PACKET_MODE_WRITE || packet->sealed) - return NBN_PACKET_WRITE_ERROR; + NBN_Assert( + (message->data != NULL && message->header.length > 0) || + (message->data == NULL && message->header.length == 0)); + + if (packet->mode != NBN_PACKET_MODE_WRITE || packet->sealed) return NBN_PACKET_WRITE_ERROR; unsigned int message_size = NBN_MESSAGE_HEADER_SIZE + message->header.length; if ( packet->header.messages_count >= NBN_MAX_MESSAGES_PER_PACKET || - packet->size + message_size > NBN_PACKET_MAX_DATA_SIZE) + packet->size + message_size > NBN_PACKET_MAX_SIZE) { return NBN_PACKET_WRITE_NO_SPACE; } - // Write message header - // TODO: endianess - - uint8_t *packet_ptr = packet->buffer + packet->size; - - *((uint16_t *)packet_ptr) = message->header.id; - packet_ptr += 2; - *((uint16_t *)packet_ptr) = message->header.length; - packet_ptr += 2; - *packet_ptr = message->header.type; - packet_ptr++; - *packet_ptr = message->header.channel_id; - packet_ptr++; + NBN_Writer writer; - assert(packet_ptr - packet->buffer == NBN_MESSAGE_HEADER_SIZE); + NBN_Writer_Init(&writer, packet->buffer + packet->size, sizeof(packet->buffer) - packet->size); - // Write message data + NBN_Writer_WriteUInt16(&writer, message->header.id); + NBN_Writer_WriteUInt16(&writer, message->header.length); + NBN_Writer_WriteUInt8(&writer, message->header.type); + NBN_Writer_WriteUInt8(&writer, message->header.channel_id); - assert((packet->buffer + sizeof(packet->buffer)) - packet_ptr >= message->header.length); - - assert(message->data != NULL || message->header.length == 0); + NBN_Assert(writer.position == NBN_MESSAGE_HEADER_SIZE); if (message->data) { - assert(message->header.length > 0); - memcpy(packet_ptr, message->data, message->header.length); + NBN_Writer_WriteBytes(&writer, message->data, message->header.length); } - packet->size += message_size; + NBN_Assert(writer.position == message_size); + + packet->size += writer.position; packet->header.messages_count++; return NBN_PACKET_WRITE_OK; @@ -2307,24 +1768,47 @@ int NBN_Packet_Seal(NBN_Packet *packet, NBN_Connection *connection) if (packet->mode != NBN_PACKET_MODE_WRITE) return NBN_ERROR; - uint8_t *packet_buffer = packet->buffer; + NBN_Writer writer; + + NBN_Writer_Init(&writer, packet->buffer, NBN_PACKET_HEADER_SIZE); + + NBN_Writer_WriteUInt32(&writer, packet->header.protocol_id); + NBN_Writer_WriteUInt32(&writer, packet->header.ack_bits); + NBN_Writer_WriteUInt16(&writer, packet->header.seq_number); + NBN_Writer_WriteUInt16(&writer, packet->header.ack); + NBN_Writer_WriteUInt8(&writer, packet->header.messages_count); - *((uint32_t *)packet_buffer) = packet->header.protocol_id; - packet_buffer += 4; - *((uint32_t *)packet_buffer) = packet->header.ack_bits; - packet_buffer += 4; - *((uint16_t *)packet_buffer) = packet->header.seq_number; - packet_buffer += 2; - *((uint16_t *)packet_buffer) = packet->header.ack; - packet_buffer += 2; - *packet_buffer = packet->header.messages_count; + NBN_Assert(writer.position == NBN_PACKET_HEADER_SIZE); - packet->size += NBN_PACKET_HEADER_SIZE; packet->sealed = true; return 0; } +int NBN_Packet_InitRead(NBN_Packet *packet, NBN_Connection *sender, unsigned int size) +{ + NBN_Assert(size >= NBN_PACKET_HEADER_SIZE); + + packet->mode = NBN_PACKET_MODE_READ; + packet->sender = sender; + packet->size = size; + packet->sealed = false; + + NBN_Reader reader; + + NBN_Reader_Init(&reader, packet->buffer, NBN_PACKET_HEADER_SIZE); + + if (NBN_Reader_ReadUInt32(&reader, &packet->header.protocol_id) < 0) return NBN_ERROR; + if (packet->header.protocol_id != sender->protocol_id) return NBN_ERROR; + + if (NBN_Reader_ReadUInt32(&reader, &packet->header.ack_bits) < 0) return NBN_ERROR; + if (NBN_Reader_ReadUInt16(&reader, &packet->header.seq_number) < 0) return NBN_ERROR; + if (NBN_Reader_ReadUInt16(&reader, &packet->header.ack) < 0) return NBN_ERROR; + if (NBN_Reader_ReadUInt8(&reader, &packet->header.messages_count) < 0) return NBN_ERROR; + + return 0; +} + #pragma endregion /* NBN_Packet */ #pragma region NBN_Connection @@ -2340,7 +1824,7 @@ static bool Connection_InsertReceivedPacketEntry(NBN_Connection *, uint16_t); static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *, uint16_t); static bool Connection_IsPacketReceived(NBN_Connection *, uint16_t); static int Connection_SendPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry *, double); -static int Connection_ReadNextMessageFromBuffer(NBN_Connection *, uint8_t *, NBN_Message *); +static int Connection_ReadNextMessageFromBuffer(NBN_Reader *, NBN_Message *); static void Connection_RecycleMessage(NBN_Channel *, NBN_Message *); static void Connection_UpdateAveragePing(NBN_Connection *, double); static void Connection_UpdateAveragePacketLoss(NBN_Connection *, uint16_t); @@ -2416,27 +1900,32 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Connection *connection, NBN_Packet if (SEQUENCE_NUMBER_GT(packet->header.seq_number, connection->last_received_packet_seq_number)) connection->last_received_packet_seq_number = packet->header.seq_number; - uint8_t *packet_buffer = packet->buffer; + NBN_Reader msg_reader; + + NBN_Reader_Init(&msg_reader, packet->buffer + NBN_PACKET_HEADER_SIZE, packet->size - NBN_PACKET_HEADER_SIZE); for (int i = 0; i < packet->header.messages_count; i++) { NBN_Message message = {0}; - int length; + int msg_len = Connection_ReadNextMessageFromBuffer(&msg_reader, &message); - // TODO: make sure we can't read past the packet buffer - - if ((length = Connection_ReadNextMessageFromBuffer(connection, packet_buffer, &message)) < 0) + if (msg_len < 0) { - NBN_LogError("Failed to read message from packet"); + NBN_LogError("Failed to read packet, invalid data"); return NBN_ERROR; } - packet_buffer += (NBN_MESSAGE_HEADER_SIZE + length); + uint8_t channel_id = message.header.channel_id; - NBN_Channel *channel = connection->channels[message.header.channel_id]; + if (channel_id > NBN_MAX_CHANNELS - 1 || connection->channels[channel_id] == NULL) + { + NBN_LogError("Failed to read packet, messages on invalid channels"); - assert(channel); + return NBN_ERROR; + } + + NBN_Channel *channel = connection->channels[channel_id]; if (channel->AddReceivedMessage(channel, &message)) { @@ -2457,7 +1946,7 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Connection *connection, NBN_Packet return 0; } -int NBN_Connection_FlushSendQueue(NBN_Connection *connection, double time) +int NBN_Connection_FlushChannels(NBN_Connection *connection, double time) { NBN_LogTrace("Flushing the send queue"); @@ -2568,8 +2057,8 @@ int NBN_Connection_FlushSendQueue(NBN_Connection *connection, double time) int NBN_Connection_InitChannel(NBN_Connection *connection, NBN_Channel *channel) { channel->connection = connection; - channel->read_chunk_buffer = (uint8_t*)NBN_Allocator(NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE); - channel->write_chunk_buffer = (uint8_t*)NBN_Allocator(NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE); + // channel->read_chunk_buffer = (uint8_t*)NBN_Allocator(NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE); + // channel->write_chunk_buffer = (uint8_t*)NBN_Allocator(NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE); channel->read_chunk_buffer_size = NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE; channel->write_chunk_buffer_size = NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE; channel->next_outgoing_chunked_message = 0; @@ -2585,9 +2074,10 @@ int NBN_Connection_InitChannel(NBN_Connection *connection, NBN_Channel *channel) channel->outgoing_message_slot_buffer[i].free = true; } - for (int i = 0; i < NBN_CHANNEL_CHUNKS_BUFFER_SIZE; i++) - channel->recv_chunk_buffer[i] = NULL; - + // TODO + /*for (int i = 0; i < NBN_CHANNEL_CHUNKS_BUFFER_SIZE; i++)*/ + /* channel->recv_chunk_buffer[i] = NULL;*/ + /**/ NBN_LogDebug("Initialized channel %d for connection %d", channel->id, connection->id); return 0; } @@ -2789,34 +2279,50 @@ static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, } } -static int Connection_ReadNextMessageFromBuffer(NBN_Connection *connection, uint8_t *buffer, NBN_Message *message) +static int Connection_ReadNextMessageFromBuffer(NBN_Reader *reader, NBN_Message *message) { - // TODO: endianess + if (NBN_Reader_ReadUInt16(reader, &message->header.id) < 0) + { + NBN_LogError("Failed to read message id"); - message->header.id = *((uint16_t *)buffer); - buffer += 2; - message->header.length = *((uint16_t *)buffer); - buffer += 2; - message->header.type = *buffer; - buffer++; - message->header.channel_id = *buffer; - buffer++; + return NBN_ERROR; + } - NBN_Channel *channel = connection->channels[message->header.channel_id]; + if (NBN_Reader_ReadUInt16(reader, &message->header.length) < 0) + { + NBN_LogError("Failed to read message length"); - if (channel == NULL) + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt8(reader, &message->header.type) < 0) { - NBN_LogError("Channel %d does not exist", message->header.channel_id); + NBN_LogError("Failed to read message type"); return NBN_ERROR; } - // TODO: user defined message allocation strategy - message->data = NBN_Allocator(message->header.length); + if (NBN_Reader_ReadUInt8(reader, &message->header.channel_id) < 0) + { + NBN_LogError("Failed to read message channel"); - memcpy(message->data, buffer, message->header.length); + return NBN_ERROR; + } + + if (message->header.length > 0) + { + // TODO: user defined message allocation strategy + message->data = NBN_Allocator(message->header.length); + + if (NBN_Reader_ReadBytes(reader, message->data, message->header.length) < 0) + { + NBN_LogError("Failed to read message data"); + + return NBN_ERROR; + } + } - return message->header.length; + return 0; } static void Connection_RecycleMessage(NBN_Channel *channel, NBN_Message *message) @@ -2930,63 +2436,62 @@ void NBN_Channel_Destroy(NBN_Channel *channel) } } - NBN_Deallocator(channel->read_chunk_buffer); - NBN_Deallocator(channel->write_chunk_buffer); + /*NBN_Deallocator(channel->read_chunk_buffer);*/ + /*NBN_Deallocator(channel->write_chunk_buffer);*/ NBN_Deallocator(channel); } -bool NBN_Channel_AddChunk(NBN_Channel *channel, NBN_Message *chunk_msg) -{ - assert(chunk_msg->header.type == NBN_MESSAGE_CHUNK_TYPE); - - NBN_MessageChunk *chunk = (NBN_MessageChunk *)chunk_msg->data; - - NBN_LogTrace("Add chunk %d to channel %d (current chunk count: %d, last recved chunk id: %d)", - chunk->id, channel->id, channel->chunk_count, channel->last_received_chunk_id); - - if (chunk->id == channel->last_received_chunk_id + 1) - { - assert(channel->recv_chunk_buffer[chunk->id] == NULL); - - channel->recv_chunk_buffer[chunk->id] = chunk; - channel->last_received_chunk_id++; - channel->chunk_count++; - - NBN_LogTrace("Chunk added (%d/%d)", channel->chunk_count, chunk->total); - - if (channel->chunk_count == chunk->total) - { - channel->last_received_chunk_id = -1; - - return true; - } - - return false; - } - else - { - NBN_LogTrace("Chunk ignored"); - } - - /* Clear the chunks buffer */ - for (unsigned int i = 0; i < channel->chunk_count; i++) - { - assert(channel->recv_chunk_buffer[i] != NULL); - - NBN_MessageChunk_Destroy(channel->recv_chunk_buffer[i]); - - channel->recv_chunk_buffer[i] = NULL; - } - - channel->chunk_count = 0; - channel->last_received_chunk_id = -1; - - if (chunk->id == 0) - return NBN_Channel_AddChunk(channel, chunk_msg); - - return false; -} +/*bool NBN_Channel_AddChunk(NBN_Channel *channel, NBN_Message *chunk_msg)*/ +/*{*/ +/* assert(chunk_msg->header.type == NBN_MESSAGE_CHUNK_TYPE);*/ +/**/ +/* NBN_MessageChunk *chunk = (NBN_MessageChunk *)chunk_msg->data;*/ +/**/ +/* NBN_LogTrace("Add chunk %d to channel %d (current chunk count: %d, last recved chunk id: %d)",*/ +/* chunk->id, channel->id, channel->chunk_count, channel->last_received_chunk_id);*/ +/**/ +/* if (chunk->id == channel->last_received_chunk_id + 1)*/ +/* {*/ +/* assert(channel->recv_chunk_buffer[chunk->id] == NULL);*/ +/**/ +/* channel->recv_chunk_buffer[chunk->id] = chunk;*/ +/* channel->last_received_chunk_id++;*/ +/* channel->chunk_count++;*/ +/**/ +/* NBN_LogTrace("Chunk added (%d/%d)", channel->chunk_count, chunk->total);*/ +/**/ +/* if (channel->chunk_count == chunk->total)*/ +/* {*/ +/* channel->last_received_chunk_id = -1;*/ +/**/ +/* return true;*/ +/* }*/ +/**/ +/* return false;*/ +/* }*/ +/* else*/ +/* {*/ +/* NBN_LogTrace("Chunk ignored");*/ +/* }*/ +/**/ +/* for (unsigned int i = 0; i < channel->chunk_count; i++)*/ +/* {*/ +/* assert(channel->recv_chunk_buffer[i] != NULL);*/ +/**/ +/* NBN_MessageChunk_Destroy(channel->recv_chunk_buffer[i]);*/ +/**/ +/* channel->recv_chunk_buffer[i] = NULL;*/ +/* }*/ +/**/ +/* channel->chunk_count = 0;*/ +/* channel->last_received_chunk_id = -1;*/ +/**/ +/* if (chunk->id == 0)*/ +/* return NBN_Channel_AddChunk(channel, chunk_msg);*/ +/**/ +/* return false;*/ +/*}*/ int NBN_Channel_ReconstructMessageFromChunks(NBN_Channel *channel, NBN_Connection *connection, NBN_Message *message) { @@ -3025,19 +2530,19 @@ int NBN_Channel_ReconstructMessageFromChunks(NBN_Channel *channel, NBN_Connectio return 0; } -void NBN_Channel_ResizeWriteChunkBuffer(NBN_Channel *channel, unsigned int size) -{ - channel->write_chunk_buffer = (uint8_t *)NBN_Reallocator(channel->write_chunk_buffer, size); - - channel->write_chunk_buffer_size = size; -} - -void NBN_Channel_ResizeReadChunkBuffer(NBN_Channel *channel, unsigned int size) -{ - channel->read_chunk_buffer = (uint8_t *)NBN_Reallocator(channel->read_chunk_buffer, size); - - channel->read_chunk_buffer_size = size; -} +/*void NBN_Channel_ResizeWriteChunkBuffer(NBN_Channel *channel, unsigned int size)*/ +/*{*/ +/* channel->write_chunk_buffer = (uint8_t *)NBN_Reallocator(channel->write_chunk_buffer, size);*/ +/**/ +/* channel->write_chunk_buffer_size = size;*/ +/*}*/ +/**/ +/*void NBN_Channel_ResizeReadChunkBuffer(NBN_Channel *channel, unsigned int size)*/ +/*{*/ +/* channel->read_chunk_buffer = (uint8_t *)NBN_Reallocator(channel->read_chunk_buffer, size);*/ +/**/ +/* channel->read_chunk_buffer_size = size;*/ +/*}*/ void NBN_Channel_UpdateMessageLastSendTime(NBN_Channel *channel, NBN_Message *message, double time) { @@ -3398,9 +2903,6 @@ bool NBN_EventQueue_IsEmpty(NBN_EventQueue *event_queue) static void Endpoint_Init(NBN_Endpoint *, bool); static void Endpoint_Deinit(NBN_Endpoint *); -static void Endpoint_RegisterMessageBuilder(NBN_Endpoint *, NBN_MessageBuilder, uint8_t); -static void Endpoint_RegisterMessageDestructor(NBN_Endpoint *, NBN_MessageDestructor, uint8_t); -static void Endpoint_RegisterMessageSerializer(NBN_Endpoint *, NBN_MessageSerializer, uint8_t); static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, uint32_t, uint32_t, int, void *); static uint32_t Endpoint_BuildProtocolId(const char *); static void Endpoint_RegisterChannel(NBN_Endpoint *, uint8_t, NBN_ChannelBuilder, NBN_ChannelDestructor); @@ -3428,7 +2930,7 @@ static void Endpoint_Init(NBN_Endpoint *endpoint, bool is_server) Endpoint_RegisterChannel(endpoint, NBN_CHANNEL_RESERVED_RELIABLE, (NBN_ChannelBuilder)NBN_ReliableOrderedChannel_Create, (NBN_ChannelDestructor)NBN_Channel_Destroy); Endpoint_RegisterChannel(endpoint, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, (NBN_ChannelBuilder)NBN_ReliableOrderedChannel_Create, (NBN_ChannelDestructor)NBN_Channel_Destroy); - /* Register general purposes reliable channels */ + // TODO: don't need to create that many channels by default for (int i = 0; i < NBN_MAX_CHANNELS - NBN_LIBRARY_RESERVED_CHANNELS; i++) { Endpoint_RegisterChannel(endpoint, i, (NBN_ChannelBuilder)NBN_ReliableOrderedChannel_Create, (NBN_ChannelDestructor)NBN_Channel_Destroy); @@ -3531,9 +3033,10 @@ static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *pa return 0; } -static NBN_Message *Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, NBN_Channel *channel, uint8_t type, uint8_t *data, uint16_t length) +static NBN_Message *Endpoint_CreateOutgoingMessage( + NBN_Endpoint *endpoint, NBN_Channel *channel, uint8_t type, uint8_t *data, uint16_t length) { - assert(channel); + NBN_Assert(channel); NBN_Message *message = &channel->outgoing_message_pool[channel->next_outgoing_message_pool_slot]; @@ -3556,12 +3059,12 @@ static NBN_Message *Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, NBN_C static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Message *message) { - assert(!connection->is_closed || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); - assert(!connection->is_stale); + NBN_Assert(!connection->is_closed || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); + NBN_Assert(!connection->is_stale); NBN_Channel *channel = connection->channels[message->header.channel_id]; - assert(channel); + NBN_Assert(channel); if (message->header.length > NBN_PACKET_MAX_DATA_SIZE) { @@ -3601,7 +3104,7 @@ static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connectio assert(false); } else - { + { assert(message->ref_count == 0); message->ref_count = 1; @@ -3742,15 +3245,6 @@ int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data) #pragma endregion /* Network driver */ -#pragma region Library reserved messages - -static void SerializeClientClosedConnectionMessage(NBN_Stream *stream, int code) -{ - NBN_SerializeUInt(stream, code, SHRT_MIN, SHRT_MAX); -} - -#pragma endregion /* Library reserved messages */ - #pragma region NBN_GameClient NBN_GameClient nbn_game_client; @@ -3772,12 +3266,6 @@ int NBN_GameClient_StartEx(const char *protocol_name, const char *host, uint16_t NBN_Abort(); } - if (data && length > NBN_CONNECTION_DATA_MAX_SIZE) - { - NBN_LogError("Connection data cannot exceed %d bytes", NBN_CONNECTION_DATA_MAX_SIZE); - NBN_Abort(); - } - Endpoint_Init(&nbn_game_client.endpoint, false); nbn_game_client.server_connection = NULL; @@ -3797,7 +3285,28 @@ int NBN_GameClient_StartEx(const char *protocol_name, const char *host, uint16_t } } - if (NBN_GameClient_SendMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, data, length) < 0) + uint8_t *msg = NULL; + unsigned int msg_length = 0; + + if (data) + { + NBN_Assert(length > 0 && length <= NBN_CONNECTION_DATA_MAX_SIZE); + + msg_length = length + 4; + msg = NBN_Allocator(msg_length); // TODO: pooling + + NBN_Writer writer; + + NBN_Writer_Init(&writer, msg, msg_length); + NBN_Writer_WriteUInt32(&writer, msg_length); + NBN_Writer_WriteBytes(&writer, data, length); + } + + if (NBN_GameClient_SendMessage( + NBN_CONNECTION_REQUEST_MESSAGE_TYPE, + NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, + msg, + msg_length) < 0) return NBN_ERROR; NBN_LogInfo("Started"); @@ -3937,7 +3446,7 @@ int NBN_GameClient_Poll(void) int NBN_GameClient_SendPackets(void) { - return NBN_Connection_FlushSendQueue(nbn_game_client.server_connection, nbn_game_client.endpoint.time); + return NBN_Connection_FlushChannels(nbn_game_client.server_connection, nbn_game_client.endpoint.time); } int NBN_GameClient_SendMessage(uint8_t type, uint8_t channel_id, uint8_t *data, uint16_t length) @@ -3949,12 +3458,7 @@ int NBN_GameClient_SendMessage(uint8_t type, uint8_t channel_id, uint8_t *data, data, length); - if (message == NULL) - { - NBN_LogError("Failed to create outgoing message"); - - return NBN_ERROR; - } + NBN_Assert(message); if (Endpoint_EnqueueOutgoingMessage(&nbn_game_client.endpoint, nbn_game_client.server_connection, message) < 0) { @@ -4036,27 +3540,27 @@ static int GameClient_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) { - NBN_Channel *channel = server_connection->channels[message->header.channel_id]; - - if (!NBN_Channel_AddChunk(channel, message)) - return 0; - - NBN_Message complete_message; - - if (NBN_Channel_ReconstructMessageFromChunks(channel, server_connection, &complete_message) < 0) - { - NBN_LogError("Failed to reconstruct message from chunks"); - - return NBN_ERROR; - } - - NBN_MessageInfo msg_info = {complete_message.header.type, complete_message.header.channel_id, complete_message.data, 0}; - - ev.data.message_info = msg_info; + /*NBN_Channel *channel = server_connection->channels[message->header.channel_id];*/ + /**/ + /*if (!NBN_Channel_AddChunk(channel, message))*/ + /* return 0;*/ + /**/ + /*NBN_Message complete_message;*/ + /**/ + /*if (NBN_Channel_ReconstructMessageFromChunks(channel, server_connection, &complete_message) < 0)*/ + /*{*/ + /* NBN_LogError("Failed to reconstruct message from chunks");*/ + /**/ + /* return NBN_ERROR;*/ + /*}*/ + /**/ + /*NBN_MessageInfo msg_info = {complete_message.header.type, complete_message.header.channel_id, complete_message.data, 0};*/ + /**/ + /*ev.data.message_info = msg_info;*/ } else { - NBN_MessageInfo msg_info = {message->header.type, message->header.channel_id, message->data, 0}; + NBN_MessageInfo msg_info = {message->header.type, message->header.channel_id, message->data, message->header.length, 0}; ev.data.message_info = msg_info; } @@ -4088,18 +3592,48 @@ static int GameClient_HandleMessageReceivedEvent(void) if (message_info.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE) { nbn_game_client.is_connected = false; - nbn_game_client.closed_code = ((NBN_ClientClosedMessage *)message_info.data)->code; + + NBN_Reader reader; + + NBN_Reader_Init(&reader, message_info.data, message_info.length); + + if (NBN_Reader_ReadInt32(&reader, &nbn_game_client.closed_code) < 0) + { + NBN_LogError("Failed to read code from client closed message"); + + return NBN_ERROR; + } + ret = NBN_DISCONNECTED; } else if (message_info.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE) { - nbn_game_client.is_connected = true; - ret = NBN_CONNECTED; + NBN_Reader reader; + + NBN_Reader_Init(&reader, message_info.data, message_info.length); + + unsigned int length; - NBN_ClientAcceptedMessage *accepted_msg = (NBN_ClientAcceptedMessage *)message_info.data; + if (NBN_Reader_ReadUInt32(&reader, &length) < 0) + { + return NBN_ERROR; + } + + if (length > NBN_SERVER_DATA_MAX_SIZE) + { + NBN_LogError("Received an invalid client accepted message"); + + return NBN_ERROR; + } - memcpy(nbn_game_client.server_data, accepted_msg->data, accepted_msg->length); - nbn_game_client.server_data_len = accepted_msg->length; + if (NBN_Reader_ReadBytes(&reader, nbn_game_client.server_data, length) < 0) + { + return NBN_ERROR; + } + + nbn_game_client.server_data_len = length; + nbn_game_client.is_connected = true; + ret = NBN_CONNECTED; } else { @@ -4307,7 +3841,7 @@ int NBN_GameServer_SendPackets(void) assert(!(client->is_closed && client->is_stale)); - if (!client->is_stale && NBN_Connection_FlushSendQueue(client, nbn_game_server.endpoint.time) < 0) + if (!client->is_stale && NBN_Connection_FlushChannels(client, nbn_game_server.endpoint.time) < 0) return NBN_ERROR; nbn_game_server.stats.upload_bandwidth += client->stats.upload_bandwidth; @@ -4355,7 +3889,7 @@ int NBN_GameServer_CloseClient(NBN_ConnectionHandle connection_handle) return GameServer_CloseClientWithCode(client, -1, false); } -int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, uint8_t channel_id, void *msg_data) +int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, uint8_t channel_id, uint8_t *data, uint16_t length) { NBN_Connection *client = NBN_ConnectionTable_Get(nbn_game_server.clients_table, connection_handle); @@ -4365,17 +3899,17 @@ int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle, uint8_t return 0; } - return GameServer_SendMessageTo(client, msg_type, channel_id, msg_data); + return GameServer_SendMessageTo(client, msg_type, channel_id, data, length); } -int NBN_GameServer_SendUnreliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, void *msg_data) +int NBN_GameServer_SendUnreliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, uint8_t *data, uint16_t length) { - return NBN_GameServer_SendMessageTo(connection_handle, msg_type, NBN_CHANNEL_RESERVED_UNRELIABLE, msg_data); + return NBN_GameServer_SendMessageTo(connection_handle, msg_type, NBN_CHANNEL_RESERVED_UNRELIABLE, data, length); } -int NBN_GameServer_SendReliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, void *msg_data) +int NBN_GameServer_SendReliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, uint8_t *data, uint16_t length) { - return NBN_GameServer_SendMessageTo(connection_handle, msg_type, NBN_CHANNEL_RESERVED_RELIABLE, msg_data); + return NBN_GameServer_SendMessageTo(connection_handle, msg_type, NBN_CHANNEL_RESERVED_RELIABLE, data, length); } int NBN_GameServer_AcceptIncomingConnection(void) @@ -4385,26 +3919,31 @@ int NBN_GameServer_AcceptIncomingConnection(void) int NBN_GameServer_AcceptIncomingConnectionWithData(uint8_t *data, unsigned int length) { - assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); - assert(nbn_game_server.last_event.data.connection != NULL); + NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); + NBN_Assert(nbn_game_server.last_event.data.connection != NULL); + NBN_Assert(data != NULL); + + NBN_Connection *client = nbn_game_server.last_event.data.connection; + uint8_t *msg = NULL; + unsigned int msg_length = 0; - if (length > NBN_SERVER_DATA_MAX_SIZE) + if (data) { - NBN_LogError("Data length cannot exceed %d bytes", NBN_SERVER_DATA_MAX_SIZE); - return NBN_ERROR; - } + NBN_Assert(length > 0); + NBN_Assert(length <= NBN_SERVER_DATA_MAX_SIZE); - NBN_Connection *client = nbn_game_server.last_event.data.connection; - NBN_ClientAcceptedMessage *msg = NBN_ClientAcceptedMessage_Create(); + msg = NBN_Allocator(length); // TODO: pooling - assert(msg != NULL); + NBN_Writer writer; - msg->length = length; + NBN_Writer_Init(&writer, msg, length); + NBN_Writer_WriteUInt32(&writer, length); + NBN_Writer_WriteBytes(&writer, data, length); - if (data) - memcpy(msg->data, data, length); + msg_length = writer.position; + } - if (GameServer_SendMessageTo(client, NBN_CLIENT_ACCEPTED_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, msg) < 0) + if (GameServer_SendMessageTo(client, NBN_CLIENT_ACCEPTED_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, msg, msg_length) < 0) return NBN_ERROR; client->is_accepted = true; @@ -4480,7 +4019,7 @@ void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, static int GameServer_SendMessageTo(NBN_Connection *client, uint8_t type, uint8_t channel_id, uint8_t *data, uint16_t length) { NBN_Channel *channel = client->channels[channel_id]; - NBN_OutgoingMessage *message = Endpoint_CreateOutgoingMessage(&nbn_game_server.endpoint, channel, type, data, length); + NBN_Message *message = Endpoint_CreateOutgoingMessage(&nbn_game_server.endpoint, channel, type, data, length); if (message == NULL) { @@ -4490,14 +4029,14 @@ static int GameServer_SendMessageTo(NBN_Connection *client, uint8_t type, uint8_ } /* The only message type we can send to an unaccepted client is a NBN_ClientAcceptedMessage message */ - assert(client->is_accepted || outgoing_msg->type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); + NBN_Assert(client->is_accepted || message->header.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); if (Endpoint_EnqueueOutgoingMessage(&nbn_game_server.endpoint, client, message) < 0) { NBN_LogError("Failed to create outgoing message for client %d", client->id); /* Do not close the client if we failed to send the close client message to avoid infinite loops */ - if (outgoing_msg->type != NBN_CLIENT_CLOSED_MESSAGE_TYPE) + if (message->header.type != NBN_CLIENT_CLOSED_MESSAGE_TYPE) { GameServer_CloseClientWithCode(client, -1, false); @@ -4558,14 +4097,14 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool { NBN_LogDebug("Send close message for client %d (code: %d)", client->id, code); - NBN_MeasureStream m_stream; + unsigned int msg_length = 4; + uint8_t *msg = NBN_Allocator(msg_length); // TODO: pooling - NBN_MeasureStream_Init(&m_stream); - SerializeClientClosedConnectionMessage(&m_stream, 0); - unsigned int length = NBN_MeasureStream_CountBytes(&m_stream); - uint8_t *msg = NBN_Allocator(length); // TODO: pooling + NBN_Writer writer; - GameServer_SendMessageTo(client, NBN_CLIENT_CLOSED_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, msg, length); + NBN_Writer_Init(&writer, msg, msg_length); + NBN_Writer_WriteInt32(&writer, code); + GameServer_SendMessageTo(client, NBN_CLIENT_CLOSED_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, msg, msg_length); } return 0; @@ -4611,23 +4150,23 @@ static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) { - NBN_Channel *channel = client->channels[message->header.channel_id]; - - if (!NBN_Channel_AddChunk(channel, message)) - return 0; - - NBN_Message complete_message; - - if (NBN_Channel_ReconstructMessageFromChunks(channel, client, &complete_message) < 0) - { - NBN_LogError("Failed to reconstruct message from chunks"); - - return NBN_ERROR; - } - - NBN_MessageInfo msg_info = {complete_message.header.type, complete_message.header.channel_id, complete_message.data, client->id}; - - ev.data.message_info = msg_info; + /*NBN_Channel *channel = client->channels[message->header.channel_id];*/ + /**/ + /*if (!NBN_Channel_AddChunk(channel, message))*/ + /* return 0;*/ + /**/ + /*NBN_Message complete_message;*/ + /**/ + /*if (NBN_Channel_ReconstructMessageFromChunks(channel, client, &complete_message) < 0)*/ + /*{*/ + /* NBN_LogError("Failed to reconstruct message from chunks");*/ + /**/ + /* return NBN_ERROR;*/ + /*}*/ + /**/ + /*NBN_MessageInfo msg_info = {complete_message.header.type, complete_message.header.channel_id, complete_message.data, client->id};*/ + /**/ + /*ev.data.message_info = msg_info;*/ } else { @@ -4745,12 +4284,11 @@ static int GameServer_HandleMessageReceivedEvent(void) if (sender == NULL) { - // skip events related to unknown clients (clients that might have been removed) NBN_LogTrace("Received message from unknown client (ID: %d)", message_info.sender); + return NBN_SKIP_EVENT; } - // skip all events related to a closed or stale connection if (sender->is_closed || sender->is_stale) return NBN_SKIP_EVENT; @@ -4771,27 +4309,56 @@ static int GameServer_HandleMessageReceivedEvent(void) return NBN_CLIENT_DISCONNECTED; } - int ret = NBN_CLIENT_MESSAGE_RECEIVED; + if (message_info.type != NBN_CONNECTION_REQUEST_MESSAGE_TYPE) + { + return NBN_CLIENT_MESSAGE_RECEIVED; + } + + // at this point we know it's a connection request + + nbn_game_server.last_connection_data_len = 0; + memset(nbn_game_server.last_connection_data, 0, sizeof(nbn_game_server.last_connection_data)); - if (message_info.type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE) + if (message_info.length > 0) { - ret = NBN_NO_EVENT; + unsigned int data_length; + NBN_Reader reader; + + NBN_Reader_Init(&reader, message_info.data, message_info.length); + + if (NBN_Reader_ReadUInt32(&reader, &data_length) < 0) + { + NBN_LogError("Failed to read client data length"); - NBN_ConnectionRequestMessage *msg = (NBN_ConnectionRequestMessage *)message_info.data; + return NBN_ERROR; + } - nbn_game_server.last_connection_data_len = msg->length; - memcpy(nbn_game_server.last_connection_data, msg->data, msg->length); + if (data_length > 0 && data_length <= NBN_CONNECTION_DATA_MAX_SIZE) + { + NBN_LogError("Invalid client data length"); - NBN_Event e; + return NBN_ERROR; + } - e.type = NBN_NEW_CONNECTION; - e.data.connection = sender; + if (NBN_Reader_ReadBytes(&reader, nbn_game_server.last_connection_data, data_length) < 0) + { + NBN_LogError("Failed to read client data"); - if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, e)) return NBN_ERROR; + } + + nbn_game_server.last_connection_data_len = data_length; } - return ret; + NBN_Event e; + + e.type = NBN_NEW_CONNECTION; + e.data.connection = sender; + + if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, e)) + return NBN_ERROR; + + return NBN_NO_EVENT; } #pragma endregion /* NBN_GameServer */ diff --git a/net_drivers/udp.h b/net_drivers/udp.h index 95059ec..e79040f 100644 --- a/net_drivers/udp.h +++ b/net_drivers/udp.h @@ -474,34 +474,34 @@ static void NBN_UDP_ServStop(void) static int NBN_UDP_ServRecvPackets(void) { - uint8_t buffer[NBN_PACKET_MAX_SIZE] = {0}; + NBN_Packet packet; SOCKADDR_IN src_addr; socklen_t src_addr_len = sizeof(src_addr); NBN_IPAddress ip_address; while (true) { - int bytes = recvfrom(nbn_udp_sock, (char *)buffer, sizeof(buffer), 0, (SOCKADDR *)&src_addr, &src_addr_len); + int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, &src_addr_len); if (bytes <= 0) break; + if (bytes <= NBN_PACKET_HEADER_SIZE) + continue; + + NBN_Packet packet; + + if (NBN_Packet_InitRead(&packet, conn, bytes) < 0) + continue; /* not a valid packet */ + ip_address.host = ntohl(src_addr.sin_addr.s_addr); ip_address.port = ntohs(src_addr.sin_port); - if (NBN_Packet_ReadProtocolId(buffer, bytes) != nbn_udp_serv.protocol_id) - continue; /* not matching the protocol of the receiver */ - NBN_Connection *conn = FindOrCreateClientConnectionByAddress(ip_address); if (conn == NULL) continue; // skip the connection - - NBN_Packet packet; - - if (NBN_Packet_InitRead(&packet, conn, buffer, bytes) < 0) - continue; /* not a valid packet */ - + if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet) < 0) { NBN_LogError("Failed to raise game server event"); @@ -633,17 +633,20 @@ static void NBN_UDP_CliStop(void) static int NBN_UDP_CliRecvPackets(void) { NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)nbn_udp_cli.server_conn->driver_data; - uint8_t buffer[NBN_PACKET_MAX_SIZE] = {0}; + NBN_Packet packet = {0}; SOCKADDR_IN src_addr; socklen_t src_addr_len = sizeof(src_addr); while (true) { - int bytes = recvfrom(nbn_udp_sock, (char *)buffer, sizeof(buffer), 0, (SOCKADDR *)&src_addr, &src_addr_len); + int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, &src_addr_len); if (bytes <= 0) break; + if (bytes < NBN_PACKET_HEADER_SIZE) + continue; + NBN_IPAddress ip_address; ip_address.host = ntohl(src_addr.sin_addr.s_addr); @@ -653,12 +656,7 @@ static int NBN_UDP_CliRecvPackets(void) if (ip_address.host != udp_conn->address.host || ip_address.port != udp_conn->address.port) continue; - if (NBN_Packet_ReadProtocolId(buffer, bytes) != nbn_udp_cli.protocol_id) - continue; /* not matching the protocol of the receiver */ - - NBN_Packet packet; - - if (NBN_Packet_InitRead(&packet, nbn_udp_cli.server_conn, buffer, bytes) < 0) + if (NBN_Packet_InitRead(&packet, nbn_udp_cli.server_conn, bytes) < 0) continue; /* not a valid packet */ NBN_Driver_RaiseEvent(NBN_DRIVER_CLI_PACKET_RECEIVED, &packet); From eda66ce38ebb6318cd68629fe249687333c40b88 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sat, 19 Oct 2024 13:25:43 +0200 Subject: [PATCH 03/85] wip --- examples/echo/client.c | 43 ++++++++++----------- examples/echo/server.c | 35 ++++++++---------- examples/echo/shared.c | 18 --------- examples/echo/shared.h | 10 ----- nbnet.h | 84 +++++++++++++++++++++++++----------------- net_drivers/udp.h | 25 +++++++------ 6 files changed, 97 insertions(+), 118 deletions(-) diff --git a/examples/echo/client.c b/examples/echo/client.c index fd082f1..1c33f10 100644 --- a/examples/echo/client.c +++ b/examples/echo/client.c @@ -63,11 +63,12 @@ void OnMessageReceived(void) assert(msg_info.type == ECHO_MESSAGE_TYPE); // Retrieve the received message - EchoMessage *msg = (EchoMessage *)msg_info.data; - Log(LOG_INFO, "Received echo: %s (%d bytes)", msg->data, msg->length); - - EchoMessage_Destroy(msg); // Destroy the received echo message + /*EchoMessage *msg = (EchoMessage *)msg_info.data;*/ + /**/ + /*Log(LOG_INFO, "Received echo: %s (%d bytes)", msg->data, msg->length);*/ + /**/ + /*EchoMessage_Destroy(msg); // Destroy the received echo message*/ } int SendEchoMessage(const char *msg) @@ -75,19 +76,20 @@ int SendEchoMessage(const char *msg) unsigned int length = strlen(msg); // Compute message length // Create the echo message - EchoMessage *echo = EchoMessage_Create(); - - if (echo == NULL) - return -1; - - // Fill echo message with message the length and the message - echo->length = length + 1; - memcpy(echo->data, msg, length + 1); - - // Reliably send it to the server - if (NBN_GameClient_SendReliableMessage(ECHO_MESSAGE_TYPE, echo) < 0) - return -1; - + + /*EchoMessage *echo = EchoMessage_Create();*/ + /**/ + /*if (echo == NULL)*/ + /* return -1;*/ + /**/ + /*// Fill echo message with message the length and the message*/ + /*echo->length = length + 1;*/ + /*memcpy(echo->data, msg, length + 1);*/ + /**/ + /*// Reliably send it to the server*/ + /*if (NBN_GameClient_SendReliableMessage(ECHO_MESSAGE_TYPE, echo) < 0)*/ + /* return -1;*/ + /**/ return 0; } @@ -172,13 +174,6 @@ int main(int argc, char *argv[]) #endif } - // Registering messages, have to be done after NBN_GameClient_StartEx - // Messages need to be registered on both client and server side - NBN_GameClient_RegisterMessage(ECHO_MESSAGE_TYPE, - (NBN_MessageBuilder)EchoMessage_Create, - (NBN_MessageDestructor)EchoMessage_Destroy, - (NBN_MessageSerializer)EchoMessage_Serialize); - // Number of seconds between client ticks double dt = 1.0 / ECHO_TICK_RATE; diff --git a/examples/echo/server.c b/examples/echo/server.c index 1a038ec..5796c97 100644 --- a/examples/echo/server.c +++ b/examples/echo/server.c @@ -41,21 +41,22 @@ static int EchoReceivedMessage(void) assert(msg_info.type == ECHO_MESSAGE_TYPE); // Retrieve the received message - EchoMessage *msg = (EchoMessage *)msg_info.data; - // Create an echo message - EchoMessage *echo = EchoMessage_Create(); - - // Fill it with the received message data and length - memcpy(echo->data, msg->data, msg->length); - echo->length = msg->length; - - // Reliably send it to the client - // If the send fails the client will be disconnected and a NBN_CLIENT_DISCONNECTED event - // will be received (see event polling in main) - NBN_GameServer_SendReliableMessageTo(client, ECHO_MESSAGE_TYPE, echo); - - EchoMessage_Destroy(msg); // Destroy the received echo message + /*EchoMessage *msg = (EchoMessage *)msg_info.data;*/ + /**/ + /*// Create an echo message*/ + /*EchoMessage *echo = EchoMessage_Create();*/ + /**/ + /*// Fill it with the received message data and length*/ + /*memcpy(echo->data, msg->data, msg->length);*/ + /*echo->length = msg->length;*/ + /**/ + /*// Reliably send it to the client*/ + /*// If the send fails the client will be disconnected and a NBN_CLIENT_DISCONNECTED event*/ + /*// will be received (see event polling in main)*/ + /*NBN_GameServer_SendReliableMessageTo(client, ECHO_MESSAGE_TYPE, echo);*/ + /**/ + /*EchoMessage_Destroy(msg); // Destroy the received echo message*/ return 0; } @@ -125,12 +126,6 @@ int main(int argc, const char **argv) #endif } - // Registering messages, have to be done after NBN_GameServer_StartEx - NBN_GameServer_RegisterMessage(ECHO_MESSAGE_TYPE, - (NBN_MessageBuilder)EchoMessage_Create, - (NBN_MessageDestructor)EchoMessage_Destroy, - (NBN_MessageSerializer)EchoMessage_Serialize); - // Number of seconds between server ticks double dt = 1.0 / ECHO_TICK_RATE; diff --git a/examples/echo/shared.c b/examples/echo/shared.c index d615842..6dd9a0c 100644 --- a/examples/echo/shared.c +++ b/examples/echo/shared.c @@ -37,24 +37,6 @@ #include "shared.h" -EchoMessage *EchoMessage_Create(void) -{ - return (EchoMessage *) malloc(sizeof(EchoMessage)); -} - -void EchoMessage_Destroy(EchoMessage *msg) -{ - free(msg); -} - -int EchoMessage_Serialize(EchoMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->length, 0, ECHO_MESSAGE_LENGTH); - NBN_SerializeBytes(stream, msg->data, msg->length); - - return 0; -} - // Sleep for a given amount of seconds // Used to limit client and server tick rate void EchoSleep(double sec) diff --git a/examples/echo/shared.h b/examples/echo/shared.h index dc54042..59f1720 100644 --- a/examples/echo/shared.h +++ b/examples/echo/shared.h @@ -69,16 +69,6 @@ void Log(int, const char *, ...); #endif // __EMSCRIPTEN__ -typedef struct -{ - unsigned int length; - char data[ECHO_MESSAGE_LENGTH]; -} EchoMessage; - -EchoMessage *EchoMessage_Create(void); -void EchoMessage_Destroy(EchoMessage *); -int EchoMessage_Serialize(EchoMessage *, NBN_Stream *); - void EchoSleep(double); #endif /* ECHO_EXAMPLE_SHARED_H */ diff --git a/nbnet.h b/nbnet.h index bec463f..e3d2604 100644 --- a/nbnet.h +++ b/nbnet.h @@ -312,7 +312,7 @@ typedef struct NBN_Packet void NBN_Packet_InitWrite(NBN_Packet *, uint32_t, uint16_t, uint16_t, uint32_t); int NBN_Packet_WriteMessage(NBN_Packet *, NBN_Message *); int NBN_Packet_Seal(NBN_Packet *, NBN_Connection *); -int NBN_Packet_InitRead(NBN_Packet *, NBN_Connection *, unsigned int); +int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); #pragma endregion /* NBN_Packet */ @@ -483,7 +483,6 @@ typedef enum NBN_ConnectionDebugCallback struct NBN_Connection { uint32_t id; - uint32_t protocol_id; double last_recv_packet_time; /* Used to detect stale connections */ double last_flush_time; /* Last time the send queue was flushed */ double last_read_packets_time; /* Last time packets were read from the network driver */ @@ -523,10 +522,10 @@ struct NBN_ConnectionListNode NBN_ConnectionListNode *prev; }; -NBN_Connection *NBN_Connection_Create(uint32_t, uint32_t, NBN_Endpoint *, NBN_Driver *, void *); +NBN_Connection *NBN_Connection_Create(uint32_t, NBN_Endpoint *, NBN_Driver *, void *); void NBN_Connection_Destroy(NBN_Connection *); int NBN_Connection_ProcessReceivedPacket(NBN_Connection *, NBN_Packet *, double); -int NBN_Connection_FlushChannels(NBN_Connection *, double); +int NBN_Connection_FlushChannels(NBN_Connection *, uint32_t, double); int NBN_Connection_InitChannel(NBN_Connection *, NBN_Channel *); bool NBN_Connection_CheckIfStale(NBN_Connection *, double); @@ -661,8 +660,9 @@ struct NBN_Endpoint NBN_ChannelBuilder channel_builders[NBN_MAX_CHANNELS]; NBN_ChannelDestructor channel_destructors[NBN_MAX_CHANNELS]; NBN_EventQueue event_queue; + uint32_t protocol_id; bool is_server; - double time; /* Current time */ + double time; #ifdef NBN_DEBUG /* Debug callbacks */ @@ -800,7 +800,7 @@ int NBN_GameClient_SendReliableMessage(uint8_t type, uint8_t *data, uint16_t len /** * For drivers only! NOT MEANT TO BE USED BY USER CODE. */ -NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *driver_data, uint32_t protocol_id); +NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *driver_data); /** * Retrieve the info about the last received message. @@ -924,7 +924,7 @@ int NBN_GameServer_SendPackets(void); /** * For drivers only! NOT MEANT TO BE USED BY USER CODE. */ -NBN_Connection *NBN_GameServer_CreateClientConnection(int, void *, uint32_t, uint32_t); +NBN_Connection *NBN_GameServer_CreateClientConnection(int, void *, uint32_t); /** * Close a client's connection without a specific code (default code is -1) @@ -1720,6 +1720,8 @@ void NBN_Packet_InitWrite(NBN_Packet *packet, uint32_t protocol_id, uint16_t seq packet->sender = NULL; packet->size = NBN_PACKET_HEADER_SIZE; packet->sealed = false; + + memset(packet->buffer, 0, sizeof(packet->buffer)); } int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_Message *message) @@ -1785,13 +1787,13 @@ int NBN_Packet_Seal(NBN_Packet *packet, NBN_Connection *connection) return 0; } -int NBN_Packet_InitRead(NBN_Packet *packet, NBN_Connection *sender, unsigned int size) +int NBN_Packet_InitRead(NBN_Packet *packet, uint32_t protocol_id, unsigned int size) { NBN_Assert(size >= NBN_PACKET_HEADER_SIZE); packet->mode = NBN_PACKET_MODE_READ; - packet->sender = sender; packet->size = size; + packet->sender = NULL; // IMPORTANT: must be set by the drivers packet->sealed = false; NBN_Reader reader; @@ -1799,7 +1801,7 @@ int NBN_Packet_InitRead(NBN_Packet *packet, NBN_Connection *sender, unsigned int NBN_Reader_Init(&reader, packet->buffer, NBN_PACKET_HEADER_SIZE); if (NBN_Reader_ReadUInt32(&reader, &packet->header.protocol_id) < 0) return NBN_ERROR; - if (packet->header.protocol_id != sender->protocol_id) return NBN_ERROR; + if (packet->header.protocol_id != protocol_id) return NBN_ERROR; if (NBN_Reader_ReadUInt32(&reader, &packet->header.ack_bits) < 0) return NBN_ERROR; if (NBN_Reader_ReadUInt16(&reader, &packet->header.seq_number) < 0) return NBN_ERROR; @@ -1818,7 +1820,7 @@ static NBN_Message *Endpoint_CreateOutgoingMessage(NBN_Endpoint *, NBN_Channel*, static uint32_t Connection_BuildPacketAckBits(NBN_Connection *); static int Connection_DecodePacketHeader(NBN_Connection *, NBN_Packet *, double); static int Connection_AckPacket(NBN_Connection *, uint16_t, double time); -static void Connection_InitOutgoingPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry **); +static void Connection_InitOutgoingPacket(NBN_Connection *, uint32_t, NBN_Packet *, NBN_PacketEntry **); static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *, uint16_t); static bool Connection_InsertReceivedPacketEntry(NBN_Connection *, uint16_t); static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *, uint16_t); @@ -1831,12 +1833,11 @@ static void Connection_UpdateAveragePacketLoss(NBN_Connection *, uint16_t); static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *, float); static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *, double); -NBN_Connection *NBN_Connection_Create(uint32_t id, uint32_t protocol_id, NBN_Endpoint *endpoint, NBN_Driver *driver, void *driver_data) +NBN_Connection *NBN_Connection_Create(uint32_t id, NBN_Endpoint *endpoint, NBN_Driver *driver, void *driver_data) { NBN_Connection *connection = (NBN_Connection*)MemoryManager_Alloc(NBN_MEM_CONNECTION); connection->id = id; - connection->protocol_id = protocol_id; connection->endpoint = endpoint; connection->last_recv_packet_time = endpoint->time; connection->next_packet_seq_number = 1; @@ -1946,16 +1947,16 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Connection *connection, NBN_Packet return 0; } -int NBN_Connection_FlushChannels(NBN_Connection *connection, double time) +int NBN_Connection_FlushChannels(NBN_Connection *connection, uint32_t protocol_id, double time) { NBN_LogTrace("Flushing the send queue"); - NBN_Packet packet; + NBN_Packet packet = {0}; NBN_PacketEntry *packet_entry; unsigned int sent_packet_count = 0; unsigned int sent_bytes = 0; - Connection_InitOutgoingPacket(connection, &packet, &packet_entry); + Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); for (unsigned int i = 0; i < NBN_MAX_CHANNELS; i++) { @@ -1994,7 +1995,7 @@ int NBN_Connection_FlushChannels(NBN_Connection *connection, double time) sent_packet_count++; sent_bytes += packet.size; - Connection_InitOutgoingPacket(connection, &packet, &packet_entry); + Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); int ret = NBN_Packet_WriteMessage(&packet, message); @@ -2169,11 +2170,11 @@ static int Connection_AckPacket(NBN_Connection *connection, uint16_t ack_packet_ } static void Connection_InitOutgoingPacket( - NBN_Connection *connection, NBN_Packet *outgoing_packet, NBN_PacketEntry **packet_entry) + NBN_Connection *connection, uint32_t protocol_id, NBN_Packet *outgoing_packet, NBN_PacketEntry **packet_entry) { NBN_Packet_InitWrite( outgoing_packet, - connection->protocol_id, + protocol_id, connection->next_packet_seq_number++, connection->last_received_packet_seq_number, Connection_BuildPacketAckBits(connection)); @@ -2901,9 +2902,9 @@ bool NBN_EventQueue_IsEmpty(NBN_EventQueue *event_queue) #pragma region NBN_Endpoint -static void Endpoint_Init(NBN_Endpoint *, bool); +static void Endpoint_Init(NBN_Endpoint *, uint32_t, bool); static void Endpoint_Deinit(NBN_Endpoint *); -static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, uint32_t, uint32_t, int, void *); +static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, uint32_t, int, void *); static uint32_t Endpoint_BuildProtocolId(const char *); static void Endpoint_RegisterChannel(NBN_Endpoint *, uint8_t, NBN_ChannelBuilder, NBN_ChannelDestructor); static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *, NBN_Packet *, NBN_Connection *); @@ -2911,11 +2912,12 @@ static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN // static int Endpoint_SplitMessageIntoChunks(NBN_Message *, NBN_OutgoingMessage *, NBN_Channel *, NBN_MessageSerializer, unsigned int, NBN_MessageChunk **); static void Endpoint_UpdateTime(NBN_Endpoint *); -static void Endpoint_Init(NBN_Endpoint *endpoint, bool is_server) +static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_server) { MemoryManager_Init(); endpoint->is_server = is_server; + endpoint->protocol_id = protocol_id; for (int i = 0; i < NBN_MAX_CHANNELS; i++) { @@ -2959,13 +2961,13 @@ static void Endpoint_Deinit(NBN_Endpoint *endpoint) MemoryManager_Deinit(); } -static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_t id, uint32_t protocol_id, int driver_id, void *driver_data) +static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_t id, int driver_id, void *driver_data) { NBN_Driver *driver = &nbn_drivers[driver_id]; assert(driver->id >= 0); - NBN_Connection *connection = NBN_Connection_Create(id, protocol_id, endpoint, driver, driver_data); + NBN_Connection *connection = NBN_Connection_Create(id, endpoint, driver, driver_data); for (unsigned int chan_id = 0; chan_id < NBN_MAX_CHANNELS; chan_id++) { @@ -3266,7 +3268,9 @@ int NBN_GameClient_StartEx(const char *protocol_name, const char *host, uint16_t NBN_Abort(); } - Endpoint_Init(&nbn_game_client.endpoint, false); + uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); + + Endpoint_Init(&nbn_game_client.endpoint, protocol_id, false); nbn_game_client.server_connection = NULL; nbn_game_client.is_connected = false; @@ -3278,7 +3282,7 @@ int NBN_GameClient_StartEx(const char *protocol_name, const char *host, uint16_t if (driver->id < 0) continue; - if (driver->impl.cli_start(Endpoint_BuildProtocolId(protocol_name), host, port) < 0) + if (driver->impl.cli_start(protocol_id, host, port) < 0) { NBN_LogError("Failed to start driver %s", driver->name); return NBN_ERROR; @@ -3446,7 +3450,10 @@ int NBN_GameClient_Poll(void) int NBN_GameClient_SendPackets(void) { - return NBN_Connection_FlushChannels(nbn_game_client.server_connection, nbn_game_client.endpoint.time); + return NBN_Connection_FlushChannels( + nbn_game_client.server_connection, + nbn_game_client.endpoint.protocol_id, + nbn_game_client.endpoint.time); } int NBN_GameClient_SendMessage(uint8_t type, uint8_t channel_id, uint8_t *data, uint16_t length) @@ -3481,9 +3488,9 @@ int NBN_GameClient_SendReliableMessage(uint8_t type, uint8_t *data, uint16_t len return NBN_GameClient_SendMessage(type, NBN_CHANNEL_RESERVED_RELIABLE, data, length); } -NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *driver_data, uint32_t protocol_id) +NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *driver_data) { - NBN_Connection *server_connection = Endpoint_CreateConnection(&nbn_game_client.endpoint, 0, protocol_id, driver_id, driver_data); + NBN_Connection *server_connection = Endpoint_CreateConnection(&nbn_game_client.endpoint, 0, driver_id, driver_data); #ifdef NBN_DEBUG server_connection->OnMessageAddedToRecvQueue = nbn_game_client.endpoint.OnMessageAddedToRecvQueue; @@ -3687,7 +3694,9 @@ int NBN_GameServer_StartEx(const char *protocol_name, uint16_t port) NBN_Abort(); } - Endpoint_Init(&nbn_game_server.endpoint, true); + uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); + + Endpoint_Init(&nbn_game_server.endpoint, protocol_id, true); if ((nbn_game_server.clients = NBN_ConnectionVector_Create()) == NULL) { @@ -3709,7 +3718,7 @@ int NBN_GameServer_StartEx(const char *protocol_name, uint16_t port) if (driver->id < 0) continue; - if (driver->impl.serv_start(Endpoint_BuildProtocolId(protocol_name), port) < 0) + if (driver->impl.serv_start(protocol_id, port) < 0) { NBN_LogError("Failed to start driver %s", driver->name); return NBN_ERROR; @@ -3841,8 +3850,15 @@ int NBN_GameServer_SendPackets(void) assert(!(client->is_closed && client->is_stale)); - if (!client->is_stale && NBN_Connection_FlushChannels(client, nbn_game_server.endpoint.time) < 0) + if ( + !client->is_stale && + NBN_Connection_FlushChannels( + client, + nbn_game_server.endpoint.protocol_id, + nbn_game_server.endpoint.time) < 0 + ) { return NBN_ERROR; + } nbn_game_server.stats.upload_bandwidth += client->stats.upload_bandwidth; } @@ -3850,11 +3866,11 @@ int NBN_GameServer_SendPackets(void) return 0; } -NBN_Connection *NBN_GameServer_CreateClientConnection(int driver_id, void *driver_data, uint32_t protocol_id, uint32_t conn_id) +NBN_Connection *NBN_GameServer_CreateClientConnection(int driver_id, void *driver_data, uint32_t conn_id) { assert(conn_id > 0); // Connection IDs start at 1 - NBN_Connection *client = Endpoint_CreateConnection(&nbn_game_server.endpoint, conn_id, protocol_id, driver_id, driver_data); + NBN_Connection *client = Endpoint_CreateConnection(&nbn_game_server.endpoint, conn_id, driver_id, driver_data); #ifdef NBN_DEBUG client->OnMessageAddedToRecvQueue = nbn_game_server.endpoint.OnMessageAddedToRecvQueue; diff --git a/net_drivers/udp.h b/net_drivers/udp.h index e79040f..c711555 100644 --- a/net_drivers/udp.h +++ b/net_drivers/udp.h @@ -444,7 +444,7 @@ static char *GetLastErrorMessage(void) typedef struct NBN_UDP_Server { NBN_UDP_HTable *connections; - uint32_t next_conn_id; // nbnet connection ids start at 1 + uint32_t next_conn_id; // nbnet connection ids, starts at 1 uint32_t protocol_id; } NBN_UDP_Server; @@ -474,7 +474,7 @@ static void NBN_UDP_ServStop(void) static int NBN_UDP_ServRecvPackets(void) { - NBN_Packet packet; + NBN_Packet packet = {0}; SOCKADDR_IN src_addr; socklen_t src_addr_len = sizeof(src_addr); NBN_IPAddress ip_address; @@ -489,10 +489,8 @@ static int NBN_UDP_ServRecvPackets(void) if (bytes <= NBN_PACKET_HEADER_SIZE) continue; - NBN_Packet packet; - - if (NBN_Packet_InitRead(&packet, conn, bytes) < 0) - continue; /* not a valid packet */ + if (NBN_Packet_InitRead(&packet, nbn_udp_serv.protocol_id, bytes) < 0) + continue; ip_address.host = ntohl(src_addr.sin_addr.s_addr); ip_address.port = ntohs(src_addr.sin_port); @@ -500,7 +498,9 @@ static int NBN_UDP_ServRecvPackets(void) NBN_Connection *conn = FindOrCreateClientConnectionByAddress(ip_address); if (conn == NULL) - continue; // skip the connection + continue; + + packet.sender = conn; if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet) < 0) { @@ -562,7 +562,7 @@ static NBN_Connection *FindOrCreateClientConnectionByAddress(NBN_IPAddress addre udp_conn->id = nbn_udp_serv.next_conn_id++; udp_conn->address = address; - udp_conn->conn = NBN_GameServer_CreateClientConnection(NBN_UDP_DRIVER_ID, udp_conn, nbn_udp_serv.protocol_id, udp_conn->id); + udp_conn->conn = NBN_GameServer_CreateClientConnection(NBN_UDP_DRIVER_ID, udp_conn, udp_conn->id); NBN_UDP_HTable_Add(nbn_udp_serv.connections, address, udp_conn); @@ -617,7 +617,7 @@ static int NBN_UDP_CliStart(uint32_t protocol_id, const char *host, uint16_t por if (BindSocket(0) < 0) return NBN_ERROR; - nbn_udp_cli.server_conn = NBN_GameClient_CreateServerConnection(NBN_UDP_DRIVER_ID, udp_conn, protocol_id); + nbn_udp_cli.server_conn = NBN_GameClient_CreateServerConnection(NBN_UDP_DRIVER_ID, udp_conn); return 0; } @@ -652,12 +652,13 @@ static int NBN_UDP_CliRecvPackets(void) ip_address.host = ntohl(src_addr.sin_addr.s_addr); ip_address.port = ntohs(src_addr.sin_port); - /* make sure the received packet is from the server */ if (ip_address.host != udp_conn->address.host || ip_address.port != udp_conn->address.port) continue; - if (NBN_Packet_InitRead(&packet, nbn_udp_cli.server_conn, bytes) < 0) - continue; /* not a valid packet */ + if (NBN_Packet_InitRead(&packet, nbn_udp_cli.protocol_id, bytes) < 0) + continue; + + packet.sender = nbn_udp_cli.server_conn; NBN_Driver_RaiseEvent(NBN_DRIVER_CLI_PACKET_RECEIVED, &packet); } From dbb49b41f0de5539c4b69f4cc522bb142c323115 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Fri, 3 Oct 2025 07:29:27 +0200 Subject: [PATCH 04/85] wip --- examples/echo/client.c | 20 ++------- examples/echo/server.c | 2 + nbnet.h | 93 +++++++++++++++++++++++++++--------------- net_drivers/udp.h | 6 +++ 4 files changed, 71 insertions(+), 50 deletions(-) diff --git a/examples/echo/client.c b/examples/echo/client.c index 1c33f10..6bd86f0 100644 --- a/examples/echo/client.c +++ b/examples/echo/client.c @@ -73,24 +73,10 @@ void OnMessageReceived(void) int SendEchoMessage(const char *msg) { - unsigned int length = strlen(msg); // Compute message length + unsigned int length = strlen(msg); + char *data = strdup(msg); - // Create the echo message - - /*EchoMessage *echo = EchoMessage_Create();*/ - /**/ - /*if (echo == NULL)*/ - /* return -1;*/ - /**/ - /*// Fill echo message with message the length and the message*/ - /*echo->length = length + 1;*/ - /*memcpy(echo->data, msg, length + 1);*/ - /**/ - /*// Reliably send it to the server*/ - /*if (NBN_GameClient_SendReliableMessage(ECHO_MESSAGE_TYPE, echo) < 0)*/ - /* return -1;*/ - /**/ - return 0; + return NBN_GameClient_SendReliableMessage(ECHO_MESSAGE_TYPE, (uint8_t *)data, length); } int main(int argc, char *argv[]) diff --git a/examples/echo/server.c b/examples/echo/server.c index 5796c97..48655a2 100644 --- a/examples/echo/server.c +++ b/examples/echo/server.c @@ -42,6 +42,8 @@ static int EchoReceivedMessage(void) // Retrieve the received message + Log(LOG_DEBUG, "RECEIVED"); + /*EchoMessage *msg = (EchoMessage *)msg_info.data;*/ /**/ /*// Create an echo message*/ diff --git a/nbnet.h b/nbnet.h index e3d2604..9a2e7b3 100644 --- a/nbnet.h +++ b/nbnet.h @@ -1672,7 +1672,7 @@ int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value) return NBN_ERROR; } - *value = *((uint32_t *)(reader->buffer + reader->position)); + *value = ntohl(*((uint32_t *)(reader->buffer + reader->position))); reader->position += 4; return 0; @@ -1800,13 +1800,41 @@ int NBN_Packet_InitRead(NBN_Packet *packet, uint32_t protocol_id, unsigned int s NBN_Reader_Init(&reader, packet->buffer, NBN_PACKET_HEADER_SIZE); - if (NBN_Reader_ReadUInt32(&reader, &packet->header.protocol_id) < 0) return NBN_ERROR; - if (packet->header.protocol_id != protocol_id) return NBN_ERROR; + if (NBN_Reader_ReadUInt32(&reader, &packet->header.protocol_id) < 0) + { + NBN_LogDebug("Failed to read packet's protocol id"); + return NBN_ERROR; + } + + if (packet->header.protocol_id != protocol_id) + { + NBN_LogDebug("Packet's protocol id did not match (expected: %d, received: %d)", protocol_id, packet->header.protocol_id); + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt32(&reader, &packet->header.ack_bits) < 0) + { + NBN_LogDebug("Failed to read packet's acked bits"); + return NBN_ERROR; + } - if (NBN_Reader_ReadUInt32(&reader, &packet->header.ack_bits) < 0) return NBN_ERROR; - if (NBN_Reader_ReadUInt16(&reader, &packet->header.seq_number) < 0) return NBN_ERROR; - if (NBN_Reader_ReadUInt16(&reader, &packet->header.ack) < 0) return NBN_ERROR; - if (NBN_Reader_ReadUInt8(&reader, &packet->header.messages_count) < 0) return NBN_ERROR; + if (NBN_Reader_ReadUInt16(&reader, &packet->header.seq_number) < 0) + { + NBN_LogDebug("Failed to read packet's sequence number"); + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt16(&reader, &packet->header.ack) < 0) + { + NBN_LogDebug("Failed to read packet's ack"); + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt8(&reader, &packet->header.messages_count) < 0) + { + NBN_LogDebug("Failed to read packet's message count"); + return NBN_ERROR; + } return 0; } @@ -2246,7 +2274,7 @@ static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, NBN_LogTrace("Send packet %d to connection %d (messages count: %d)", packet->header.seq_number, connection->id, packet->header.messages_count); - assert(packet_entry->messages_count == packet->header.messages_count); + NBN_Assert(packet_entry->messages_count == packet->header.messages_count); if (NBN_Packet_Seal(packet, connection) < 0) { @@ -3425,7 +3453,7 @@ int NBN_GameClient_Poll(void) while ((msg = channel->GetNextRecvedMessage(channel)) != NULL) { - NBN_LogTrace("Got message %d of type %d from the recv queue", msg->header.id, msg->header.type); + NBN_LogTrace("Got message %d of type %d from channel %d", msg->header.id, msg->header.type, channel->id); if (GameClient_ProcessReceivedMessage(msg, nbn_game_client.server_connection) < 0) { @@ -3503,7 +3531,7 @@ NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *drive NBN_MessageInfo NBN_GameClient_GetMessageInfo(void) { - assert(nbn_game_client.last_event.type == NBN_MESSAGE_RECEIVED); + NBN_Assert(nbn_game_client.last_event.type == NBN_MESSAGE_RECEIVED); return nbn_game_client.last_event.data.message_info; } @@ -3615,27 +3643,25 @@ static int GameClient_HandleMessageReceivedEvent(void) } else if (message_info.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE) { - NBN_Reader reader; - - NBN_Reader_Init(&reader, message_info.data, message_info.length); + int length = message_info.length; - unsigned int length; - - if (NBN_Reader_ReadUInt32(&reader, &length) < 0) + if (length > 0) { - return NBN_ERROR; - } + if (length > NBN_SERVER_DATA_MAX_SIZE) + { + NBN_LogError("Received an invalid client accepted message"); - if (length > NBN_SERVER_DATA_MAX_SIZE) - { - NBN_LogError("Received an invalid client accepted message"); + return NBN_ERROR; + } - return NBN_ERROR; - } + NBN_Reader reader; - if (NBN_Reader_ReadBytes(&reader, nbn_game_client.server_data, length) < 0) - { - return NBN_ERROR; + NBN_Reader_Init(&reader, message_info.data, length); + + if (NBN_Reader_ReadBytes(&reader, nbn_game_client.server_data, length) < 0) + { + return NBN_ERROR; + } } nbn_game_client.server_data_len = length; @@ -3937,7 +3963,6 @@ int NBN_GameServer_AcceptIncomingConnectionWithData(uint8_t *data, unsigned int { NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); - NBN_Assert(data != NULL); NBN_Connection *client = nbn_game_server.last_event.data.connection; uint8_t *msg = NULL; @@ -3948,15 +3973,13 @@ int NBN_GameServer_AcceptIncomingConnectionWithData(uint8_t *data, unsigned int NBN_Assert(length > 0); NBN_Assert(length <= NBN_SERVER_DATA_MAX_SIZE); - msg = NBN_Allocator(length); // TODO: pooling + msg_length = length; + msg = NBN_Allocator(msg_length); // TODO: pooling NBN_Writer writer; NBN_Writer_Init(&writer, msg, length); - NBN_Writer_WriteUInt32(&writer, length); NBN_Writer_WriteBytes(&writer, data, length); - - msg_length = writer.position; } if (GameServer_SendMessageTo(client, NBN_CLIENT_ACCEPTED_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, msg, msg_length) < 0) @@ -4074,6 +4097,7 @@ static int GameServer_AddClient(NBN_Connection *client) NBN_ConnectionVector_Add(nbn_game_server.clients, client); NBN_ConnectionTable_Add(nbn_game_server.clients_table, client); + NBN_LogDebug("Added client %d", client->id); return 0; } @@ -4186,8 +4210,9 @@ static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio } else { - NBN_MessageInfo msg_info = {message->header.type, message->header.channel_id, message->data, client->id}; + NBN_MessageInfo msg_info = {message->header.type, message->header.channel_id, message->data, message->header.length, client->id}; + NBN_LogDebug("Received message (type: %d, id: %d) from client %d", message->header.type, message->header.id, client->id); ev.data.message_info = msg_info; } @@ -4295,12 +4320,13 @@ static int GameServer_HandleEvent(void) static int GameServer_HandleMessageReceivedEvent(void) { + NBN_MessageInfo message_info = nbn_game_server.last_event.data.message_info; NBN_Connection *sender = NBN_ConnectionTable_Get(nbn_game_server.clients_table, message_info.sender); if (sender == NULL) { - NBN_LogTrace("Received message from unknown client (ID: %d)", message_info.sender); + NBN_LogTrace("Received message (type: %d) from unknown client (ID: %d)", message_info.type, message_info.sender); return NBN_SKIP_EVENT; } @@ -4331,6 +4357,7 @@ static int GameServer_HandleMessageReceivedEvent(void) } // at this point we know it's a connection request + NBN_Assert(message_info.type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE); nbn_game_server.last_connection_data_len = 0; memset(nbn_game_server.last_connection_data, 0, sizeof(nbn_game_server.last_connection_data)); diff --git a/net_drivers/udp.h b/net_drivers/udp.h index c711555..39761bd 100644 --- a/net_drivers/udp.h +++ b/net_drivers/udp.h @@ -490,7 +490,10 @@ static int NBN_UDP_ServRecvPackets(void) continue; if (NBN_Packet_InitRead(&packet, nbn_udp_serv.protocol_id, bytes) < 0) + { + NBN_LogDebug("Discarded invalid packet"); continue; + } ip_address.host = ntohl(src_addr.sin_addr.s_addr); ip_address.port = ntohs(src_addr.sin_port); @@ -656,7 +659,10 @@ static int NBN_UDP_CliRecvPackets(void) continue; if (NBN_Packet_InitRead(&packet, nbn_udp_cli.protocol_id, bytes) < 0) + { + NBN_LogDebug("Discarded invalid packet"); continue; + } packet.sender = nbn_udp_cli.server_conn; From d0c91ae1b0448b33b3500d57ad3511a44641f51f Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sat, 25 Oct 2025 14:28:53 +0200 Subject: [PATCH 05/85] rework message system --- nbnet.h | 1267 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 788 insertions(+), 479 deletions(-) diff --git a/nbnet.h b/nbnet.h index 9a2e7b3..862abae 100644 --- a/nbnet.h +++ b/nbnet.h @@ -196,6 +196,7 @@ void NBN_Writer_WriteInt32(NBN_Writer *writer, int32_t value); void NBN_Writer_WriteUInt16(NBN_Writer *writer, uint16_t value); void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value); void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value); +void NBN_Writer_WriteFloat(NBN_Writer *writer, float value); void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int length); void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length); @@ -203,17 +204,21 @@ int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value); int NBN_Reader_ReadUInt16(NBN_Reader *reader, uint16_t *value); int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value); int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value); +int NBN_Reader_ReadFloat(NBN_Reader *reader, float *value); int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length); #pragma endregion /* Serialization */ #pragma region NBN_Message -#define NBN_MAX_CHANNELS 8 -#define NBN_LIBRARY_RESERVED_CHANNELS 3 +#define NBN_MAX_CHANNELS 3 // TODO: find a way to support more user-defined channels #define NBN_MAX_MESSAGE_TYPES 255 /* Maximum value of uint8_t, see message header */ +#define NBN_RESERVED_MESSAGE_TYPES 4 /* Number of message types reserved for the library */ +/* Range of message types that can be used by user code */ +#define NBN_USER_MESSAGE_TYPE_RANGE (NBN_MAX_MESSAGE_TYPES - NBN_RESERVED_MESSAGE_TYPES) #define NBN_MESSAGE_RESEND_DELAY 0.1 /* Number of seconds before a message is resent (reliable messages redundancy) */ #define NBN_MESSAGE_HEADER_SIZE 6 /* See NBN_MessageHeader struct */ +#define NBN_LIBRARY_MESSAGE_POOL_INITIAL_SIZE 32 // IMPORTANT: make sure you update NBN_MESSAGE_HEADER_SIZE if you modify NBN_MessageHeader struct typedef struct NBN_MessageHeader @@ -224,14 +229,30 @@ typedef struct NBN_MessageHeader uint8_t channel_id; } NBN_MessageHeader; +typedef enum { NBN_OUTGOING_MESSAGE, NBN_INCOMING_MSG } NBN_MessageType; + typedef struct NBN_Message { NBN_MessageHeader header; + NBN_MessageType type; NBN_Connection *sender; unsigned int ref_count; uint8_t *data; } NBN_Message; +typedef struct NBN_OutgoingMessage +{ + NBN_Message *message; + uint16_t id; + double last_send_time; // TODO: use float +} NBN_OutgoingMessage; + +typedef struct NBN_IncomingMessage +{ + NBN_Message message; + bool free; +} NBN_IncomingMessage; + /** * Information about a received message. */ @@ -310,7 +331,7 @@ typedef struct NBN_Packet } NBN_Packet; void NBN_Packet_InitWrite(NBN_Packet *, uint32_t, uint16_t, uint16_t, uint32_t); -int NBN_Packet_WriteMessage(NBN_Packet *, NBN_Message *); +int NBN_Packet_WriteMessage(NBN_Packet *, NBN_OutgoingMessage *); int NBN_Packet_Seal(NBN_Packet *, NBN_Connection *); int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); @@ -318,11 +339,11 @@ int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); #pragma region Library reserved messages -#define NBN_MESSAGE_CHUNK_TYPE (NBN_MAX_MESSAGE_TYPES) -#define NBN_CLIENT_CLOSED_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 1) -#define NBN_CLIENT_ACCEPTED_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 2) -#define NBN_DISCONNECTION_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 3) -#define NBN_CONNECTION_REQUEST_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 4) +// IMPORTANT: update NBN_RESERVED_MESSAGE_TYPES if you add or remove library messages +#define NBN_CLIENT_CLOSED_MESSAGE_TYPE NBN_MAX_MESSAGE_TYPES +#define NBN_CLIENT_ACCEPTED_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 1) +#define NBN_DISCONNECTION_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 2) +#define NBN_CONNECTION_REQUEST_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 3) /* * Chunk max size is the number of bytes of data a packet can hold minus the size of a message header minus 2 bytes @@ -330,8 +351,8 @@ int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); */ #define NBN_MESSAGE_CHUNK_SIZE (NBN_PACKET_MAX_DATA_SIZE - NBN_MESSAGE_HEADER_SIZE - 2) -#define NBN_SERVER_DATA_MAX_SIZE 1024 -#define NBN_CONNECTION_DATA_MAX_SIZE 512 +#define NBN_SERVER_DATA_MAX_SIZE 256 +#define NBN_CONNECTION_DATA_MAX_SIZE 256 #pragma endregion /* Library reserved messages */ @@ -345,23 +366,13 @@ int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); /* IMPORTANT: if you add a library reserved channel below, make sure to update NBN_LIBRARY_RESERVED_CHANNELS */ /* Library reserved unreliable ordered channel */ -#define NBN_CHANNEL_RESERVED_UNRELIABLE (NBN_MAX_CHANNELS - 1) +#define NBN_CHANNEL_RESERVED_UNRELIABLE 0 /* Library reserved reliable ordered channel */ -#define NBN_CHANNEL_RESERVED_RELIABLE (NBN_MAX_CHANNELS - 2) - -/* Library reserved messages channel */ -#define NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES (NBN_MAX_CHANNELS - 3) +#define NBN_CHANNEL_RESERVED_RELIABLE 1 typedef NBN_Channel *(*NBN_ChannelBuilder)(void); -typedef void (*NBN_ChannelDestructor)(NBN_Channel *); - -typedef struct NBN_MessageSlot -{ - NBN_Message message; - double last_send_time; - bool free; -} NBN_MessageSlot; +typedef void (*NBN_ChannelDestructor)(NBN_Endpoint *, NBN_Channel *); struct NBN_Channel { @@ -369,7 +380,6 @@ struct NBN_Channel // uint8_t *write_chunk_buffer; uint16_t next_outgoing_message_id; uint16_t next_recv_message_id; - unsigned int next_outgoing_message_pool_slot; unsigned int outgoing_message_count; unsigned int chunk_count; unsigned int write_chunk_buffer_size; @@ -379,25 +389,23 @@ struct NBN_Channel // uint8_t *read_chunk_buffer; NBN_ChannelDestructor destructor; NBN_Connection *connection; - NBN_MessageSlot outgoing_message_slot_buffer[NBN_CHANNEL_BUFFER_SIZE]; - NBN_MessageSlot recved_message_slot_buffer[NBN_CHANNEL_BUFFER_SIZE]; + NBN_OutgoingMessage outgoing_messages_buffer[NBN_CHANNEL_BUFFER_SIZE]; + NBN_IncomingMessage incoming_messages_buffer[NBN_CHANNEL_BUFFER_SIZE]; // NBN_MessageChunk *recv_chunk_buffer[NBN_CHANNEL_CHUNKS_BUFFER_SIZE]; - NBN_Message outgoing_message_pool[NBN_CHANNEL_OUTGOING_MESSAGE_POOL_SIZE]; - bool (*AddReceivedMessage)(NBN_Channel *, NBN_Message *); + bool (*AddReceivedMessage)(NBN_Endpoint *, NBN_Channel *, NBN_Message *); bool (*AddOutgoingMessage)(NBN_Channel *, NBN_Message *); NBN_Message *(*GetNextRecvedMessage)(NBN_Channel *); - NBN_Message *(*GetNextOutgoingMessage)(NBN_Channel *, double); - int (*OnOutgoingMessageAcked)(NBN_Channel *, uint16_t); - int (*OnOutgoingMessageSent)(NBN_Channel *, NBN_Message *); + bool (*GetNextOutgoingMessage)(NBN_Channel *, NBN_OutgoingMessage *, double); + int (*OnOutgoingMessageAcked)(NBN_Endpoint *, NBN_Channel *, uint16_t); + int (*OnOutgoingMessageSent)(NBN_Endpoint *, NBN_Channel *, NBN_Message *); }; -void NBN_Channel_Destroy(NBN_Channel *); +void NBN_Channel_Destroy(NBN_Endpoint *, NBN_Channel *); // bool NBN_Channel_AddChunk(NBN_Channel *, NBN_Message *); int NBN_Channel_ReconstructMessageFromChunks(NBN_Channel *, NBN_Connection *, NBN_Message *); /*void NBN_Channel_ResizeWriteChunkBuffer(NBN_Channel *, unsigned int);*/ /*void NBN_Channel_ResizeReadChunkBuffer(NBN_Channel *, unsigned int);*/ -void NBN_Channel_UpdateMessageLastSendTime(NBN_Channel *, NBN_Message *, double); /* Unreliable ordered @@ -499,7 +507,7 @@ struct NBN_Connection #ifdef NBN_DEBUG /* Debug callbacks */ - void (*OnMessageAddedToRecvQueue)(struct NBN_Connection *, NBN_Message *); + void (*OnMessageAddedToRecvQueue)(struct NBN_Connection *, NBN_Message *); // TODO: rename this function pointer #endif /* NBN_DEBUG */ /* @@ -522,10 +530,13 @@ struct NBN_ConnectionListNode NBN_ConnectionListNode *prev; }; +typedef uint8_t *(*NBN_AllocMessageFunc)(NBN_MessageHeader *); +typedef uint8_t *(*NBN_DeallocMessageFunc)(NBN_MessageHeader *, uint8_t *); + NBN_Connection *NBN_Connection_Create(uint32_t, NBN_Endpoint *, NBN_Driver *, void *); -void NBN_Connection_Destroy(NBN_Connection *); -int NBN_Connection_ProcessReceivedPacket(NBN_Connection *, NBN_Packet *, double); -int NBN_Connection_FlushChannels(NBN_Connection *, uint32_t, double); +void NBN_Connection_Destroy(NBN_Endpoint *, NBN_Connection *); +int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); +int NBN_Connection_FlushChannels(NBN_Endpoint *, NBN_Connection *, uint32_t, double); int NBN_Connection_InitChannel(NBN_Connection *, NBN_Channel *); bool NBN_Connection_CheckIfStale(NBN_Connection *, double); @@ -651,9 +662,10 @@ void NBN_PacketSimulator_Stop(NBN_PacketSimulator *); #pragma region NBN_Endpoint -#define NBN_IsReservedMessage(type) (type == NBN_MESSAGE_CHUNK_TYPE || type == NBN_CLIENT_CLOSED_MESSAGE_TYPE \ -|| type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE || type == NBN_BYTE_ARRAY_MESSAGE_TYPE \ -|| type == NBN_DISCONNECTION_MESSAGE_TYPE || type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE) +typedef struct NBN_UserMessageDef { + int type; + unsigned int max_length; +} NBN_UserMessageDef; struct NBN_Endpoint { @@ -663,6 +675,12 @@ struct NBN_Endpoint uint32_t protocol_id; bool is_server; double time; + NBN_MemPool *outgoing_msg_pool; + NBN_MemPool *msg_buffer_pools[NBN_MAX_MESSAGE_TYPES + 1]; + NBN_Message *write_message; + unsigned int write_message_buffer_len; + NBN_Writer message_writer; + NBN_Reader message_reader; #ifdef NBN_DEBUG /* Debug callbacks */ @@ -690,21 +708,32 @@ enum NBN_MESSAGE_RECEIVED }; +typedef struct NBN_GameClient_Config +{ + const char *protocol_name; + const char *host; + uint16_t port; + NBN_UserMessageDef user_message_defs[NBN_USER_MESSAGE_TYPE_RANGE + 1]; +} NBN_GameClient_Config; + typedef struct NBN_GameClient { NBN_Endpoint endpoint; NBN_Connection *server_connection; bool is_connected; - uint8_t server_data[NBN_SERVER_DATA_MAX_SIZE]; /* Data sent by the server when accepting the client's connection */ - unsigned int server_data_len; /* Length of the received server data in bytes */ + uint8_t server_data_buffer[NBN_SERVER_DATA_MAX_SIZE]; + uint8_t client_data_buffer[NBN_CONNECTION_DATA_MAX_SIZE]; + unsigned int server_data_len; NBN_Event last_event; int closed_code; + NBN_Writer client_data_writer; + NBN_Reader server_data_reader; } NBN_GameClient; extern NBN_GameClient nbn_game_client; /** - * Start the game client and send a connection request to the server. + * Create a minimal configuration to start a client. * * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be able to communicate * @param host Host to connect to @@ -712,34 +741,30 @@ extern NBN_GameClient nbn_game_client; * * @return 0 when successully started, -1 otherwise */ -int NBN_GameClient_Start(const char *protocol_name, const char *host, uint16_t port); +NBN_GameClient_Config NBN_GameClient_CreateConfig(const char *protocol_name, const char *host, uint16_t port); + +// TODO: doc +void NBN_GameClient_RegisterMessageType(NBN_GameClient_Config *config, uint8_t type, unsigned int max_length); + +// TODO: doc +NBN_Writer *NBN_GameClient_GetConnectionDataWriter(void); /** - * Same as NBN_GameClient_Start but with additional parameters. + * Start the game client with the provided configuration. * - * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be able to communicate - * @param host Host to connect to - * @param port Port to connect to - * @param data Array of bytes to send to the server upon connection (cannot exceed NBN_CONNECTION_DATA_MAX_SIZE) - * @param length length of the array of bytes + * @param config the configuration to use to start the client * * @return 0 when successully started, -1 otherwise */ -int NBN_GameClient_StartEx(const char *protocol_name, const char *host, uint16_t port, uint8_t *data, unsigned int length); +int NBN_GameClient_Start(NBN_GameClient_Config config); /** * Disconnect from the server. The client can be restarted by calling NBN_GameClient_Start or NBN_GameClient_StartWithData again. */ void NBN_GameClient_Stop(void); -/** - * Read the server data that was received upon connection into a preallocated buffer (the buffer must have a size of at least NBN_SERVER_DATA_MAX_SIZE bytes). - * - * @param data The target buffer to copy the server data to - * - * @return the length of the server data in bytes - */ -unsigned int NBN_GameClient_ReadServerData(uint8_t *data); +// TODO: doc +NBN_Reader *NBN_GameClient_GetServerDataReader(void); /** * Poll game client events. @@ -760,20 +785,20 @@ int NBN_GameClient_Poll(void); */ int NBN_GameClient_SendPackets(void); -/** - * Send a message to the server on a given channel. - * - * It's recommended to use NBN_GameClient_SendUnreliableMessage or NBN_GameClient_SendReliableMessage - * unless you really want to use a specific channel. - * - * @param type The user-defined type of message to send - * @param channel_id The ID of the channel to send the message on - * @param data A pointer to the message data buffer (managed by user code) - * @param length The length of the message data buffer in bytes - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameClient_SendMessage(uint8_t type, uint8_t channel_id, uint8_t *data, uint16_t length); +// TODO: doc +void NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id); +// TODO: doc +void NBN_GameClient_CreateUnreliableMessage(uint8_t type); +// TODO: doc +void NBN_GameClient_CreateReliableMessage(uint8_t type); + +// TODO: doc +int NBN_GameClient_SendMessage(void); + +// TODO: doc +NBN_Writer *NBN_GameClient_GetMessageWriter(void); +// TODO: doc +NBN_Reader *NBN_GameClient_GetMessageReader(void); /** * Send a message to the server, unreliably. @@ -863,6 +888,13 @@ typedef struct NBN_GameServerStats float download_bandwidth; /* Total download bandwith of the game server */ } NBN_GameServerStats; +typedef struct NBN_GameServer_Config +{ + const char *protocol_name; + uint16_t port; + NBN_UserMessageDef user_message_defs[NBN_USER_MESSAGE_TYPE_RANGE + 1]; +} NBN_GameServer_Config; + typedef struct NBN_GameServer { NBN_Endpoint endpoint; @@ -871,31 +903,36 @@ typedef struct NBN_GameServer NBN_ConnectionListNode *closed_clients_head; NBN_GameServerStats stats; NBN_Event last_event; - uint8_t last_connection_data[NBN_CONNECTION_DATA_MAX_SIZE]; - unsigned int last_connection_data_len; + uint8_t server_data_buffer[NBN_SERVER_DATA_MAX_SIZE]; + uint8_t client_data_buffer[NBN_CONNECTION_DATA_MAX_SIZE]; + unsigned int client_data_len; + NBN_Writer server_data_writer; + NBN_Reader client_data_reader; } NBN_GameServer; extern NBN_GameServer nbn_game_server; /** - * Start the game server. - * + * Create a minimal configuration to start a server. + * * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be able to communicate - * @param port The server's port + * @param port The port clients will connect to * - * @return 0 when successfully started, -1 otherwise + * @return 0 when successully started, -1 otherwise */ -int NBN_GameServer_Start(const char *protocol_name, uint16_t port); +NBN_GameServer_Config NBN_GameServer_CreateConfig(const char *protocol_name, uint16_t port); + +// TODO: doc +void NBN_GameServer_RegisterMessageType(NBN_GameServer_Config *config, uint8_t type, unsigned int max_length); /** - * Same as NBN_GameServer_Start but with additional parameters. - * - * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be able to communicate - * @param port The server's port + * Start the game server with the provided configuration. + * + * @param config the configuration to use to start the server * * @return 0 when successfully started, -1 otherwise */ -int NBN_GameServer_StartEx(const char *protocol_name, uint16_t port); +int NBN_GameServer_Start(NBN_GameServer_Config config); /** * Stop the game server and clean everything up. @@ -947,65 +984,26 @@ int NBN_GameServer_CloseClient(NBN_ConnectionHandle connection_handle); */ int NBN_GameServer_CloseClientWithCode(NBN_ConnectionHandle connection_handle, int code); -/** - * Send a message to a client on a given channel. - * - * It's recommended to use NBN_GameServer_SendUnreliableMessageTo or NBN_GameServer_SendReliableMessageTo - * unless you really want to use a specific channel. - * - * @param connection_handle The connection to send the message to - * @param type The type of message to send - * @param channel_id The ID of the channel to send the message on - * @param data A pointer to the message data buffer (managed by user code) - * @param length The length of the message data buffer in bytes - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle, uint8_t type, uint8_t channel_id, uint8_t *data, uint16_t length); - -/** - * Send a message to a client, unreliably. - * - * @param connection_handle The connection to send the message to - * @param type The type of message to send - * @param data A pointer to the message data buffer (managed by user code) - * @param length The length of the message data buffer in bytes - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_SendUnreliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t type, uint8_t *data, uint16_t length); - -/** - * Send a message to a client, reliably. - * - * @param connection_handle The connection to send the message to - * @param type The type of message to send - * @param data A pointer to the message data buffer (managed by user code) - * @param length The length of the message data buffer in bytes - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_SendReliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t type, uint8_t *data, uint16_t length); - -/** - * Accept the last client connection request and send a blob of data to the client. - * The client can read that data using the NBN_GameClient_ReadServerData function. - * If you do not wish to send any data to the client upon accepting his connection, use the NBN_GameServer_AcceptIncomingConnection function instead. - * - * Call this function after receiving a NBN_NEW_CONNECTION event. - * - * @param data Data to send - * @param length Data length in bytes - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_AcceptIncomingConnectionWithData(uint8_t *data, unsigned int length); - -/** - * Accept the last client connection request. - * - * @return 0 when successful, -1 otherwise - */ +// TODO: doc +void NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id); +// TODO: doc +void NBN_GameServer_CreateUnreliableMessage(uint8_t type); +// TODO: doc +void NBN_GameServer_CreateReliableMessage(uint8_t type); +// TODO: doc +int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle); +// TODO: doc +int NBN_GameServer_BroadcastMessage(void); + +// TODO: doc +NBN_Writer *NBN_GameServer_GetMessageWriter(void); +// TODO: doc +NBN_Reader *NBN_GameServer_GetMessageReader(void); + +// TODO: doc +NBN_Writer *NBN_GameServer_GetConnectionDataWriter(void); + +// TODO: doc int NBN_GameServer_AcceptIncomingConnection(void); /** @@ -1038,14 +1036,8 @@ int NBN_GameServer_RejectIncomingConnection(void); */ NBN_ConnectionHandle NBN_GameServer_GetIncomingConnection(void); -/** - * Read the last connection data into a preallocated buffer. The target buffer must have a length of at least NBN_CONNECTION_DATA_MAX_SIZE bytes. - * - * @param data the buffer to copy the connection data to - * - * @return the length in bytes of the connection data - */ -unsigned int NBN_GameServer_ReadIncomingConnectionData(uint8_t *data); +// TODO: doc +NBN_Reader *NBN_GameServer_GetConnectionDataReader(void); /** * Return the last disconnected client. @@ -1585,7 +1577,12 @@ static void MemPool_Grow(NBN_MemPool *pool, unsigned int block_count) pool->blocks = (uint8_t**)NBN_Reallocator(pool->blocks, sizeof(uint8_t *) * block_count); for (unsigned int i = 0; i < block_count - pool->block_count; i++) - pool->blocks[pool->block_idx + i] = (uint8_t*)NBN_Allocator(pool->block_size); + { + uint8_t *block = (uint8_t*)NBN_Allocator(pool->block_size); + + memset(block, 0, pool->block_size); + pool->blocks[pool->block_idx + i] = block; + } pool->block_count = block_count; } @@ -1632,6 +1629,14 @@ void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value) writer->position++; } +void NBN_Writer_WriteFloat(NBN_Writer *writer, float value) +{ + NBN_Assert(writer->position + 4 <= writer->length); + + uint32_t *val_u = (uint32_t *)&value; + NBN_Writer_WriteUInt32(writer, htonl(*val_u)); +} + void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int length) { NBN_Assert(writer->position + length <= writer->length); @@ -1659,7 +1664,7 @@ int NBN_Reader_ReadUInt16(NBN_Reader *reader, uint16_t *value) return NBN_ERROR; } - *value = *((uint16_t *)(reader->buffer + reader->position)); + *value = ntohs(*((uint16_t *)(reader->buffer + reader->position))); reader->position += 2; return 0; @@ -1691,10 +1696,22 @@ int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value) return 0; } +int NBN_Reader_ReadFloat(NBN_Reader *reader, float *value) +{ + if (NBN_Reader_ReadUInt32(reader, (uint32_t *)value) < 0) + { + return -1; + } + + return 0; +} + int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length) { if (reader->position + length > reader->length) { + NBN_LogError("reader->position = %d, length = %d, reader->length = %d", + reader->position, length, reader->length); return NBN_ERROR; } @@ -1724,8 +1741,14 @@ void NBN_Packet_InitWrite(NBN_Packet *packet, uint32_t protocol_id, uint16_t seq memset(packet->buffer, 0, sizeof(packet->buffer)); } -int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_Message *message) +int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_OutgoingMessage *out_msg) { + NBN_Message *message = out_msg->message; + + NBN_LogTrace("Write message %d (type: %d, length: %d) to packet %d", + message->header.id, message->header.type, message->header.length, + packet->header.seq_number); + NBN_Assert( (message->data != NULL && message->header.length > 0) || (message->data == NULL && message->header.length == 0)); @@ -1745,7 +1768,7 @@ int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_Message *message) NBN_Writer_Init(&writer, packet->buffer + packet->size, sizeof(packet->buffer) - packet->size); - NBN_Writer_WriteUInt16(&writer, message->header.id); + NBN_Writer_WriteUInt16(&writer, out_msg->id); NBN_Writer_WriteUInt16(&writer, message->header.length); NBN_Writer_WriteUInt8(&writer, message->header.type); NBN_Writer_WriteUInt8(&writer, message->header.channel_id); @@ -1754,6 +1777,7 @@ int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_Message *message) if (message->data) { + NBN_Assert(message->header.length > 0); NBN_Writer_WriteBytes(&writer, message->data, message->header.length); } @@ -1843,19 +1867,19 @@ int NBN_Packet_InitRead(NBN_Packet *packet, uint32_t protocol_id, unsigned int s #pragma region NBN_Connection -static NBN_Message *Endpoint_CreateOutgoingMessage(NBN_Endpoint *, NBN_Channel*, uint8_t, uint8_t *, uint16_t); +static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *, uint8_t, uint8_t); static uint32_t Connection_BuildPacketAckBits(NBN_Connection *); -static int Connection_DecodePacketHeader(NBN_Connection *, NBN_Packet *, double); -static int Connection_AckPacket(NBN_Connection *, uint16_t, double time); +static int Connection_DecodePacketHeader(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); +static int Connection_AckPacket(NBN_Endpoint *, NBN_Connection *, uint16_t, double time); static void Connection_InitOutgoingPacket(NBN_Connection *, uint32_t, NBN_Packet *, NBN_PacketEntry **); static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *, uint16_t); static bool Connection_InsertReceivedPacketEntry(NBN_Connection *, uint16_t); static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *, uint16_t); static bool Connection_IsPacketReceived(NBN_Connection *, uint16_t); static int Connection_SendPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry *, double); -static int Connection_ReadNextMessageFromBuffer(NBN_Reader *, NBN_Message *); -static void Connection_RecycleMessage(NBN_Channel *, NBN_Message *); +static int Connection_ReadNextMessageFromBuffer(NBN_Endpoint *, NBN_Reader *, NBN_Message *); +static void Connection_ReleaseMessage(NBN_Endpoint *, NBN_Channel *, NBN_Message *); static void Connection_UpdateAveragePing(NBN_Connection *, double); static void Connection_UpdateAveragePacketLoss(NBN_Connection *, uint16_t); static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *, float); @@ -1896,7 +1920,7 @@ NBN_Connection *NBN_Connection_Create(uint32_t id, NBN_Endpoint *endpoint, NBN_D return connection; } -void NBN_Connection_Destroy(NBN_Connection *connection) +void NBN_Connection_Destroy(NBN_Endpoint *endpoint, NBN_Connection *connection) { for (int i = 0; i < NBN_MAX_CHANNELS; i++) { @@ -1904,17 +1928,18 @@ void NBN_Connection_Destroy(NBN_Connection *connection) if (channel) { - assert(channel->destructor); - channel->destructor(channel); + NBN_Assert(channel->destructor); + channel->destructor(endpoint, channel); } } MemoryManager_Dealloc(connection, NBN_MEM_CONNECTION); } -int NBN_Connection_ProcessReceivedPacket(NBN_Connection *connection, NBN_Packet *packet, double time) +int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, + NBN_Packet *packet, double time) { - if (Connection_DecodePacketHeader(connection, packet, time) < 0) + if (Connection_DecodePacketHeader(endpoint, connection, packet, time) < 0) { NBN_LogError("Failed to decode packet %d header", packet->header.seq_number); @@ -1933,10 +1958,16 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Connection *connection, NBN_Packet NBN_Reader_Init(&msg_reader, packet->buffer + NBN_PACKET_HEADER_SIZE, packet->size - NBN_PACKET_HEADER_SIZE); + NBN_LogDebug("Processing received packet %d (message count: %d)", packet->header.seq_number, packet->header.messages_count); + for (int i = 0; i < packet->header.messages_count; i++) { + NBN_LogTrace("Reading message number %d from packet %d", i, packet->header.seq_number); + NBN_Message message = {0}; - int msg_len = Connection_ReadNextMessageFromBuffer(&msg_reader, &message); + message.type = NBN_INCOMING_MSG; + message.ref_count = 1; + int msg_len = Connection_ReadNextMessageFromBuffer(endpoint, &msg_reader, &message); if (msg_len < 0) { @@ -1956,9 +1987,10 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Connection *connection, NBN_Packet NBN_Channel *channel = connection->channels[channel_id]; - if (channel->AddReceivedMessage(channel, &message)) + if (channel->AddReceivedMessage(endpoint, channel, &message)) { - NBN_LogTrace("Received message %d on channel %d : added to recv queue", message.header.id, channel->id); + NBN_LogDebug("Received message %d (type: %d) on channel %d", + message.header.id, message.header.type, channel->id); #ifdef NBN_DEBUG if (connection->OnMessageAddedToRecvQueue) connection->OnMessageAddedToRecvQueue(connection, &message); @@ -1966,18 +1998,19 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Connection *connection, NBN_Packet } else { - NBN_LogTrace("Received message %d : discarded", message.header.id); + NBN_LogDebug("Received message %d : discarded", message.header.id); - Connection_RecycleMessage(channel, &message); + Connection_ReleaseMessage(endpoint, channel, &message); } } return 0; } -int NBN_Connection_FlushChannels(NBN_Connection *connection, uint32_t protocol_id, double time) +int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connection, uint32_t protocol_id, + double time) { - NBN_LogTrace("Flushing the send queue"); + NBN_LogTrace("Flushing all channels"); NBN_Packet packet = {0}; NBN_PacketEntry *packet_entry; @@ -1995,17 +2028,20 @@ int NBN_Connection_FlushChannels(NBN_Connection *connection, uint32_t protocol_i NBN_LogTrace("Flushing channel %d (message count: %d)", channel->id, channel->outgoing_message_count); - NBN_Message *message; + NBN_OutgoingMessage out_msg; unsigned int j = 0; + // TODO: use bandwidth to determine how many packets to send at most while ( j < channel->outgoing_message_count && sent_packet_count < NBN_CONNECTION_MAX_SENT_PACKET_COUNT && - (message = channel->GetNextOutgoingMessage(channel, time)) != NULL + channel->GetNextOutgoingMessage(channel, &out_msg, time) ) { + NBN_Message *message = out_msg.message; + uint8_t msg_id = out_msg.id; bool message_sent = false; - int ret = NBN_Packet_WriteMessage(&packet, message); + int ret = NBN_Packet_WriteMessage(&packet, &out_msg); if (ret == NBN_PACKET_WRITE_OK) { @@ -2025,7 +2061,7 @@ int NBN_Connection_FlushChannels(NBN_Connection *connection, uint32_t protocol_i Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); - int ret = NBN_Packet_WriteMessage(&packet, message); + int ret = NBN_Packet_WriteMessage(&packet, &out_msg); if (ret != NBN_PACKET_WRITE_OK) { @@ -2039,24 +2075,27 @@ int NBN_Connection_FlushChannels(NBN_Connection *connection, uint32_t protocol_i else if (ret == NBN_PACKET_WRITE_ERROR) { NBN_LogError("Failed to write message %d of type %d to packet %d", - message->header.id, message->header.type, packet.header.seq_number); + msg_id, message->header.type, packet.header.seq_number); return NBN_ERROR; } if (message_sent) { - NBN_LogTrace("Message %d added to packet %d", message->header.id, packet.header.seq_number); + NBN_LogTrace("Message %d added to packet %d (length: %d, type: %d)", + msg_id, packet.header.seq_number, + message->header.length, message->header.type); - NBN_Channel_UpdateMessageLastSendTime(channel, message, time); + // TODO: this does not work, last_send_time needs to be updated in the channel buffer + out_msg.last_send_time = time; - NBN_MessageEntry e = { message->header.id, channel->id }; - - packet_entry->messages[packet_entry->messages_count++] = e; + packet_entry->messages[packet_entry->messages_count++] = (NBN_MessageEntry){ + msg_id, channel->id + }; if (channel->OnOutgoingMessageSent) { - channel->OnOutgoingMessageSent(channel, message); + channel->OnOutgoingMessageSent(endpoint, channel, message); } } @@ -2099,8 +2138,8 @@ int NBN_Connection_InitChannel(NBN_Connection *connection, NBN_Channel *channel) for (unsigned int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) { - channel->recved_message_slot_buffer[i].free = true; - channel->outgoing_message_slot_buffer[i].free = true; + channel->incoming_messages_buffer[i].free = true; + channel->outgoing_messages_buffer[i].message = NULL; } // TODO @@ -2122,9 +2161,10 @@ bool NBN_Connection_CheckIfStale(NBN_Connection *connection, double time) #endif } -static int Connection_DecodePacketHeader(NBN_Connection *connection, NBN_Packet *packet, double time) +static int Connection_DecodePacketHeader(NBN_Endpoint *endpoint, NBN_Connection *connection, + NBN_Packet *packet, double time) { - if (Connection_AckPacket(connection, packet->header.ack, time) < 0) + if (Connection_AckPacket(endpoint, connection, packet->header.ack, time) < 0) { NBN_LogError("Failed to ack packet %d", packet->header.seq_number); @@ -2136,7 +2176,7 @@ static int Connection_DecodePacketHeader(NBN_Connection *connection, NBN_Packet if (B_IS_UNSET(packet->header.ack_bits, i)) continue; - if (Connection_AckPacket(connection, packet->header.ack - (i + 1), time) < 0) + if (Connection_AckPacket(endpoint, connection, packet->header.ack - (i + 1), time) < 0) { NBN_LogError("Failed to ack packet %d", packet->header.seq_number); @@ -2167,7 +2207,7 @@ static uint32_t Connection_BuildPacketAckBits(NBN_Connection *connection) return ack_bits; } -static int Connection_AckPacket(NBN_Connection *connection, uint16_t ack_packet_seq_number, double time) +static int Connection_AckPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, uint16_t ack_packet_seq_number, double time) { NBN_PacketEntry *packet_entry = Connection_FindSendPacketEntry(connection, ack_packet_seq_number); @@ -2184,11 +2224,11 @@ static int Connection_AckPacket(NBN_Connection *connection, uint16_t ack_packet_ NBN_MessageEntry *msg_entry = &packet_entry->messages[i]; NBN_Channel *channel = connection->channels[msg_entry->channel_id]; - assert(channel != NULL); + NBN_Assert(channel != NULL); if (channel->OnOutgoingMessageAcked) { - if (channel->OnOutgoingMessageAcked(channel, msg_entry->id) < 0) + if (channel->OnOutgoingMessageAcked(endpoint, channel, msg_entry->id) < 0) return NBN_ERROR; } } @@ -2308,7 +2348,7 @@ static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, } } -static int Connection_ReadNextMessageFromBuffer(NBN_Reader *reader, NBN_Message *message) +static int Connection_ReadNextMessageFromBuffer(NBN_Endpoint *endpoint, NBN_Reader *reader, NBN_Message *message) { if (NBN_Reader_ReadUInt16(reader, &message->header.id) < 0) { @@ -2340,8 +2380,23 @@ static int Connection_ReadNextMessageFromBuffer(NBN_Reader *reader, NBN_Message if (message->header.length > 0) { - // TODO: user defined message allocation strategy - message->data = NBN_Allocator(message->header.length); + NBN_MemPool *msg_buffer_pool = endpoint->msg_buffer_pools[message->header.type]; + + if (msg_buffer_pool == NULL) + { + NBN_LogError("No message buffer pool for message of type %d", message->header.type); + + return NBN_ERROR; + } + + if (message->header.length > msg_buffer_pool->block_size) + { + NBN_LogError("Message of type %d is too big for buffer pool", message->header.type); + + return NBN_ERROR; + } + + message->data = MemPool_Alloc(msg_buffer_pool); if (NBN_Reader_ReadBytes(reader, message->data, message->header.length) < 0) { @@ -2354,35 +2409,66 @@ static int Connection_ReadNextMessageFromBuffer(NBN_Reader *reader, NBN_Message return 0; } -static void Connection_RecycleMessage(NBN_Channel *channel, NBN_Message *message) +static void Connection_ReleaseMessage(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) { - // for incoming messages : message->outgoing_msg == NULL - assert(message->ref_count > 0); + NBN_Assert(message->ref_count > 0); message->ref_count--; - if (message->ref_count == 0) + if (message->ref_count > 0) { - // TODO + return; + } - /*if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) + if (message->type == NBN_OUTGOING_MESSAGE) + { + if (message->header.length > 0) { - NBN_MessageChunk *chunk = (NBN_MessageChunk*)message->data; + NBN_MemPool *msg_buffer_pool = endpoint->msg_buffer_pools[message->header.type]; - if (chunk->outgoing_msg) - { - assert(chunk->outgoing_msg->ref_count > 0); + NBN_Assert(msg_buffer_pool != NULL); + MemPool_Dealloc(msg_buffer_pool, message->data); + } - if (--chunk->outgoing_msg->ref_count == 0) - { - // TODO - } - } - }*/ + MemPool_Dealloc(endpoint->outgoing_msg_pool, message); + } + else if (message->type == NBN_INCOMING_MSG) + { + if (message->header.length > 0) + { + NBN_MemPool *msg_buffer_pool = endpoint->msg_buffer_pools[message->header.type]; - // TODO: user defined message allocation strategy - NBN_Deallocator(message->data); + NBN_Assert(msg_buffer_pool != NULL); + MemPool_Dealloc(msg_buffer_pool, message->data); + } } + else + { + NBN_LogError("Invalid internal message type: %d", message->type); + NBN_Abort(); + } + + // TODO: chunk releasing + // if (message->ref_count == 0) + // { + // + // /*if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) + // { + // NBN_MessageChunk *chunk = (NBN_MessageChunk*)message->data; + // + // if (chunk->outgoing_msg) + // { + // NBN_Assert(chunk->outgoing_msg->ref_count > 0); + // + // if (--chunk->outgoing_msg->ref_count == 0) + // { + // // TODO + // } + // } + // }*/ + // + // NBN_Deallocator(message->data); + // } } static void Connection_UpdateAveragePing(NBN_Connection *connection, double ping) @@ -2446,22 +2532,22 @@ static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *connection #pragma region NBN_Channel -void NBN_Channel_Destroy(NBN_Channel *channel) +void NBN_Channel_Destroy(NBN_Endpoint *endpoint, NBN_Channel *channel) { for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) { - NBN_MessageSlot *slot = &channel->recved_message_slot_buffer[i]; + NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[i]; - if (!slot->free) + if (!inc_msg->free) { - Connection_RecycleMessage(channel, &slot->message); + Connection_ReleaseMessage(endpoint, channel, &inc_msg->message); } - slot = &channel->outgoing_message_slot_buffer[i]; + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[i]; - if (!slot->free) + if (out_msg->message) { - Connection_RecycleMessage(channel, &slot->message); + Connection_ReleaseMessage(endpoint, channel, out_msg->message); } } @@ -2473,7 +2559,7 @@ void NBN_Channel_Destroy(NBN_Channel *channel) /*bool NBN_Channel_AddChunk(NBN_Channel *channel, NBN_Message *chunk_msg)*/ /*{*/ -/* assert(chunk_msg->header.type == NBN_MESSAGE_CHUNK_TYPE);*/ +/* NBN_Assert(chunk_msg->header.type == NBN_MESSAGE_CHUNK_TYPE);*/ /**/ /* NBN_MessageChunk *chunk = (NBN_MessageChunk *)chunk_msg->data;*/ /**/ @@ -2482,7 +2568,7 @@ void NBN_Channel_Destroy(NBN_Channel *channel) /**/ /* if (chunk->id == channel->last_received_chunk_id + 1)*/ /* {*/ -/* assert(channel->recv_chunk_buffer[chunk->id] == NULL);*/ +/* NBN_Assert(channel->recv_chunk_buffer[chunk->id] == NULL);*/ /**/ /* channel->recv_chunk_buffer[chunk->id] = chunk;*/ /* channel->last_received_chunk_id++;*/ @@ -2506,7 +2592,7 @@ void NBN_Channel_Destroy(NBN_Channel *channel) /**/ /* for (unsigned int i = 0; i < channel->chunk_count; i++)*/ /* {*/ -/* assert(channel->recv_chunk_buffer[i] != NULL);*/ +/* NBN_Assert(channel->recv_chunk_buffer[i] != NULL);*/ /**/ /* NBN_MessageChunk_Destroy(channel->recv_chunk_buffer[i]);*/ /**/ @@ -2573,28 +2659,18 @@ int NBN_Channel_ReconstructMessageFromChunks(NBN_Channel *channel, NBN_Connectio /* channel->read_chunk_buffer_size = size;*/ /*}*/ -void NBN_Channel_UpdateMessageLastSendTime(NBN_Channel *channel, NBN_Message *message, double time) -{ - NBN_MessageSlot *slot = &channel->outgoing_message_slot_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; - - assert(slot->message.header.id == message->header.id); - - slot->last_send_time = time; -} - /* Unreliable ordered */ -static bool UnreliableOrderedChannel_AddReceivedMessage(NBN_Channel *, NBN_Message *); +static bool UnreliableOrderedChannel_AddReceivedMessage(NBN_Endpoint *, NBN_Channel *, NBN_Message *); static bool UnreliableOrderedChannel_AddOutgoingMessage(NBN_Channel *, NBN_Message *); static NBN_Message *UnreliableOrderedChannel_GetNextRecvedMessage(NBN_Channel *); -static NBN_Message *UnreliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *, double); -static int UnreliableOrderedChannel_OnMessageSent(NBN_Channel *, NBN_Message *); +static bool UnreliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *, NBN_OutgoingMessage *, double); +static int UnreliableOrderedChannel_OnMessageSent(NBN_Endpoint *, NBN_Channel *, NBN_Message *); NBN_UnreliableOrderedChannel *NBN_UnreliableOrderedChannel_Create(void) { NBN_UnreliableOrderedChannel *channel = (NBN_UnreliableOrderedChannel *)NBN_Allocator(sizeof(NBN_UnreliableOrderedChannel)); - channel->base.next_outgoing_message_pool_slot = 0; channel->base.AddReceivedMessage = UnreliableOrderedChannel_AddReceivedMessage; channel->base.AddOutgoingMessage = UnreliableOrderedChannel_AddOutgoingMessage; channel->base.GetNextRecvedMessage = UnreliableOrderedChannel_GetNextRecvedMessage; @@ -2602,28 +2678,29 @@ NBN_UnreliableOrderedChannel *NBN_UnreliableOrderedChannel_Create(void) channel->base.OnOutgoingMessageAcked = NULL; channel->base.OnOutgoingMessageSent = UnreliableOrderedChannel_OnMessageSent; - memset(channel->base.outgoing_message_pool, 0, sizeof(channel->base.outgoing_message_pool)); - channel->last_received_message_id = 0; channel->next_outgoing_message_slot = 0; return channel; } -static bool UnreliableOrderedChannel_AddReceivedMessage(NBN_Channel *channel, NBN_Message *message) +static bool UnreliableOrderedChannel_AddReceivedMessage(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) { NBN_UnreliableOrderedChannel *unreliable_ordered_channel = (NBN_UnreliableOrderedChannel *)channel; if (SEQUENCE_NUMBER_GT(message->header.id, unreliable_ordered_channel->last_received_message_id)) { - NBN_MessageSlot *slot = &channel->recved_message_slot_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; + NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[ + message->header.id % NBN_CHANNEL_BUFFER_SIZE]; - memcpy(&slot->message, message, sizeof(NBN_Message)); - - slot->free = false; + inc_msg->message = *message; + inc_msg->free = false; unreliable_ordered_channel->last_received_message_id = message->header.id; + NBN_LogTrace("Add incomoing message %d of type %d to unreliable channel %d (last received msg id: %d)", + message->header.id, message->header.type, channel->id, unreliable_ordered_channel->last_received_message_id); + return true; } @@ -2633,12 +2710,21 @@ static bool UnreliableOrderedChannel_AddReceivedMessage(NBN_Channel *channel, NB static bool UnreliableOrderedChannel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *message) { uint16_t msg_id = channel->next_outgoing_message_id; - NBN_MessageSlot *slot = &channel->outgoing_message_slot_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; + NBN_OutgoingMessage *out_msg = + &channel->outgoing_messages_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; - memcpy(&slot->message, message, sizeof(NBN_Message)); + // make sure this buffer slot is not already in use + if (out_msg->message) + { + NBN_LogError("No outgoing message available in unreliable channel %d (outgoing message count: %d)", + channel->id, channel->outgoing_message_count); + + return false; + } - slot->message.header.id = msg_id; - slot->free = false; + out_msg->id = msg_id; + out_msg->message = message; + out_msg->last_send_time = -1; channel->next_outgoing_message_id++; channel->outgoing_message_count++; @@ -2652,13 +2738,15 @@ static NBN_Message *UnreliableOrderedChannel_GetNextRecvedMessage(NBN_Channel *c while (SEQUENCE_NUMBER_LT(channel->next_recv_message_id, unreliable_ordered_channel->last_received_message_id)) { - NBN_MessageSlot *slot = &channel->recved_message_slot_buffer[channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; + NBN_IncomingMessage *inc_msg= &channel->incoming_messages_buffer[ + channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; - if (!slot->free && slot->message.header.id == channel->next_recv_message_id) + if (!inc_msg->free && + inc_msg->message.header.id == channel->next_recv_message_id) { - slot->free = true; + inc_msg->free = true; - return &slot->message; + return &inc_msg->message; } channel->next_recv_message_id++; @@ -2667,45 +2755,45 @@ static NBN_Message *UnreliableOrderedChannel_GetNextRecvedMessage(NBN_Channel *c return NULL; } -static NBN_Message *UnreliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *channel, double time) +static bool UnreliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *channel, + NBN_OutgoingMessage *res_out_msg, double time) { (void)time; NBN_UnreliableOrderedChannel *unreliable_ordered_channel = (NBN_UnreliableOrderedChannel *)channel; + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[ + unreliable_ordered_channel->next_outgoing_message_slot]; - NBN_MessageSlot *slot = &channel->outgoing_message_slot_buffer[unreliable_ordered_channel->next_outgoing_message_slot]; - - if (slot->free) - return NULL; - - slot->free = true; + if (out_msg->message == NULL) return false; - unreliable_ordered_channel->next_outgoing_message_slot = - (unreliable_ordered_channel->next_outgoing_message_slot + 1) % NBN_CHANNEL_BUFFER_SIZE; + *res_out_msg = *out_msg; + out_msg->message = NULL; + unreliable_ordered_channel->next_outgoing_message_slot++; + unreliable_ordered_channel->next_outgoing_message_slot %= NBN_CHANNEL_BUFFER_SIZE; - return &slot->message; + return true; } -static int UnreliableOrderedChannel_OnMessageSent(NBN_Channel *channel, NBN_Message *message) +static int UnreliableOrderedChannel_OnMessageSent(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) { - Connection_RecycleMessage(channel, message); + Connection_ReleaseMessage(endpoint, channel, message); + channel->outgoing_message_count--; return 0; } /* Reliable ordered */ -static bool ReliableOrderedChannel_AddReceivedMessage(NBN_Channel *, NBN_Message *); +static bool ReliableOrderedChannel_AddReceivedMessage(NBN_Endpoint *, NBN_Channel *, NBN_Message *); static bool ReliableOrderedChannel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *); static NBN_Message *ReliableOrderedChannel_GetNextRecvedMessage(NBN_Channel *); -static NBN_Message *ReliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *, double); -static int ReliableOrderedChannel_OnOutgoingMessageAcked(NBN_Channel *, uint16_t); +static bool ReliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *, NBN_OutgoingMessage *, double); +static int ReliableOrderedChannel_OnOutgoingMessageAcked(NBN_Endpoint *, NBN_Channel *, uint16_t); NBN_ReliableOrderedChannel *NBN_ReliableOrderedChannel_Create(void) { NBN_ReliableOrderedChannel *channel = (NBN_ReliableOrderedChannel *)NBN_Allocator(sizeof(NBN_ReliableOrderedChannel)); - channel->base.next_outgoing_message_pool_slot = 0; channel->base.AddReceivedMessage = ReliableOrderedChannel_AddReceivedMessage; channel->base.AddOutgoingMessage = ReliableOrderedChannel_AddOutgoingMessage; channel->base.GetNextRecvedMessage = ReliableOrderedChannel_GetNextRecvedMessage; @@ -2713,8 +2801,7 @@ NBN_ReliableOrderedChannel *NBN_ReliableOrderedChannel_Create(void) channel->base.OnOutgoingMessageAcked = ReliableOrderedChannel_OnOutgoingMessageAcked; channel->base.OnOutgoingMessageSent = NULL; - memset(channel->base.recved_message_slot_buffer, 0, sizeof(channel->base.recved_message_slot_buffer)); - memset(channel->base.outgoing_message_pool, 0, sizeof(channel->base.outgoing_message_pool)); + memset(channel->base.incoming_messages_buffer, 0, sizeof(channel->base.incoming_messages_buffer)); for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) channel->ack_buffer[i] = false; @@ -2733,18 +2820,18 @@ static unsigned int ReliableOrderedChannel_ComputeMessageIdDelta(uint16_t id1, u return (id2 >= id1) ? id2 - id1 : (((0xFFFF + 1) - id1) + id2) % 0xFFFF; } -static bool ReliableOrderedChannel_AddReceivedMessage(NBN_Channel *channel, NBN_Message *message) +static bool ReliableOrderedChannel_AddReceivedMessage(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) { NBN_ReliableOrderedChannel *reliable_ordered_channel = (NBN_ReliableOrderedChannel *)channel; unsigned int dt = ReliableOrderedChannel_ComputeMessageIdDelta( message->header.id, reliable_ordered_channel->most_recent_message_id); - NBN_LogTrace("Add recved message %d of type %d to channel %d (most recent msg id: %d, dt: %d)", + NBN_LogTrace("Add incomoing message %d of type %d to reliable channel %d (most recent msg id: %d, dt: %d)", message->header.id, message->header.type, channel->id, reliable_ordered_channel->most_recent_message_id, dt); if (SEQUENCE_NUMBER_GT(message->header.id, reliable_ordered_channel->most_recent_message_id)) { - assert(dt < NBN_CHANNEL_BUFFER_SIZE); + NBN_Assert(dt < NBN_CHANNEL_BUFFER_SIZE); reliable_ordered_channel->most_recent_message_id = message->header.id; } @@ -2752,20 +2839,19 @@ static bool ReliableOrderedChannel_AddReceivedMessage(NBN_Channel *channel, NBN_ { /* This is an old message that has already been received, probably coming from an out of order late packet. */ - if (dt >= NBN_CHANNEL_BUFFER_SIZE) - return false; + if (dt >= NBN_CHANNEL_BUFFER_SIZE) return false; } - NBN_MessageSlot *slot = &channel->recved_message_slot_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; + NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[ + message->header.id % NBN_CHANNEL_BUFFER_SIZE]; - if (!slot->free) + if (!inc_msg->free) { - Connection_RecycleMessage(channel, &slot->message); + Connection_ReleaseMessage(endpoint, channel, &inc_msg->message); } - memcpy(&slot->message, message, sizeof(NBN_Message)); - - slot->free = false; + inc_msg->message = *message; + inc_msg->free = false; return true; } @@ -2774,47 +2860,48 @@ static bool ReliableOrderedChannel_AddOutgoingMessage(NBN_Channel *channel, NBN_ { uint16_t msg_id = channel->next_outgoing_message_id; int index = msg_id % NBN_CHANNEL_BUFFER_SIZE; - NBN_MessageSlot *slot = &channel->outgoing_message_slot_buffer[index]; + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[index]; - if (!slot->free) + // make sure the outgoing message is not already in use + if (out_msg->message) { - NBN_LogTrace("Slot %d is not free on channel %d (slot msg id: %d, outgoing message count: %d)", - index, channel->id, slot->message.header.id, channel->outgoing_message_count); + NBN_LogError("No outgoing message available in reliable channel %d (outgoing message count: %d)", + channel->id, channel->outgoing_message_count); return false; } - memcpy(&slot->message, message, sizeof(NBN_Message)); - - slot->message.header.id = msg_id; - slot->last_send_time = -1; - slot->free = false; + out_msg->message = message; + out_msg->id = msg_id; + out_msg->last_send_time = -1; channel->next_outgoing_message_id++; channel->outgoing_message_count++; NBN_LogTrace("Added outgoing message %d of type %d to channel %d (index: %d)", - slot->message.header.id, slot->message.header.type, channel->id, index); + msg_id, message->header.type, channel->id, index); return true; } static NBN_Message *ReliableOrderedChannel_GetNextRecvedMessage(NBN_Channel *channel) { - NBN_MessageSlot *slot = &channel->recved_message_slot_buffer[channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; + NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[ + channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; - if (!slot->free && slot->message.header.id == channel->next_recv_message_id) + if (!inc_msg->free && inc_msg->message.header.id == channel->next_recv_message_id) { - slot->free = true; + inc_msg->free = true; channel->next_recv_message_id++; - return &slot->message; + return &inc_msg->message; } return NULL; } -static NBN_Message *ReliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *channel, double time) +static bool ReliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *channel, + NBN_OutgoingMessage *res_out_msg, double time) { NBN_ReliableOrderedChannel *reliable_ordered_channel = (NBN_ReliableOrderedChannel *)channel; @@ -2827,35 +2914,39 @@ static NBN_Message *ReliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *c while (SEQUENCE_NUMBER_LT(msg_id, max_message_id)) { - NBN_MessageSlot *slot = &channel->outgoing_message_slot_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; if ( - !slot->free && - (slot->last_send_time < 0 || time - slot->last_send_time >= NBN_MESSAGE_RESEND_DELAY)) + out_msg->message && + (out_msg->last_send_time < 0 || time - out_msg->last_send_time >= NBN_MESSAGE_RESEND_DELAY)) { - return &slot->message; + *res_out_msg = *out_msg; + return true; } msg_id++; } - return NULL; + return false; } -static int ReliableOrderedChannel_OnOutgoingMessageAcked(NBN_Channel *channel, uint16_t msg_id) +static int ReliableOrderedChannel_OnOutgoingMessageAcked(NBN_Endpoint *endpoint, NBN_Channel *channel, uint16_t msg_id) { NBN_ReliableOrderedChannel *reliable_ordered_channel = (NBN_ReliableOrderedChannel *)channel; - NBN_MessageSlot *slot = &reliable_ordered_channel->base.outgoing_message_slot_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; + int index = msg_id % NBN_CHANNEL_BUFFER_SIZE; + NBN_OutgoingMessage *out_msg = &reliable_ordered_channel-> + base.outgoing_messages_buffer[index]; - if (slot->free || slot->message.header.id != msg_id) + if (out_msg->message == NULL || out_msg->id != msg_id) return 0; - slot->free = true; + NBN_Message *message = out_msg->message; + out_msg->message = NULL; NBN_LogTrace("Message %d acked on channel %d (buffer index: %d, oldest unacked: %d)", - msg_id, channel->id, msg_id % NBN_CHANNEL_BUFFER_SIZE, reliable_ordered_channel->oldest_unacked_message_id); + msg_id, channel->id, index, reliable_ordered_channel->oldest_unacked_message_id); - reliable_ordered_channel->ack_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE] = true; + reliable_ordered_channel->ack_buffer[index] = true; channel->outgoing_message_count--; if (msg_id == reliable_ordered_channel->oldest_unacked_message_id) @@ -2880,7 +2971,7 @@ static int ReliableOrderedChannel_OnOutgoingMessageAcked(NBN_Channel *channel, u channel->id, reliable_ordered_channel->oldest_unacked_message_id); } - Connection_RecycleMessage(channel, &slot->message); + Connection_ReleaseMessage(endpoint, channel, message); return 0; } @@ -2930,7 +3021,7 @@ bool NBN_EventQueue_IsEmpty(NBN_EventQueue *event_queue) #pragma region NBN_Endpoint -static void Endpoint_Init(NBN_Endpoint *, uint32_t, bool); +static void Endpoint_Init(NBN_Endpoint *, NBN_UserMessageDef[NBN_USER_MESSAGE_TYPE_RANGE + 1], uint32_t, bool); static void Endpoint_Deinit(NBN_Endpoint *); static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, uint32_t, int, void *); static uint32_t Endpoint_BuildProtocolId(const char *); @@ -2940,12 +3031,16 @@ static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN // static int Endpoint_SplitMessageIntoChunks(NBN_Message *, NBN_OutgoingMessage *, NBN_Channel *, NBN_MessageSerializer, unsigned int, NBN_MessageChunk **); static void Endpoint_UpdateTime(NBN_Endpoint *); -static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_server) +static void Endpoint_Init(NBN_Endpoint *endpoint, + NBN_UserMessageDef user_message_defs[NBN_USER_MESSAGE_TYPE_RANGE + 1], + uint32_t protocol_id, bool is_server) { MemoryManager_Init(); endpoint->is_server = is_server; endpoint->protocol_id = protocol_id; + endpoint->write_message = NULL; + endpoint->write_message_buffer_len = 0; for (int i = 0; i < NBN_MAX_CHANNELS; i++) { @@ -2958,14 +3053,40 @@ static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_ /* Register library reserved reliable channel */ Endpoint_RegisterChannel(endpoint, NBN_CHANNEL_RESERVED_UNRELIABLE, (NBN_ChannelBuilder)NBN_UnreliableOrderedChannel_Create, (NBN_ChannelDestructor)NBN_Channel_Destroy); Endpoint_RegisterChannel(endpoint, NBN_CHANNEL_RESERVED_RELIABLE, (NBN_ChannelBuilder)NBN_ReliableOrderedChannel_Create, (NBN_ChannelDestructor)NBN_Channel_Destroy); - Endpoint_RegisterChannel(endpoint, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, (NBN_ChannelBuilder)NBN_ReliableOrderedChannel_Create, (NBN_ChannelDestructor)NBN_Channel_Destroy); - // TODO: don't need to create that many channels by default - for (int i = 0; i < NBN_MAX_CHANNELS - NBN_LIBRARY_RESERVED_CHANNELS; i++) + // create message pools for the library messages + endpoint->msg_buffer_pools[NBN_CLIENT_CLOSED_MESSAGE_TYPE] = NBN_Allocator(sizeof(NBN_MemPool)); + endpoint->msg_buffer_pools[NBN_CLIENT_ACCEPTED_MESSAGE_TYPE] = NBN_Allocator(sizeof(NBN_MemPool)); + endpoint->msg_buffer_pools[NBN_CONNECTION_REQUEST_MESSAGE_TYPE] = NBN_Allocator(sizeof(NBN_MemPool)); + + // TODO: use macro for pool block sizes + MemPool_Init(endpoint->msg_buffer_pools[NBN_CLIENT_CLOSED_MESSAGE_TYPE], 4, + NBN_LIBRARY_MESSAGE_POOL_INITIAL_SIZE); + MemPool_Init(endpoint->msg_buffer_pools[NBN_CLIENT_ACCEPTED_MESSAGE_TYPE], + NBN_SERVER_DATA_MAX_SIZE + 4, NBN_LIBRARY_MESSAGE_POOL_INITIAL_SIZE); + MemPool_Init(endpoint->msg_buffer_pools[NBN_CONNECTION_REQUEST_MESSAGE_TYPE], + NBN_CONNECTION_DATA_MAX_SIZE + 4, NBN_LIBRARY_MESSAGE_POOL_INITIAL_SIZE); + + // create message pools for the user-defined messages + for (int i = 0; i < NBN_USER_MESSAGE_TYPE_RANGE + 1; i++) { - Endpoint_RegisterChannel(endpoint, i, (NBN_ChannelBuilder)NBN_ReliableOrderedChannel_Create, (NBN_ChannelDestructor)NBN_Channel_Destroy); + NBN_UserMessageDef def = user_message_defs[i]; + + if (def.type >= 0) + { + // TODO: define initial count from config? + endpoint->msg_buffer_pools[i] = NBN_Allocator(sizeof(NBN_MemPool)); + MemPool_Init(endpoint->msg_buffer_pools[i], def.max_length, 32); + } + else + { + endpoint->msg_buffer_pools[i] = NULL; + } } + endpoint->outgoing_msg_pool = NBN_Allocator(sizeof(NBN_MemPool)); + MemPool_Init(endpoint->outgoing_msg_pool, sizeof(NBN_Message), 32); // TODO: macro for initial pool size + #ifdef NBN_DEBUG endpoint->OnMessageAddedToRecvQueue = NULL; #endif @@ -2986,6 +3107,20 @@ static void Endpoint_Deinit(NBN_Endpoint *endpoint) NBN_PacketSimulator_Stop(&endpoint->packet_simulator); #endif + for (int i = 0; i < NBN_USER_MESSAGE_TYPE_RANGE + 1; i++) + { + if (endpoint->msg_buffer_pools[i]) + { + NBN_MemPool *pool = endpoint->msg_buffer_pools[i]; + + MemPool_Deinit(pool); + NBN_Deallocator(pool); + } + } + + MemPool_Deinit(endpoint->outgoing_msg_pool); + NBN_Deallocator(endpoint->outgoing_msg_pool); + MemoryManager_Deinit(); } @@ -2993,10 +3128,11 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_ { NBN_Driver *driver = &nbn_drivers[driver_id]; - assert(driver->id >= 0); + NBN_Assert(driver->id >= 0); NBN_Connection *connection = NBN_Connection_Create(id, endpoint, driver, driver_data); + // TODO: don't, remove this builder thing, we only have unreliable and reliable channels for (unsigned int chan_id = 0; chan_id < NBN_MAX_CHANNELS; chan_id++) { NBN_ChannelBuilder builder = endpoint->channel_builders[chan_id]; @@ -3005,11 +3141,11 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_ NBN_ChannelDestructor destructor = endpoint->channel_destructors[chan_id]; - assert(destructor); + NBN_Assert(destructor); NBN_Channel *channel = builder(); - assert(channel); + NBN_Assert(channel); channel->id = chan_id; channel->destructor = destructor; @@ -3017,7 +3153,7 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_ connection->channels[chan_id] = channel; NBN_Connection_InitChannel(connection, channel); - } + } return connection; } @@ -3054,7 +3190,7 @@ static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *pa NBN_LogTrace("Received packet %d (conn id: %d, ack: %d, messages count: %d)", packet->header.seq_number, connection->id, packet->header.ack, packet->header.messages_count); - if (NBN_Connection_ProcessReceivedPacket(connection, packet, endpoint->time) < 0) + if (NBN_Connection_ProcessReceivedPacket(endpoint, connection, packet, endpoint->time) < 0) return NBN_ERROR; connection->last_recv_packet_time = endpoint->time; @@ -3063,28 +3199,31 @@ static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *pa return 0; } -static NBN_Message *Endpoint_CreateOutgoingMessage( - NBN_Endpoint *endpoint, NBN_Channel *channel, uint8_t type, uint8_t *data, uint16_t length) +static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, uint8_t type, uint8_t channel_id) { - NBN_Assert(channel); - - NBN_Message *message = &channel->outgoing_message_pool[channel->next_outgoing_message_pool_slot]; - - if (message->ref_count != 0) - { - NBN_LogError("Outgoing message pool has run out of space"); + NBN_Message *message = (NBN_Message *)MemPool_Alloc(endpoint->outgoing_msg_pool); + NBN_Assert(message != NULL); + NBN_Assert(message->ref_count == 0); - return NULL; - } - - message->header = (NBN_MessageHeader){-1, length, type, channel->id}; + message->header = (NBN_MessageHeader){-1, 0, type, channel_id}; message->sender = NULL; - message->data = data; + message->type = NBN_OUTGOING_MESSAGE; + message->data = NULL; message->ref_count = 0; - channel->next_outgoing_message_pool_slot = (channel->next_outgoing_message_pool_slot + 1) % NBN_CHANNEL_OUTGOING_MESSAGE_POOL_SIZE; + endpoint->write_message_buffer_len = 0; + endpoint->message_writer.position = 0; + + NBN_MemPool *msg_buffer_pool = endpoint->msg_buffer_pools[type]; - return message; + // message with no data have no buffer pool + if (msg_buffer_pool) + { + message->data = MemPool_Alloc(msg_buffer_pool); + endpoint->write_message_buffer_len = msg_buffer_pool->block_size; + } + + endpoint->write_message = message; } static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Message *message) @@ -3101,7 +3240,7 @@ static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connectio /*NBN_MessageChunk *chunks[NBN_CHANNEL_CHUNKS_BUFFER_SIZE]; int chunk_count = Endpoint_SplitMessageIntoChunks(&message, outgoing_msg, channel, msg_serializer, message_size, chunks); - assert(chunk_count <= NBN_CHANNEL_CHUNKS_BUFFER_SIZE); + NBN_Assert(chunk_count <= NBN_CHANNEL_CHUNKS_BUFFER_SIZE); if (chunk_count < 0) { @@ -3110,7 +3249,7 @@ static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connectio return NBN_ERROR; } - assert(outgoing_msg->ref_count == 0); + NBN_Assert(outgoing_msg->ref_count == 0); outgoing_msg->ref_count = chunk_count; NBN_Channel *channel = connection->channels[channel_id]; @@ -3131,13 +3270,11 @@ static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connectio }*/ // TODO: fix chunks - assert(false); + NBN_Assert(false); } else { - assert(message->ref_count == 0); - - message->ref_count = 1; + message->ref_count++; NBN_LogTrace("Enqueue message of type %d on channel %d", message->header.type, channel->id); @@ -3197,7 +3334,7 @@ static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connectio unsigned int offset = i * NBN_MESSAGE_CHUNK_SIZE; unsigned int chunk_size = MIN(NBN_MESSAGE_CHUNK_SIZE, message_size - offset); - assert(chunk_size <= NBN_MESSAGE_CHUNK_SIZE); + NBN_Assert(chunk_size <= NBN_MESSAGE_CHUNK_SIZE); memcpy(chunk->data, channel->write_chunk_buffer + offset, chunk_size); @@ -3239,12 +3376,12 @@ static int ServerDriver_OnClientPacketReceived(NBN_Packet *); void NBN_Driver_Register(int id, const char *name, NBN_DriverImplementation implementation) { // driver id must be valid - assert(id >= 0 && id < NBN_MAX_DRIVERS); + NBN_Assert(id >= 0 && id < NBN_MAX_DRIVERS); NBN_Driver *driver = &nbn_drivers[id]; // driver id must be unique - assert(driver->id == -1); + NBN_Assert(driver->id == -1); driver->id = id; driver->name = name; @@ -3283,12 +3420,38 @@ static int GameClient_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); static int GameClient_HandleEvent(void); static int GameClient_HandleMessageReceivedEvent(void); -int NBN_GameClient_Start(const char *protocol_name, const char *host, uint16_t port) +NBN_GameClient_Config NBN_GameClient_CreateConfig(const char *protocol_name, const char *host, uint16_t port) +{ + NBN_GameClient_Config config = {protocol_name, host, port}; + + for (int i = 0; i < NBN_USER_MESSAGE_TYPE_RANGE + 1; i++) + { + config.user_message_defs[i].type = -1; + } + + return config; +} + +void NBN_GameClient_RegisterMessageType(NBN_GameClient_Config *config, uint8_t type, unsigned int max_length) +{ + NBN_Assert(type >= 0 && type <= NBN_USER_MESSAGE_TYPE_RANGE); + // make sure we don't override an already defined message + NBN_Assert(config->user_message_defs[type].type < 0); + + config->user_message_defs[type] = (NBN_UserMessageDef){type, max_length}; +} + +NBN_Writer *NBN_GameClient_GetConnectionDataWriter(void) { - return NBN_GameClient_StartEx(protocol_name, host, port, NULL, 0); + NBN_Writer_Init( + &nbn_game_client.client_data_writer, + nbn_game_client.client_data_buffer, + sizeof(nbn_game_client.server_data_buffer)); + + return &nbn_game_client.client_data_writer; } -int NBN_GameClient_StartEx(const char *protocol_name, const char *host, uint16_t port, uint8_t *data, unsigned int length) +int NBN_GameClient_Start(NBN_GameClient_Config config) { if (nbn_driver_count < 1) { @@ -3296,9 +3459,12 @@ int NBN_GameClient_StartEx(const char *protocol_name, const char *host, uint16_t NBN_Abort(); } + const char *protocol_name = config.protocol_name; + const char *host = config.host; + uint16_t port = config.port; uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - Endpoint_Init(&nbn_game_client.endpoint, protocol_id, false); + Endpoint_Init(&nbn_game_client.endpoint, config.user_message_defs, protocol_id, false); nbn_game_client.server_connection = NULL; nbn_game_client.is_connected = false; @@ -3317,28 +3483,24 @@ int NBN_GameClient_StartEx(const char *protocol_name, const char *host, uint16_t } } - uint8_t *msg = NULL; - unsigned int msg_length = 0; - - if (data) - { - NBN_Assert(length > 0 && length <= NBN_CONNECTION_DATA_MAX_SIZE); + unsigned int connection_data_len = nbn_game_client.client_data_writer.position; - msg_length = length + 4; - msg = NBN_Allocator(msg_length); // TODO: pooling + NBN_GameClient_CreateReliableMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE); + NBN_Writer *writer = NBN_GameClient_GetMessageWriter(); - NBN_Writer writer; + if (connection_data_len > 0) + { + NBN_Assert(connection_data_len <= sizeof(nbn_game_client.client_data_buffer)); - NBN_Writer_Init(&writer, msg, msg_length); - NBN_Writer_WriteUInt32(&writer, msg_length); - NBN_Writer_WriteBytes(&writer, data, length); + NBN_Writer_WriteUInt32(writer, connection_data_len); + NBN_Writer_WriteBytes(writer, nbn_game_client.client_data_buffer, connection_data_len); + } + else + { + NBN_Writer_WriteUInt32(writer, 0); } - if (NBN_GameClient_SendMessage( - NBN_CONNECTION_REQUEST_MESSAGE_TYPE, - NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, - msg, - msg_length) < 0) + if (NBN_GameClient_SendMessage() < 0) return NBN_ERROR; NBN_LogInfo("Started"); @@ -3357,7 +3519,9 @@ void NBN_GameClient_Stop(void) { NBN_LogInfo("Disconnecting..."); - if (NBN_GameClient_SendMessage(NBN_DISCONNECTION_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, NULL, 0) < 0) + NBN_GameClient_CreateReliableMessage(NBN_DISCONNECTION_MESSAGE_TYPE); + + if (NBN_GameClient_SendMessage() < 0) { NBN_LogError("Failed to send disconnection message"); } @@ -3372,7 +3536,7 @@ void NBN_GameClient_Stop(void) NBN_LogInfo("Disconnected"); } - NBN_Connection_Destroy(nbn_game_client.server_connection); + NBN_Connection_Destroy(&nbn_game_client.endpoint, nbn_game_client.server_connection); nbn_game_client.server_connection = NULL; } @@ -3391,17 +3555,18 @@ void NBN_GameClient_Stop(void) nbn_game_client.closed_code = -1; nbn_game_client.server_data_len = 0; - memset(nbn_game_client.server_data, 0, sizeof(nbn_game_client.server_data)); Endpoint_Deinit(&nbn_game_client.endpoint); NBN_LogInfo("Stopped"); } -unsigned int NBN_GameClient_ReadServerData(uint8_t *data) +NBN_Reader *NBN_GameClient_GetServerDataReader(void) { - memcpy(data, nbn_game_client.server_data, nbn_game_client.server_data_len); + NBN_Reader_Init(&nbn_game_client.server_data_reader, + nbn_game_client.server_data_buffer, + nbn_game_client.server_data_len); - return nbn_game_client.server_data_len; + return &nbn_game_client.server_data_reader; } int NBN_GameClient_Poll(void) @@ -3479,41 +3644,78 @@ int NBN_GameClient_Poll(void) int NBN_GameClient_SendPackets(void) { return NBN_Connection_FlushChannels( + &nbn_game_client.endpoint, nbn_game_client.server_connection, nbn_game_client.endpoint.protocol_id, nbn_game_client.endpoint.time); } -int NBN_GameClient_SendMessage(uint8_t type, uint8_t channel_id, uint8_t *data, uint16_t length) +void NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id) { - NBN_Message *message = Endpoint_CreateOutgoingMessage( - &nbn_game_client.endpoint, - nbn_game_client.server_connection->channels[channel_id], - type, - data, - length); + NBN_Endpoint *endpoint = &nbn_game_client.endpoint; + NBN_Assert(endpoint->write_message == NULL); - NBN_Assert(message); + Endpoint_CreateOutgoingMessage(endpoint, type, channel_id); +} - if (Endpoint_EnqueueOutgoingMessage(&nbn_game_client.endpoint, nbn_game_client.server_connection, message) < 0) +void NBN_GameClient_CreateUnreliableMessage(uint8_t type) +{ + NBN_GameClient_CreateMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE); +} + +void NBN_GameClient_CreateReliableMessage(uint8_t type) +{ + NBN_GameClient_CreateMessage(type, NBN_CHANNEL_RESERVED_RELIABLE); +} + +int NBN_GameClient_SendMessage(void) +{ + NBN_Endpoint *endpoint = &nbn_game_client.endpoint; + NBN_Message *message = endpoint->write_message; + NBN_Assert(message != NULL); + NBN_Assert(message->ref_count == 0); + + message->header.length = endpoint->message_writer.position; + + if (Endpoint_EnqueueOutgoingMessage(endpoint, nbn_game_client.server_connection, message) < 0) { NBN_LogError("Failed to create outgoing message"); return NBN_ERROR; } + endpoint->write_message = NULL; + return 0; } - -int NBN_GameClient_SendUnreliableMessage(uint8_t type, uint8_t *data, uint16_t length) +NBN_Writer *NBN_GameClient_GetMessageWriter(void) { - return NBN_GameClient_SendMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE, data, length); + NBN_Endpoint *endpoint = &nbn_game_client.endpoint; + + NBN_Assert(endpoint->write_message != NULL); + NBN_Assert(endpoint->write_message_buffer_len > 0); + + NBN_Writer *writer = &endpoint->message_writer; + + NBN_Writer_Init(writer, endpoint->write_message->data, + endpoint->write_message_buffer_len); + + return writer; } -int NBN_GameClient_SendReliableMessage(uint8_t type, uint8_t *data, uint16_t length) +NBN_Reader *NBN_GameClient_GetMessageReader(void) { - return NBN_GameClient_SendMessage(type, NBN_CHANNEL_RESERVED_RELIABLE, data, length); + NBN_Assert(nbn_game_client.last_event.type == NBN_MESSAGE_RECEIVED); + + NBN_MessageInfo msg_info = nbn_game_client.last_event.data.message_info; + NBN_Assert(msg_info.length > 0 && msg_info.data != NULL); + + NBN_Reader *reader = &nbn_game_client.endpoint.message_reader; + + NBN_Reader_Init(reader, msg_info.data, msg_info.length); + + return reader; } NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *driver_data) @@ -3567,13 +3769,14 @@ void NBN_GameClient_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, static int GameClient_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *server_connection) { - assert(nbn_game_client.server_connection == server_connection); + NBN_Assert(nbn_game_client.server_connection == server_connection); NBN_Event ev; ev.type = NBN_MESSAGE_RECEIVED; - if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) + // if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) + if (false) // TODO: { /*NBN_Channel *channel = server_connection->channels[message->header.channel_id];*/ /**/ @@ -3627,12 +3830,9 @@ static int GameClient_HandleMessageReceivedEvent(void) if (message_info.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE) { nbn_game_client.is_connected = false; + NBN_Reader *reader = NBN_GameClient_GetMessageReader(); - NBN_Reader reader; - - NBN_Reader_Init(&reader, message_info.data, message_info.length); - - if (NBN_Reader_ReadInt32(&reader, &nbn_game_client.closed_code) < 0) + if (NBN_Reader_ReadInt32(reader, &nbn_game_client.closed_code) < 0) { NBN_LogError("Failed to read code from client closed message"); @@ -3643,28 +3843,41 @@ static int GameClient_HandleMessageReceivedEvent(void) } else if (message_info.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE) { - int length = message_info.length; + if (message_info.length < 4) + { + NBN_LogError("Accept message invalid length"); + + return NBN_ERROR; + } + + NBN_Reader *reader = NBN_GameClient_GetMessageReader(); + unsigned int data_length; + + if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) + { + NBN_LogError("Failed to read client data length"); + + return NBN_ERROR; + } - if (length > 0) + if (data_length > 0) { - if (length > NBN_SERVER_DATA_MAX_SIZE) + if (data_length > sizeof(nbn_game_client.server_data_buffer)) { NBN_LogError("Received an invalid client accepted message"); return NBN_ERROR; } - NBN_Reader reader; - - NBN_Reader_Init(&reader, message_info.data, length); - - if (NBN_Reader_ReadBytes(&reader, nbn_game_client.server_data, length) < 0) + if (NBN_Reader_ReadBytes(reader, nbn_game_client.server_data_buffer, data_length) < 0) { + NBN_LogError("Failed to read server data"); + return NBN_ERROR; } } - nbn_game_client.server_data_len = length; + nbn_game_client.server_data_len = data_length; nbn_game_client.is_connected = true; ret = NBN_CONNECTED; } @@ -3696,7 +3909,7 @@ static void ClientDriver_OnPacketReceived(NBN_Packet *packet) NBN_GameServer nbn_game_server; -static int GameServer_SendMessageTo(NBN_Connection *client, uint8_t msg_type, uint8_t channel_id, uint8_t *data, uint16_t length); +static int GameServer_SendMessageTo(NBN_Connection *client, NBN_Message *message); static int GameServer_AddClient(NBN_Connection *); static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection); static void GameServer_AddClientToClosedList(NBN_Connection *client); @@ -3707,12 +3920,29 @@ static void GameServer_RemoveClosedClientConnections(void); static int GameServer_HandleEvent(void); static int GameServer_HandleMessageReceivedEvent(void); -int NBN_GameServer_Start(const char *protocol_name, uint16_t port) +NBN_GameServer_Config NBN_GameServer_CreateConfig(const char *protocol_name, uint16_t port) { - return NBN_GameServer_StartEx(protocol_name, port); + NBN_GameServer_Config config = {protocol_name, port}; + + for (int i = 0; i < NBN_USER_MESSAGE_TYPE_RANGE + 1; i++) + { + config.user_message_defs[i].type = -1; + } + + return config; } -int NBN_GameServer_StartEx(const char *protocol_name, uint16_t port) +void NBN_GameServer_RegisterMessageType(NBN_GameServer_Config *config, uint8_t type, unsigned int max_length) +{ + // TODO: maybe find a way to remove code duplication with NBN_GameClient_RegisterMessageType + NBN_Assert(type >= 0 && type <= NBN_USER_MESSAGE_TYPE_RANGE); + // make sure we don't override an already defined message + NBN_Assert(config->user_message_defs[type].type < 0); + + config->user_message_defs[type] = (NBN_UserMessageDef){type, max_length}; +} + +int NBN_GameServer_Start(NBN_GameServer_Config config) { if (nbn_driver_count < 1) { @@ -3720,9 +3950,11 @@ int NBN_GameServer_StartEx(const char *protocol_name, uint16_t port) NBN_Abort(); } + const char *protocol_name = config.protocol_name; + uint16_t port = config.port; uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - Endpoint_Init(&nbn_game_server.endpoint, protocol_id, true); + Endpoint_Init(&nbn_game_server.endpoint, config.user_message_defs, protocol_id, true); if ((nbn_game_server.clients = NBN_ConnectionVector_Create()) == NULL) { @@ -3730,6 +3962,8 @@ int NBN_GameServer_StartEx(const char *protocol_name, uint16_t port) NBN_Abort(); } + // TODO: get rid of the table? I think we only need it because we pass + // connection IDs to user code, could we just pass connection pointer? if ((nbn_game_server.clients_table = NBN_ConnectionTable_Create()) == NULL) { NBN_LogError("Failed to create connections table"); @@ -3763,7 +3997,7 @@ void NBN_GameServer_Stop(void) for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { - NBN_Connection_Destroy(nbn_game_server.clients->connections[i]); + NBN_Connection_Destroy(&nbn_game_server.endpoint, nbn_game_server.clients->connections[i]); } NBN_ConnectionVector_Destroy(nbn_game_server.clients); @@ -3874,11 +4108,12 @@ int NBN_GameServer_SendPackets(void) { NBN_Connection *client = nbn_game_server.clients->connections[i]; - assert(!(client->is_closed && client->is_stale)); + NBN_Assert(!(client->is_closed && client->is_stale)); if ( !client->is_stale && NBN_Connection_FlushChannels( + &nbn_game_server.endpoint, client, nbn_game_server.endpoint.protocol_id, nbn_game_server.endpoint.time) < 0 @@ -3894,7 +4129,7 @@ int NBN_GameServer_SendPackets(void) NBN_Connection *NBN_GameServer_CreateClientConnection(int driver_id, void *driver_data, uint32_t conn_id) { - assert(conn_id > 0); // Connection IDs start at 1 + NBN_Assert(conn_id > 0); // Connection IDs start at 1 NBN_Connection *client = Endpoint_CreateConnection(&nbn_game_server.endpoint, conn_id, driver_id, driver_data); @@ -3931,8 +4166,32 @@ int NBN_GameServer_CloseClient(NBN_ConnectionHandle connection_handle) return GameServer_CloseClientWithCode(client, -1, false); } -int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, uint8_t channel_id, uint8_t *data, uint16_t length) +void NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id) { + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + + Endpoint_CreateOutgoingMessage(endpoint, type, channel_id); +} + +void NBN_GameServer_CreateUnreliableMessage(uint8_t type) +{ + NBN_GameServer_CreateMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE); +} + +void NBN_GameServer_CreateReliableMessage(uint8_t type) +{ + NBN_GameServer_CreateMessage(type, NBN_CHANNEL_RESERVED_RELIABLE); +} + +int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle) +{ + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + NBN_Message *message = endpoint->write_message; + NBN_Assert(message != NULL); + + message->header.length = endpoint->message_writer.position; + + // TODO: remove that table NBN_Connection *client = NBN_ConnectionTable_Get(nbn_game_server.clients_table, connection_handle); if (client == NULL) @@ -3941,48 +4200,99 @@ int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle, uint8_t return 0; } - return GameServer_SendMessageTo(client, msg_type, channel_id, data, length); + return GameServer_SendMessageTo(client, message); } -int NBN_GameServer_SendUnreliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, uint8_t *data, uint16_t length) +int NBN_GameServer_BroadcastMessage(void) { - return NBN_GameServer_SendMessageTo(connection_handle, msg_type, NBN_CHANNEL_RESERVED_UNRELIABLE, data, length); + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + NBN_Message *message = endpoint->write_message; + NBN_Assert(message != NULL); + + message->header.length = endpoint->message_writer.position; + + for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) + { + NBN_Connection *conn = nbn_game_server.clients->connections[i]; + + if (!conn->is_accepted || conn->is_closed) continue; + + if (GameServer_SendMessageTo(conn, message) < 0) + { + NBN_LogError("Failed to send message to client %d when broadcasting", conn->id); + return -1; + } + } + + return 0; } -int NBN_GameServer_SendReliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, uint8_t *data, uint16_t length) +NBN_Writer *NBN_GameServer_GetMessageWriter(void) { - return NBN_GameServer_SendMessageTo(connection_handle, msg_type, NBN_CHANNEL_RESERVED_RELIABLE, data, length); + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + + NBN_Assert(endpoint->write_message != NULL); + NBN_Assert(endpoint->write_message_buffer_len > 0); + + NBN_Writer *writer = &endpoint->message_writer; + + NBN_Writer_Init(writer, endpoint->write_message->data, + endpoint->write_message_buffer_len); + + return writer; } -int NBN_GameServer_AcceptIncomingConnection(void) +NBN_Reader *NBN_GameServer_GetMessageReader(void) { - return NBN_GameServer_AcceptIncomingConnectionWithData(NULL, 0); + NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); + + NBN_MessageInfo msg_info = nbn_game_server.last_event.data.message_info; + NBN_Assert(msg_info.length > 0 && msg_info.data != NULL); + + NBN_Reader *reader = &nbn_game_server.endpoint.message_reader; + + NBN_Reader_Init(reader, msg_info.data, msg_info.length); + + return reader; } -int NBN_GameServer_AcceptIncomingConnectionWithData(uint8_t *data, unsigned int length) +NBN_Writer *NBN_GameServer_GetConnectionDataWriter(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); - NBN_Connection *client = nbn_game_server.last_event.data.connection; - uint8_t *msg = NULL; - unsigned int msg_length = 0; + NBN_Writer_Init( + &nbn_game_server.server_data_writer, + nbn_game_server.server_data_buffer, + sizeof(nbn_game_server.server_data_buffer)); - if (data) - { - NBN_Assert(length > 0); - NBN_Assert(length <= NBN_SERVER_DATA_MAX_SIZE); + return &nbn_game_server.server_data_writer; +} - msg_length = length; - msg = NBN_Allocator(msg_length); // TODO: pooling +int NBN_GameServer_AcceptIncomingConnection(void) +{ + NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); + NBN_Assert(nbn_game_server.last_event.data.connection != NULL); - NBN_Writer writer; + unsigned data_length = nbn_game_server.server_data_writer.position; - NBN_Writer_Init(&writer, msg, length); - NBN_Writer_WriteBytes(&writer, data, length); + NBN_Connection *client = nbn_game_server.last_event.data.connection; + NBN_GameServer_CreateReliableMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); + NBN_Writer *writer = NBN_GameServer_GetMessageWriter(); + + if (data_length > 0) + { + NBN_Assert(data_length <= NBN_SERVER_DATA_MAX_SIZE); + + NBN_Writer_WriteUInt32(writer, data_length); + NBN_Writer_WriteBytes(writer, nbn_game_server.server_data_buffer, data_length); + } + else + { + NBN_Writer_WriteUInt32(writer, 0); } - if (GameServer_SendMessageTo(client, NBN_CLIENT_ACCEPTED_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, msg, msg_length) < 0) + if (NBN_GameServer_SendMessageTo(client->id) < 0) return NBN_ERROR; client->is_accepted = true; @@ -3994,10 +4304,13 @@ int NBN_GameServer_AcceptIncomingConnectionWithData(uint8_t *data, unsigned int int NBN_GameServer_RejectIncomingConnectionWithCode(int code) { - assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); - assert(nbn_game_server.last_event.data.connection != NULL); + NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); + NBN_Assert(nbn_game_server.last_event.data.connection != NULL); + + NBN_Connection *conn = nbn_game_server.last_event.data.connection; + NBN_LogDebug("Rejecting incoming connection %d (code: %d)", conn->id, code); - return GameServer_CloseClientWithCode(nbn_game_server.last_event.data.connection, code, false); + return GameServer_CloseClientWithCode(conn, code, false); } int NBN_GameServer_RejectIncomingConnection(void) @@ -4007,31 +4320,33 @@ int NBN_GameServer_RejectIncomingConnection(void) NBN_ConnectionHandle NBN_GameServer_GetIncomingConnection(void) { - assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); - assert(nbn_game_server.last_event.data.connection != NULL); + NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); + NBN_Assert(nbn_game_server.last_event.data.connection != NULL); return nbn_game_server.last_event.data.connection->id; } -unsigned int NBN_GameServer_ReadIncomingConnectionData(uint8_t *data) +NBN_Reader *NBN_GameServer_GetConnectionDataReader(void) { - assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); + NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); - memcpy(data, nbn_game_server.last_connection_data, nbn_game_server.last_connection_data_len); + NBN_Reader_Init(&nbn_game_server.client_data_reader, + nbn_game_server.client_data_buffer, + nbn_game_server.client_data_len); - return nbn_game_server.last_connection_data_len; + return &nbn_game_server.client_data_reader; } NBN_ConnectionHandle NBN_GameServer_GetDisconnectedClient(void) { - assert(nbn_game_server.last_event.type == NBN_CLIENT_DISCONNECTED); + NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_DISCONNECTED); return nbn_game_server.last_event.data.connection_handle; } NBN_MessageInfo NBN_GameServer_GetMessageInfo(void) { - assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); + NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); return nbn_game_server.last_event.data.message_info; } @@ -4055,20 +4370,14 @@ void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, #endif /* NBN_DEBUG */ -static int GameServer_SendMessageTo(NBN_Connection *client, uint8_t type, uint8_t channel_id, uint8_t *data, uint16_t length) +static int GameServer_SendMessageTo(NBN_Connection *client, NBN_Message *message) { - NBN_Channel *channel = client->channels[channel_id]; - NBN_Message *message = Endpoint_CreateOutgoingMessage(&nbn_game_server.endpoint, channel, type, data, length); + NBN_Assert(message != NULL); - if (message == NULL) - { - NBN_LogError("Failed to create outgoing message"); + message->header.length = nbn_game_server.endpoint.message_writer.position; - return NBN_ERROR; - } - - /* The only message type we can send to an unaccepted client is a NBN_ClientAcceptedMessage message */ - NBN_Assert(client->is_accepted || message->header.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); + /* Only NBN_CLIENT_ACCEPTED_MESSAGE_TYPE and NBN_CLIENT_CLOSED_MESSAGE_TYPE messages can be sent to an unaccapted client */ + NBN_Assert(client->is_accepted || message->header.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); if (Endpoint_EnqueueOutgoingMessage(&nbn_game_server.endpoint, client, message) < 0) { @@ -4137,14 +4446,11 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool { NBN_LogDebug("Send close message for client %d (code: %d)", client->id, code); - unsigned int msg_length = 4; - uint8_t *msg = NBN_Allocator(msg_length); // TODO: pooling + NBN_GameServer_CreateReliableMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE); + NBN_Writer *writer = NBN_GameServer_GetMessageWriter(); - NBN_Writer writer; - - NBN_Writer_Init(&writer, msg, msg_length); - NBN_Writer_WriteInt32(&writer, code); - GameServer_SendMessageTo(client, NBN_CLIENT_CLOSED_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, msg, msg_length); + NBN_Writer_WriteInt32(writer, code); + NBN_GameServer_SendMessageTo(client->id); } return 0; @@ -4188,7 +4494,8 @@ static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio ev.type = NBN_CLIENT_MESSAGE_RECEIVED; - if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) + if (false) // TODO: + // if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) { /*NBN_Channel *channel = client->channels[message->header.channel_id];*/ /**/ @@ -4252,7 +4559,7 @@ static void GameServer_RemoveClosedClientConnections(void) NBN_ConnectionListNode *next = current->next; NBN_Connection *client = current->conn; - assert(client->id > 0); + NBN_Assert(client->id > 0); if (client->is_stale) { @@ -4280,7 +4587,7 @@ static void GameServer_RemoveClosedClientConnections(void) // Destroy the connection - NBN_Connection_Destroy(client); + NBN_Connection_Destroy(&nbn_game_server.endpoint, client); // Remove the connection from the closed clients list @@ -4318,9 +4625,9 @@ static int GameServer_HandleEvent(void) nbn_game_server.last_event.type; } +// TODO: big ass function static int GameServer_HandleMessageReceivedEvent(void) { - NBN_MessageInfo message_info = nbn_game_server.last_event.data.message_info; NBN_Connection *sender = NBN_ConnectionTable_Get(nbn_game_server.clients_table, message_info.sender); @@ -4359,40 +4666,42 @@ static int GameServer_HandleMessageReceivedEvent(void) // at this point we know it's a connection request NBN_Assert(message_info.type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE); - nbn_game_server.last_connection_data_len = 0; - memset(nbn_game_server.last_connection_data, 0, sizeof(nbn_game_server.last_connection_data)); - - if (message_info.length > 0) + if (message_info.length < 4) { - unsigned int data_length; - NBN_Reader reader; + NBN_LogError("Connection request invalid length"); - NBN_Reader_Init(&reader, message_info.data, message_info.length); + return NBN_ERROR; + } - if (NBN_Reader_ReadUInt32(&reader, &data_length) < 0) - { - NBN_LogError("Failed to read client data length"); + NBN_Reader *reader = NBN_GameServer_GetMessageReader(); + unsigned int data_length; - return NBN_ERROR; - } + if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) + { + NBN_LogError("Failed to read client data length"); + + return NBN_ERROR; + } - if (data_length > 0 && data_length <= NBN_CONNECTION_DATA_MAX_SIZE) + if (data_length > 0) + { + if (data_length > sizeof(nbn_game_server.client_data_buffer)) { NBN_LogError("Invalid client data length"); return NBN_ERROR; } - if (NBN_Reader_ReadBytes(&reader, nbn_game_server.last_connection_data, data_length) < 0) + if (NBN_Reader_ReadBytes(reader, nbn_game_server.client_data_buffer, data_length) < 0) { NBN_LogError("Failed to read client data"); return NBN_ERROR; } - - nbn_game_server.last_connection_data_len = data_length; } + nbn_game_server.client_data_len = data_length; + NBN_Event e; e.type = NBN_NEW_CONNECTION; From fd36320c89be58bbde534d1ac428fb3c642699e0 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sat, 1 Nov 2025 17:31:30 +0100 Subject: [PATCH 06/85] remove client table, fix msg id issue --- nbnet.h | 141 ++++++++++++++++++++------------------------------------ 1 file changed, 51 insertions(+), 90 deletions(-) diff --git a/nbnet.h b/nbnet.h index 862abae..0eb9ac7 100644 --- a/nbnet.h +++ b/nbnet.h @@ -105,7 +105,6 @@ typedef struct NBN_Endpoint NBN_Endpoint; typedef struct NBN_Connection NBN_Connection; typedef struct NBN_Channel NBN_Channel; typedef struct NBN_Driver NBN_Driver; -typedef uint32_t NBN_ConnectionHandle; #pragma region NBN_ConnectionVector @@ -244,7 +243,7 @@ typedef struct NBN_OutgoingMessage { NBN_Message *message; uint16_t id; - double last_send_time; // TODO: use float + double last_send_time; } NBN_OutgoingMessage; typedef struct NBN_IncomingMessage @@ -273,9 +272,9 @@ typedef struct NBN_MessageInfo /** * The message's sender. * - * On the client side, it will always be 0 (all received messages come from the game server). + * On the client side, it will always be NULL as all received messages come from the game server */ - NBN_ConnectionHandle sender; + NBN_Connection *sender; } NBN_MessageInfo; #pragma endregion /* NBN_Message */ @@ -407,6 +406,8 @@ int NBN_Channel_ReconstructMessageFromChunks(NBN_Channel *, NBN_Connection *, NB /*void NBN_Channel_ResizeWriteChunkBuffer(NBN_Channel *, unsigned int);*/ /*void NBN_Channel_ResizeReadChunkBuffer(NBN_Channel *, unsigned int);*/ +static void Channel_UpdateMessageSendTime(NBN_Channel *channel, uint16_t msg_id, double time); + /* Unreliable ordered @@ -552,7 +553,6 @@ typedef union NBN_EventData { NBN_MessageInfo message_info; NBN_Connection *connection; - NBN_ConnectionHandle connection_handle; } NBN_EventData; typedef struct NBN_Event @@ -899,7 +899,6 @@ typedef struct NBN_GameServer { NBN_Endpoint endpoint; NBN_ConnectionVector *clients; /* Vector of clients connections */ - NBN_ConnectionTable *clients_table; /* Hash table of clients connections */ NBN_ConnectionListNode *closed_clients_head; NBN_GameServerStats stats; NBN_Event last_event; @@ -966,11 +965,11 @@ NBN_Connection *NBN_GameServer_CreateClientConnection(int, void *, uint32_t); /** * Close a client's connection without a specific code (default code is -1) * - * @param connection_handle The connection to close + * @param conn The connection to close * * @return 0 when successful, -1 otherwise */ -int NBN_GameServer_CloseClient(NBN_ConnectionHandle connection_handle); +int NBN_GameServer_CloseClient(NBN_Connection *conn); /** * Close a client's connection with a specific code. @@ -978,11 +977,11 @@ int NBN_GameServer_CloseClient(NBN_ConnectionHandle connection_handle); * The code is an arbitrary integer to let the client knows * why his connection was closed. * - * @param connection_handle The connection to close + * @param conn The connection to close * * @return 0 when successful, -1 otherwise */ -int NBN_GameServer_CloseClientWithCode(NBN_ConnectionHandle connection_handle, int code); +int NBN_GameServer_CloseClientWithCode(NBN_Connection *conn, int code); // TODO: doc void NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id); @@ -991,7 +990,7 @@ void NBN_GameServer_CreateUnreliableMessage(uint8_t type); // TODO: doc void NBN_GameServer_CreateReliableMessage(uint8_t type); // TODO: doc -int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle); +int NBN_GameServer_SendMessageTo(NBN_Connection *conn); // TODO: doc int NBN_GameServer_BroadcastMessage(void); @@ -1032,21 +1031,21 @@ int NBN_GameServer_RejectIncomingConnection(void); * * Call this function after receiving a NBN_NEW_CONNECTION event. * - * @return A NBN_ConnectionHandle for the new connection + * @return A pointer to a NBN_Connection representing the new connection */ -NBN_ConnectionHandle NBN_GameServer_GetIncomingConnection(void); +NBN_Connection *NBN_GameServer_GetIncomingConnection(void); // TODO: doc NBN_Reader *NBN_GameServer_GetConnectionDataReader(void); /** - * Return the last disconnected client. + * Return the last disconnected client's connection. * - * Call this function after receiving a NBN_CLIENT_DISCONNECTED event. + * Call this function after receiving a NBN_CLIENT_DISCONNECTED event * - * @return The last disconnected connection + * @return the last disconnected client's connection */ -NBN_ConnectionHandle NBN_GameServer_GetDisconnectedClient(void); +NBN_Connection *NBN_GameServer_GetDisconnectedClient(void); /** * Retrieve the info about the last received message. @@ -1746,7 +1745,7 @@ int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_OutgoingMessage *out_msg) NBN_Message *message = out_msg->message; NBN_LogTrace("Write message %d (type: %d, length: %d) to packet %d", - message->header.id, message->header.type, message->header.length, + out_msg->id, message->header.type, message->header.length, packet->header.seq_number); NBN_Assert( @@ -2039,7 +2038,7 @@ int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connect ) { NBN_Message *message = out_msg.message; - uint8_t msg_id = out_msg.id; + uint16_t msg_id = out_msg.id; bool message_sent = false; int ret = NBN_Packet_WriteMessage(&packet, &out_msg); @@ -2086,8 +2085,7 @@ int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connect msg_id, packet.header.seq_number, message->header.length, message->header.type); - // TODO: this does not work, last_send_time needs to be updated in the channel buffer - out_msg.last_send_time = time; + Channel_UpdateMessageSendTime(channel, msg_id, time); packet_entry->messages[packet_entry->messages_count++] = (NBN_MessageEntry){ msg_id, channel->id @@ -2659,6 +2657,14 @@ int NBN_Channel_ReconstructMessageFromChunks(NBN_Channel *channel, NBN_Connectio /* channel->read_chunk_buffer_size = size;*/ /*}*/ +static void Channel_UpdateMessageSendTime(NBN_Channel *channel, uint16_t msg_id, double time) +{ + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; + NBN_Assert(msg_id == out_msg->id); + + out_msg->last_send_time = time; +} + /* Unreliable ordered */ static bool UnreliableOrderedChannel_AddReceivedMessage(NBN_Endpoint *, NBN_Channel *, NBN_Message *); @@ -2878,7 +2884,7 @@ static bool ReliableOrderedChannel_AddOutgoingMessage(NBN_Channel *channel, NBN_ channel->next_outgoing_message_id++; channel->outgoing_message_count++; - NBN_LogTrace("Added outgoing message %d of type %d to channel %d (index: %d)", + NBN_LogTrace("Added outgoing message %d of type %d to reliable channel %d (index: %d)", msg_id, message->header.type, channel->id, index); return true; @@ -3962,14 +3968,6 @@ int NBN_GameServer_Start(NBN_GameServer_Config config) NBN_Abort(); } - // TODO: get rid of the table? I think we only need it because we pass - // connection IDs to user code, could we just pass connection pointer? - if ((nbn_game_server.clients_table = NBN_ConnectionTable_Create()) == NULL) - { - NBN_LogError("Failed to create connections table"); - NBN_Abort(); - } - nbn_game_server.closed_clients_head = NULL; for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) @@ -4001,7 +3999,6 @@ void NBN_GameServer_Stop(void) } NBN_ConnectionVector_Destroy(nbn_game_server.clients); - NBN_ConnectionTable_Destroy(nbn_game_server.clients_table); for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) { @@ -4140,30 +4137,14 @@ NBN_Connection *NBN_GameServer_CreateClientConnection(int driver_id, void *drive return client; } -int NBN_GameServer_CloseClientWithCode(NBN_ConnectionHandle connection_handle, int code) +int NBN_GameServer_CloseClientWithCode(NBN_Connection *conn, int code) { - NBN_Connection *client = NBN_ConnectionTable_Get(nbn_game_server.clients_table, connection_handle); - - if (client == NULL) - { - NBN_LogError("Client %d does not exist", connection_handle); - return NBN_ERROR; - } - - return GameServer_CloseClientWithCode(client, code, false); + return GameServer_CloseClientWithCode(conn, code, false); } -int NBN_GameServer_CloseClient(NBN_ConnectionHandle connection_handle) +int NBN_GameServer_CloseClient(NBN_Connection *conn) { - NBN_Connection *client = NBN_ConnectionTable_Get(nbn_game_server.clients_table, connection_handle); - - if (client == NULL) - { - NBN_LogError("Client %d does not exist", connection_handle); - return NBN_ERROR; - } - - return GameServer_CloseClientWithCode(client, -1, false); + return GameServer_CloseClientWithCode(conn, -1, false); } void NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id) @@ -4183,7 +4164,7 @@ void NBN_GameServer_CreateReliableMessage(uint8_t type) NBN_GameServer_CreateMessage(type, NBN_CHANNEL_RESERVED_RELIABLE); } -int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle) +int NBN_GameServer_SendMessageTo(NBN_Connection *conn) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; NBN_Message *message = endpoint->write_message; @@ -4191,16 +4172,7 @@ int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle) message->header.length = endpoint->message_writer.position; - // TODO: remove that table - NBN_Connection *client = NBN_ConnectionTable_Get(nbn_game_server.clients_table, connection_handle); - - if (client == NULL) - { - NBN_LogWarning("Cannot send message to client %d (does not exist)", connection_handle); - return 0; - } - - return GameServer_SendMessageTo(client, message); + return GameServer_SendMessageTo(conn, message); } int NBN_GameServer_BroadcastMessage(void) @@ -4292,7 +4264,7 @@ int NBN_GameServer_AcceptIncomingConnection(void) NBN_Writer_WriteUInt32(writer, 0); } - if (NBN_GameServer_SendMessageTo(client->id) < 0) + if (NBN_GameServer_SendMessageTo(client) < 0) return NBN_ERROR; client->is_accepted = true; @@ -4318,12 +4290,12 @@ int NBN_GameServer_RejectIncomingConnection(void) return NBN_GameServer_RejectIncomingConnectionWithCode(-1); } -NBN_ConnectionHandle NBN_GameServer_GetIncomingConnection(void) +NBN_Connection *NBN_GameServer_GetIncomingConnection(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); - return nbn_game_server.last_event.data.connection->id; + return nbn_game_server.last_event.data.connection; } NBN_Reader *NBN_GameServer_GetConnectionDataReader(void) @@ -4337,11 +4309,11 @@ NBN_Reader *NBN_GameServer_GetConnectionDataReader(void) return &nbn_game_server.client_data_reader; } -NBN_ConnectionHandle NBN_GameServer_GetDisconnectedClient(void) +NBN_Connection *NBN_GameServer_GetDisconnectedClient(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_DISCONNECTED); - return nbn_game_server.last_event.data.connection_handle; + return nbn_game_server.last_event.data.connection; } NBN_MessageInfo NBN_GameServer_GetMessageInfo(void) @@ -4405,7 +4377,6 @@ static int GameServer_AddClient(NBN_Connection *client) } NBN_ConnectionVector_Add(nbn_game_server.clients, client); - NBN_ConnectionTable_Add(nbn_game_server.clients_table, client); NBN_LogDebug("Added client %d", client->id); return 0; @@ -4420,7 +4391,7 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool NBN_Event e; e.type = NBN_CLIENT_DISCONNECTED; - e.data.connection_handle = client->id; + e.data.connection = client; if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, e)) return NBN_ERROR; @@ -4450,7 +4421,7 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool NBN_Writer *writer = NBN_GameServer_GetMessageWriter(); NBN_Writer_WriteInt32(writer, code); - NBN_GameServer_SendMessageTo(client->id); + NBN_GameServer_SendMessageTo(client); } return 0; @@ -4517,7 +4488,13 @@ static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio } else { - NBN_MessageInfo msg_info = {message->header.type, message->header.channel_id, message->data, message->header.length, client->id}; + NBN_MessageInfo msg_info = { + message->header.type, + message->header.channel_id, + message->data, + message->header.length, + client + }; NBN_LogDebug("Received message (type: %d, id: %d) from client %d", message->header.type, message->header.id, client->id); ev.data.message_info = msg_info; @@ -4577,14 +4554,6 @@ static void GameServer_RemoveClosedClientConnections(void) NBN_Abort(); } - bool table_rm = NBN_ConnectionTable_Remove(nbn_game_server.clients_table, client->id); - - if (!table_rm) - { - NBN_LogError("Failed to remove client connection from connections table"); - NBN_Abort(); - } - // Destroy the connection NBN_Connection_Destroy(&nbn_game_server.endpoint, client); @@ -4629,17 +4598,9 @@ static int GameServer_HandleEvent(void) static int GameServer_HandleMessageReceivedEvent(void) { NBN_MessageInfo message_info = nbn_game_server.last_event.data.message_info; - NBN_Connection *sender = NBN_ConnectionTable_Get(nbn_game_server.clients_table, message_info.sender); - - if (sender == NULL) - { - NBN_LogTrace("Received message (type: %d) from unknown client (ID: %d)", message_info.type, message_info.sender); - - return NBN_SKIP_EVENT; - } + NBN_Connection *sender = message_info.sender; - if (sender->is_closed || sender->is_stale) - return NBN_SKIP_EVENT; + if (sender->is_closed || sender->is_stale) return NBN_SKIP_EVENT; if (message_info.type == NBN_DISCONNECTION_MESSAGE_TYPE) { @@ -4651,7 +4612,7 @@ static int GameServer_HandleMessageReceivedEvent(void) sender->is_stale = true; nbn_game_server.last_event.type = NBN_CLIENT_DISCONNECTED; - nbn_game_server.last_event.data.connection_handle = sender->id; + nbn_game_server.last_event.data.connection = sender; GameServer_RemoveClosedClientConnections(); From 0f945174b63209142863e1d0257030505d201ffc Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Mon, 8 Dec 2025 18:04:33 +0100 Subject: [PATCH 07/85] 3.0 wip --- .gitignore | 4 + examples/echo/client.c | 137 +- examples/echo/server.c | 167 +- examples/echo/shared.c | 30 +- examples/echo/shared.h | 17 +- examples/echo_bytes/CMakeLists.txt | 57 - examples/echo_bytes/README.md | 3 - examples/echo_bytes/client.c | 208 -- examples/echo_bytes/package.json | 9 - examples/echo_bytes/server.c | 160 -- examples/echo_bytes/shared.c | 76 - examples/echo_bytes/shared.h | 63 - examples/raylib/CMakeLists.txt | 1 + examples/raylib/client.c | 172 +- examples/raylib/server.c | 178 +- examples/raylib/shared.c | 104 +- examples/raylib/shared.h | 44 +- examples/rpc/CMakeLists.txt | 59 - examples/rpc/README.md | 37 - examples/rpc/client.c | 151 -- examples/rpc/package-lock.json | 865 ------- examples/rpc/package.json | 9 - examples/rpc/server.c | 135 -- examples/rpc/shared.c | 75 - examples/rpc/shared.h | 119 - nbnet.h | 3597 ++++++++++------------------ net_drivers/udp.h | 269 +-- soak/client.c | 294 ++- soak/server.c | 292 ++- soak/soak.c | 198 +- soak/soak.h | 48 +- 31 files changed, 2130 insertions(+), 5448 deletions(-) delete mode 100644 examples/echo_bytes/CMakeLists.txt delete mode 100644 examples/echo_bytes/README.md delete mode 100644 examples/echo_bytes/client.c delete mode 100644 examples/echo_bytes/package.json delete mode 100644 examples/echo_bytes/server.c delete mode 100644 examples/echo_bytes/shared.c delete mode 100644 examples/echo_bytes/shared.h delete mode 100644 examples/rpc/CMakeLists.txt delete mode 100644 examples/rpc/README.md delete mode 100644 examples/rpc/client.c delete mode 100644 examples/rpc/package-lock.json delete mode 100644 examples/rpc/package.json delete mode 100644 examples/rpc/server.c delete mode 100644 examples/rpc/shared.c delete mode 100644 examples/rpc/shared.h diff --git a/.gitignore b/.gitignore index 8ede5b0..162e5aa 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,7 @@ _deps build build_web .DS_Store +.clangd +.clang-format +.cache +compile_flags.txt diff --git a/examples/echo/client.c b/examples/echo/client.c index 6bd86f0..4d83105 100644 --- a/examples/echo/client.c +++ b/examples/echo/client.c @@ -20,8 +20,8 @@ */ -#include #include +#include #include // Has to be defined in exactly *one* source file before including the nbnet header @@ -33,15 +33,13 @@ static bool running = true; static bool connected = false; static bool disconnected = false; -void OnConnected(void) -{ +void OnConnected(void) { Log(LOG_INFO, "Connected"); connected = true; // Start sending messages } -void OnDisconnected(void) -{ +void OnDisconnected(void) { Log(LOG_INFO, "Disconnected"); // Stop the main loop @@ -49,40 +47,45 @@ void OnDisconnected(void) running = false; // Retrieve the server code used when closing our client connection - if (NBN_GameClient_GetServerCloseCode() == ECHO_SERVER_BUSY_CODE) - { + if (NBN_GameClient_GetServerCloseCode() == ECHO_SERVER_BUSY_CODE) { Log(LOG_INFO, "Another client is already connected"); } } -void OnMessageReceived(void) -{ +void OnMessageReceived(void) { // Get info about the received message NBN_MessageInfo msg_info = NBN_GameClient_GetMessageInfo(); assert(msg_info.type == ECHO_MESSAGE_TYPE); - // Retrieve the received message + NBN_Reader *reader = NBN_GameClient_GetMessageReader(); + unsigned int length; + int res; + + res = NBN_Reader_ReadUInt32(reader, &length); + assert(res == 0); + static char msg_str[ECHO_MESSAGE_MAX_LENGTH]; + + res = NBN_Reader_ReadBytes(reader, (uint8_t *)msg_str, length); + assert(res == 0); + msg_str[length] = 0; - /*EchoMessage *msg = (EchoMessage *)msg_info.data;*/ - /**/ - /*Log(LOG_INFO, "Received echo: %s (%d bytes)", msg->data, msg->length);*/ - /**/ - /*EchoMessage_Destroy(msg); // Destroy the received echo message*/ + Log(LOG_INFO, "Received echo: %s (length: %d, channel: %d)", msg_str, msg_info.length, msg_info.channel_id); } -int SendEchoMessage(const char *msg) -{ +int SendMessage(const char *msg) { + NBN_GameClient_CreateReliableMessage(ECHO_MESSAGE_TYPE); + NBN_Writer *writer = NBN_GameClient_GetMessageWriter(); unsigned int length = strlen(msg); - char *data = strdup(msg); - return NBN_GameClient_SendReliableMessage(ECHO_MESSAGE_TYPE, (uint8_t *)data, length); + NBN_Writer_WriteUInt32(writer, length); + NBN_Writer_WriteBytes(writer, (uint8_t *)msg, length); + + return NBN_GameClient_SendMessage(); } -int main(int argc, char *argv[]) -{ - if (argc != 2) - { +int main(int argc, char *argv[]) { + if (argc != 2) { printf("Usage: client MSG\n"); // Error, quit the client application @@ -94,10 +97,11 @@ int main(int argc, char *argv[]) } const char *msg = argv[1]; + // reserve 4 bytes to write the message length in the message (see the SendMessage function) + unsigned int msg_max_len = ECHO_MESSAGE_MAX_LENGTH - 4; - if (strlen(msg) > ECHO_MESSAGE_LENGTH - 1) - { - Log(LOG_ERROR, "Message length cannot exceed %d. Exit", ECHO_MESSAGE_LENGTH - 1); + if (strlen(msg) > msg_max_len) { + Log(LOG_ERROR, "Message length cannot exceed %d. Exit", msg_max_len); // Error, quit the client application #ifdef __EMSCRIPTEN__ @@ -126,15 +130,14 @@ int main(int argc, char *argv[]) bool enable_tls = false; #endif // NBN_TLS - const char *ice_servers[] = { "stun:stun01.sipphone.com" }; - NBN_WebRTC_C_Config cfg = { - .ice_servers = ice_servers, - .ice_servers_count = 1, - .enable_tls = enable_tls, - .cert_path = NULL, - .key_path = NULL, - .passphrase = NULL, - .log_level = RTC_LOG_VERBOSE}; + const char *ice_servers[] = {"stun:stun01.sipphone.com"}; + NBN_WebRTC_C_Config cfg = {.ice_servers = ice_servers, + .ice_servers_count = 1, + .enable_tls = enable_tls, + .cert_path = NULL, + .key_path = NULL, + .passphrase = NULL, + .log_level = RTC_LOG_VERBOSE}; NBN_WebRTC_C_Register(cfg); @@ -142,14 +145,17 @@ int main(int argc, char *argv[]) #if !defined(__EMSCRIPTEN__) && !defined(NBN_WEBRTC_NATIVE) NBN_UDP_Register(); // Register the UDP driver -#endif // __EMSCRIPTEN__ +#endif // __EMSCRIPTEN__ - // Initialize the client + // Initialize the client // Start the client with a protocol name (must be the same than the one used by the server) - // the server host and port and with packet encryption on or off - if (NBN_GameClient_StartEx(ECHO_PROTOCOL_NAME, "127.0.0.1", ECHO_EXAMPLE_PORT, NULL, 0) < 0) - { + // the server host and port + + NBN_GameClient_Config config = NBN_GameClient_CreateConfig(ECHO_PROTOCOL_NAME, "127.0.0.1", ECHO_EXAMPLE_PORT, + AllocateMessage, DeallocateMessage); + + if (NBN_GameClient_Start(config) < 0) { Log(LOG_ERROR, "Failed to start client"); // Error, quit the client application @@ -157,21 +163,18 @@ int main(int argc, char *argv[]) emscripten_force_exit(1); #else return 1; -#endif +#endif } // Number of seconds between client ticks double dt = 1.0 / ECHO_TICK_RATE; - while (running) - { + while (running) { int ev; // Poll for client events - while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { + while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) { + if (ev < 0) { Log(LOG_ERROR, "An error occured while polling client events. Exit"); // Stop main loop @@ -179,32 +182,29 @@ int main(int argc, char *argv[]) break; } - switch (ev) - { - // Client is connected to the server - case NBN_CONNECTED: - OnConnected(); - break; - - // Client has disconnected from the server - case NBN_DISCONNECTED: - OnDisconnected(); - break; - - // A message has been received from the server - case NBN_MESSAGE_RECEIVED: - OnMessageReceived(); - break; + switch (ev) { + // Client is connected to the server + case NBN_CONNECTED: + OnConnected(); + break; + + // Client has disconnected from the server + case NBN_DISCONNECTED: + OnDisconnected(); + break; + + // A message has been received from the server + case NBN_MESSAGE_RECEIVED: + OnMessageReceived(); + break; } } if (disconnected) break; - if (connected) - { - if (SendEchoMessage(msg) < 0) - { + if (connected) { + if (SendMessage(msg) < 0) { Log(LOG_ERROR, "Failed to send message. Exit"); // Stop main loop @@ -214,8 +214,7 @@ int main(int argc, char *argv[]) } // Pack all enqueued messages as packets and send them - if (NBN_GameClient_SendPackets() < 0) - { + if (NBN_GameClient_SendPackets() < 0) { Log(LOG_ERROR, "Failed to send packets. Exit"); // Stop main loop diff --git a/examples/echo/server.c b/examples/echo/server.c index 48655a2..b712e7e 100644 --- a/examples/echo/server.c +++ b/examples/echo/server.c @@ -20,8 +20,8 @@ */ -#include #include +#include #include // Has to be defined in exactly *one* source file before including the nbnet header @@ -29,51 +29,52 @@ #include "shared.h" -static NBN_ConnectionHandle client = 0; +static NBN_Connection *connection = NULL; +static uint32_t conn_id; // Echo the received message -static int EchoReceivedMessage(void) -{ +static int EchoReceivedMessage(void) { // Get info about the received message NBN_MessageInfo msg_info = NBN_GameServer_GetMessageInfo(); - assert(msg_info.sender == client); + assert(msg_info.sender->id == conn_id); assert(msg_info.type == ECHO_MESSAGE_TYPE); - // Retrieve the received message - - Log(LOG_DEBUG, "RECEIVED"); - - /*EchoMessage *msg = (EchoMessage *)msg_info.data;*/ - /**/ - /*// Create an echo message*/ - /*EchoMessage *echo = EchoMessage_Create();*/ - /**/ - /*// Fill it with the received message data and length*/ - /*memcpy(echo->data, msg->data, msg->length);*/ - /*echo->length = msg->length;*/ - /**/ - /*// Reliably send it to the client*/ - /*// If the send fails the client will be disconnected and a NBN_CLIENT_DISCONNECTED event*/ - /*// will be received (see event polling in main)*/ - /*NBN_GameServer_SendReliableMessageTo(client, ECHO_MESSAGE_TYPE, echo);*/ - /**/ - /*EchoMessage_Destroy(msg); // Destroy the received echo message*/ - - return 0; + // read message data + NBN_Reader *reader = NBN_GameServer_GetMessageReader(); + unsigned int length; + int res; + + res = NBN_Reader_ReadUInt32(reader, &length); + assert(res == 0); + static char msg_str[ECHO_MESSAGE_MAX_LENGTH]; + + res = NBN_Reader_ReadBytes(reader, (uint8_t *)msg_str, length); + assert(res == 0); + msg_str[length] = 0; + + Log(LOG_INFO, "Received message: %s, send echo (length: %d, channel: %d)", msg_str, msg_info.length, + msg_info.channel_id); + + // create and send an echo of the received message + NBN_GameServer_CreateReliableMessage(ECHO_MESSAGE_TYPE); + NBN_Writer *writer = NBN_GameServer_GetMessageWriter(); + + NBN_Writer_WriteUInt32(writer, length); + NBN_Writer_WriteBytes(writer, (uint8_t *)msg_str, length); + + return NBN_GameServer_SendMessageTo(connection); } static bool error = false; -int main(int argc, const char **argv) -{ +int main(int argc, const char **argv) { #ifdef __EMSCRIPTEN__ // Register the WebRTC driver -#ifdef NBN_TLS +#ifdef NBN_TLS - if (argc != 3) - { + if (argc != 3) { printf("Usage: server CERT_PATH KEY_PATH\n"); return 1; } @@ -98,15 +99,14 @@ int main(int argc, const char **argv) bool enable_tls = false; #endif // NBN_TLS - const char *ice_servers[] = { "stun:stun01.sipphone.com" }; - NBN_WebRTC_C_Config cfg = { - .ice_servers = ice_servers, - .ice_servers_count = 1, - .enable_tls = enable_tls, - .cert_path = NULL, - .key_path = NULL, - .passphrase = NULL, - .log_level = RTC_LOG_VERBOSE}; + const char *ice_servers[] = {"stun:stun01.sipphone.com"}; + NBN_WebRTC_C_Config cfg = {.ice_servers = ice_servers, + .ice_servers_count = 1, + .enable_tls = enable_tls, + .cert_path = NULL, + .key_path = NULL, + .passphrase = NULL, + .log_level = RTC_LOG_VERBOSE}; NBN_WebRTC_C_Register(cfg); #endif // NBN_WEBRTC_NATIVE @@ -115,9 +115,12 @@ int main(int argc, const char **argv) NBN_UDP_Register(); // Register the UDP driver #endif - // Start the server with a protocol name, a port, and with packet encryption on or off - if (NBN_GameServer_StartEx(ECHO_PROTOCOL_NAME, ECHO_EXAMPLE_PORT) < 0) - { + // Start the server with a protocol name and a port + + NBN_GameServer_Config config = + NBN_GameServer_CreateConfig(ECHO_PROTOCOL_NAME, ECHO_EXAMPLE_PORT, AllocateMessage, DeallocateMessage); + + if (NBN_GameServer_Start(config) < 0) { Log(LOG_ERROR, "Failed to start the server"); // Error, quit the server application @@ -131,15 +134,13 @@ int main(int argc, const char **argv) // Number of seconds between server ticks double dt = 1.0 / ECHO_TICK_RATE; - while (true) - { + while (true) { int ev; + NBN_DisconnectionInfo disconnect_info; // Poll for server events - while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { + while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) { + if (ev < 0) { Log(LOG_ERROR, "Something went wrong"); // Error, quit the server application @@ -147,46 +148,42 @@ int main(int argc, const char **argv) break; } - switch (ev) - { - // New connection request... - case NBN_NEW_CONNECTION: - // Echo server work with one single client at a time - if (client) - { - NBN_GameServer_RejectIncomingConnectionWithCode(ECHO_SERVER_BUSY_CODE); - } - else - { - NBN_GameServer_AcceptIncomingConnection(); - client = NBN_GameServer_GetIncomingConnection(); - } - - break; - - // The client has disconnected - case NBN_CLIENT_DISCONNECTED: - assert(NBN_GameServer_GetDisconnectedClient() == client); - - client = 0; - break; - - // A message has been received from the client - case NBN_CLIENT_MESSAGE_RECEIVED: - if (EchoReceivedMessage() < 0) - { - Log(LOG_ERROR, "Failed to echo received message"); - - // Error, quit the server application - error = true; - } - break; + switch (ev) { + // New connection request... + case NBN_NEW_CONNECTION: + // Echo server work with one single client at a time + if (connection) { + NBN_GameServer_RejectIncomingConnectionWithCode(ECHO_SERVER_BUSY_CODE); + } else { + NBN_GameServer_AcceptIncomingConnection(); + connection = NBN_GameServer_GetIncomingConnection(); + conn_id = connection->id; + } + + break; + + // The client has disconnected + case NBN_CLIENT_DISCONNECTED: + disconnect_info = NBN_GameServer_GetDisconnectionInfo(); + + assert(disconnect_info.conn_id == conn_id); + connection = NULL; + break; + + // A message has been received from the client + case NBN_CLIENT_MESSAGE_RECEIVED: + if (EchoReceivedMessage() < 0) { + Log(LOG_ERROR, "Failed to echo received message"); + + // Error, quit the server application + error = true; + } + break; } } // Pack all enqueued messages as packets and send them - if (NBN_GameServer_SendPackets() < 0) - { + if (NBN_GameServer_SendPackets() < 0) { Log(LOG_ERROR, "Failed to send packets"); // Error, quit the server application diff --git a/examples/echo/shared.c b/examples/echo/shared.c index 6dd9a0c..b7f0bbb 100644 --- a/examples/echo/shared.c +++ b/examples/echo/shared.c @@ -20,17 +20,18 @@ */ +#include +#include #include #include -#include // Sleep function #if defined(__EMSCRIPTEN__) #include #elif defined(_WIN32) || defined(_WIN64) -#include -#include #include +#include +#include #else #include #endif @@ -39,8 +40,7 @@ // Sleep for a given amount of seconds // Used to limit client and server tick rate -void EchoSleep(double sec) -{ +void EchoSleep(double sec) { #if defined(__EMSCRIPTEN__) emscripten_sleep(sec * 1000); #elif defined(_WIN32) || defined(_WIN64) @@ -53,17 +53,10 @@ void EchoSleep(double sec) #endif } -static const char *log_type_strings[] = { - "INFO", - "ERROR", - "DEBUG", - "TRACE", - "WARNING" -}; +static const char *log_type_strings[] = {"INFO", "ERROR", "DEBUG", "TRACE", "WARNING"}; // Basic logging function -void Log(int type, const char *fmt, ...) -{ +void Log(int type, const char *fmt, ...) { va_list args; va_start(args, fmt); @@ -74,3 +67,12 @@ void Log(int type, const char *fmt, ...) va_end(args); } + +uint8_t *AllocateMessage(uint8_t type, uint16_t *length) { + NBN_Assert(type == ECHO_MESSAGE_TYPE); + + *length = ECHO_MESSAGE_MAX_LENGTH; + return (uint8_t *)NBN_Allocator(*length); +} + +void DeallocateMessage(uint8_t type, uint8_t *data) { NBN_Deallocator(data); } diff --git a/examples/echo/shared.h b/examples/echo/shared.h index 59f1720..27ae633 100644 --- a/examples/echo/shared.h +++ b/examples/echo/shared.h @@ -26,7 +26,7 @@ #define ECHO_PROTOCOL_NAME "echo-example" #define ECHO_EXAMPLE_PORT 42042 #define ECHO_MESSAGE_TYPE 0 -#define ECHO_MESSAGE_LENGTH 255 +#define ECHO_MESSAGE_MAX_LENGTH 255 #define ECHO_TICK_RATE 30 // An arbitrary chosen code used when rejecting a client to let it know that another client is already connected @@ -34,16 +34,9 @@ // nbnet logging // nbnet does not implement any logging capabilities, you need to provide your own -enum -{ - LOG_INFO, - LOG_ERROR, - LOG_DEBUG, - LOG_TRACE, - LOG_WARNING -}; - -#define NBN_LogInfo(...) Log(LOG_INFO, __VA_ARGS__) +enum { LOG_INFO, LOG_ERROR, LOG_DEBUG, LOG_TRACE, LOG_WARNING }; + +#define NBN_LogInfo(...) Log(LOG_INFO, __VA_ARGS__) #define NBN_LogError(...) Log(LOG_ERROR, __VA_ARGS__) #define NBN_LogDebug(...) Log(LOG_DEBUG, __VA_ARGS__) #define NBN_LogTrace(...) Log(LOG_TRACE, __VA_ARGS__) @@ -70,5 +63,7 @@ void Log(int, const char *, ...); #endif // __EMSCRIPTEN__ void EchoSleep(double); +uint8_t *AllocateMessage(uint8_t type, uint16_t *); +void DeallocateMessage(uint8_t type, uint8_t *data); #endif /* ECHO_EXAMPLE_SHARED_H */ diff --git a/examples/echo_bytes/CMakeLists.txt b/examples/echo_bytes/CMakeLists.txt deleted file mode 100644 index eb60afb..0000000 --- a/examples/echo_bytes/CMakeLists.txt +++ /dev/null @@ -1,57 +0,0 @@ -cmake_minimum_required(VERSION 3.1) - -project(echo_bytes) - -option(CPP_COMPILE OFF) - -# allow to compile as cpp -if (CPP_COMPILE) - file(GLOB_RECURSE CFILES "${CMAKE_SOURCE_DIR}/*.c") - SET_SOURCE_FILES_PROPERTIES(${CFILES} PROPERTIES LANGUAGE CXX) - set (CMAKE_CXX_STANDARD 20) -endif (CPP_COMPILE) - -unset(CPP_COMPILE) - -add_executable(echo_bytes_client client.c shared.c) -add_executable(echo_bytes_server server.c shared.c) - -add_compile_options(-Wall -Wextra -Wpedantic) - -target_compile_definitions(echo_bytes_client PUBLIC NBN_DEBUG) -target_compile_definitions(echo_bytes_server PUBLIC NBN_DEBUG) - -if(WIN32) - target_link_libraries(echo_bytes_client wsock32 ws2_32) - target_link_libraries(echo_bytes_server wsock32 ws2_32) -else() - # link with pthread when we are not on windows - target_link_libraries(echo_bytes_client pthread) - target_link_libraries(echo_bytes_server pthread) -endif(WIN32) - -if (UNIX) - # link with libm on unix - target_link_libraries(echo_bytes_client m) - target_link_libraries(echo_bytes_server m) -endif (UNIX) - -if (EMSCRIPTEN) - set(ASYNCIFY_IMPORTS "[\"__js_game_server_start\", \"__js_game_client_start\", \"__js_game_client_close\"]") - - set_target_properties(echo_bytes_server PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/api.js \ - -s ALLOW_MEMORY_GROWTH=1 \ - -s TOTAL_MEMORY=30MB \ - -s EXIT_RUNTIME=1 \ - -s ASSERTIONS=1 \ - -s ASYNCIFY \ - -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") - - set_target_properties(echo_bytes_client PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/api.js \ - -s ALLOW_MEMORY_GROWTH=1 \ - -s TOTAL_MEMORY=30MB \ - -s EXIT_RUNTIME=1 \ - -s ASSERTIONS=1 \ - -s ASYNCIFY \ - -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") -endif() diff --git a/examples/echo_bytes/README.md b/examples/echo_bytes/README.md deleted file mode 100644 index 2798833..0000000 --- a/examples/echo_bytes/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Echo (byte array) - -The very same example as the "echo" one expect it uses byte arrays instead of a user defined message. diff --git a/examples/echo_bytes/client.c b/examples/echo_bytes/client.c deleted file mode 100644 index 4cedaf8..0000000 --- a/examples/echo_bytes/client.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#include -#include -#include - -// Has to be defined in exactly *one* source file before including the nbnet header -#define NBNET_IMPL - -#include "shared.h" - -static bool running = true; -static bool connected = false; -static bool disconnected = false; - -void OnConnected(void) -{ - Log(LOG_INFO, "Connected"); - - connected = true; // Start sending messages -} - -void OnDisconnected(void) -{ - Log(LOG_INFO, "Disconnected"); - - // Stop the main loop - disconnected = true; - running = false; - - // Retrieve the server code used when closing our client connection - if (NBN_GameClient_GetServerCloseCode() == ECHO_SERVER_BUSY_CODE) - { - Log(LOG_INFO, "Another client is already connected"); - } -} - -void OnMessageReceived(void) -{ - // Get info about the received message - NBN_MessageInfo msg_info = NBN_GameClient_GetMessageInfo(); - - assert(msg_info.type == NBN_BYTE_ARRAY_MESSAGE_TYPE); - - // Retrieve the received message - NBN_ByteArrayMessage *msg = (NBN_ByteArrayMessage *)msg_info.data; - - Log(LOG_INFO, "Received echo: %s (%d bytes)", msg->bytes, msg->length); - - // Destroy the received message - NBN_ByteArrayMessage_Destroy(msg); -} - -int SendEchoMessage(const char *msg) -{ - unsigned int length = strlen(msg); // Compute message length - - // Reliably send bytes to the server - if (NBN_GameClient_SendReliableByteArray((uint8_t *)msg, length) < 0) - return -1; - - return 0; -} - -int main(int argc, char *argv[]) -{ - if (argc != 2) - { - printf("Usage: client MSG\n"); - -// Error, quit the client application -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(1); -#else - return 1; -#endif - } - - const char *msg = argv[1]; - - if (strlen(msg) > NBN_BYTE_ARRAY_MAX_SIZE - 1) - { - Log(LOG_ERROR, "Message length cannot exceed %d. Exit", NBN_BYTE_ARRAY_MAX_SIZE - 1); - -// Error, quit the client application -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(1); -#else - return 1; -#endif - } - -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register(); // Register the WebRTC driver -#else - NBN_UDP_Register(); // Register the UDP driver -#endif // __EMSCRIPTEN__ - - // Start the client with a protocol name (must be the same than the one used by the server) - // the server host and port - if (NBN_GameClient_Start(ECHO_PROTOCOL_NAME, "127.0.0.1", ECHO_EXAMPLE_PORT) < 0) - { - Log(LOG_ERROR, "Failed to start client"); - -// Error, quit the client application -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(1); -#else - return 1; -#endif - } - - // Number of seconds between client ticks - double dt = 1.0 / ECHO_TICK_RATE; - - while (running) - { - int ev; - - // Poll for client events - while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { - Log(LOG_ERROR, "An error occured while polling client events. Exit"); - - // Stop main loop - running = false; - break; - } - - switch (ev) - { - // Client is connected to the server - case NBN_CONNECTED: - OnConnected(); - break; - - // Client has disconnected from the server - case NBN_DISCONNECTED: - OnDisconnected(); - break; - - // A message has been received from the server - case NBN_MESSAGE_RECEIVED: - OnMessageReceived(); - break; - } - } - - if (disconnected) - break; - - if (connected) - { - if (SendEchoMessage(msg) < 0) - { - Log(LOG_ERROR, "Failed to send message. Exit"); - - // Stop main loop - running = false; - break; - } - } - - // Pack all enqueued messages as packets and send them - if (NBN_GameClient_SendPackets() < 0) - { - Log(LOG_ERROR, "Failed to send packets. Exit"); - - // Stop main loop - running = false; - break; - } - - // Cap the client tick rate - EchoSleep(dt); - } - - // Stop the client - NBN_GameClient_Stop(); - -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(0); -#else - return 0; -#endif -} diff --git a/examples/echo_bytes/package.json b/examples/echo_bytes/package.json deleted file mode 100644 index 2048af3..0000000 --- a/examples/echo_bytes/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "scripts": { - "server": "node echo_bytes_server.js", - "client": "node echo_bytes_client.js" - }, - "dependencies": { - "nbnet": "file:../../net_drivers/webrtc" - } -} diff --git a/examples/echo_bytes/server.c b/examples/echo_bytes/server.c deleted file mode 100644 index 2062a26..0000000 --- a/examples/echo_bytes/server.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#include -#include -#include - -// Has to be defined in exactly *one* source file before including the nbnet header -#define NBNET_IMPL - -#include "shared.h" - -static NBN_ConnectionHandle client = 0; - -// Echo the received message -static int EchoReceivedMessage(void) -{ - // Get info about the received message - NBN_MessageInfo msg_info = NBN_GameServer_GetMessageInfo(); - - assert(msg_info.sender == client); - assert(msg_info.type == NBN_BYTE_ARRAY_MESSAGE_TYPE); - - // Retrieve the received message - NBN_ByteArrayMessage *msg = (NBN_ByteArrayMessage *)msg_info.data; - - // If the send fails the client will be disconnected and a NBN_CLIENT_DISCONNECTED event - // will be received (see event polling in main) - if (NBN_GameServer_SendReliableByteArrayTo(client, msg->bytes, msg->length) < 0) - return -1; - - // Destroy the received message - NBN_ByteArrayMessage_Destroy(msg); - - return 0; -} - -static bool error = false; - -int main(void) -{ -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register(); // Register the WebRTC driver -#else - NBN_UDP_Register(); // Register the UDP driver -#endif // __EMSCRIPTEN__ - - // Start the server with a protocol name and a port - if (NBN_GameServer_Start(ECHO_PROTOCOL_NAME, ECHO_EXAMPLE_PORT) < 0) - { - Log(LOG_ERROR, "Failed to start the server"); - - // Error, quit the server application -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(1); -#else - return 1; -#endif - } - - // Number of seconds between server ticks - double dt = 1.0 / ECHO_TICK_RATE; - - while (true) - { - int ev; - - // Poll for server events - while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { - Log(LOG_ERROR, "Something went wrong"); - - // Error, quit the server application - error = true; - break; - } - - switch (ev) - { - // New connection request... - case NBN_NEW_CONNECTION: - // Echo server work with one single client at a time - if (client) - { - NBN_GameServer_RejectIncomingConnectionWithCode(ECHO_SERVER_BUSY_CODE); - } - else - { - NBN_GameServer_AcceptIncomingConnection(); - client = NBN_GameServer_GetIncomingConnection(); - } - - break; - - // The client has disconnected - case NBN_CLIENT_DISCONNECTED: - assert(NBN_GameServer_GetDisconnectedClient() == client); - - client = 0; - break; - - // A message has been received from the client - case NBN_CLIENT_MESSAGE_RECEIVED: - if (EchoReceivedMessage() < 0) - { - Log(LOG_ERROR, "Failed to echo received message"); - - // Error, quit the server application - error = true; - } - break; - } - } - - // Pack all enqueued messages as packets and send them - if (NBN_GameServer_SendPackets() < 0) - { - Log(LOG_ERROR, "Failed to send packets"); - - // Error, quit the server application - error = true; - break; - } - - // Cap the server tick rate - EchoSleep(dt); - } - - // Stop the server - NBN_GameServer_Stop(); - - int ret = error ? 1 : 0; - -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(ret); -#else - return ret; -#endif -} diff --git a/examples/echo_bytes/shared.c b/examples/echo_bytes/shared.c deleted file mode 100644 index 6dd9a0c..0000000 --- a/examples/echo_bytes/shared.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#include -#include -#include - -// Sleep function -#if defined(__EMSCRIPTEN__) -#include -#elif defined(_WIN32) || defined(_WIN64) -#include -#include -#include -#else -#include -#endif - -#include "shared.h" - -// Sleep for a given amount of seconds -// Used to limit client and server tick rate -void EchoSleep(double sec) -{ -#if defined(__EMSCRIPTEN__) - emscripten_sleep(sec * 1000); -#elif defined(_WIN32) || defined(_WIN64) - Sleep(sec * 1000); -#else /* UNIX / OSX */ - long nanos = sec * 1e9; - struct timespec t = {.tv_sec = nanos / 999999999, .tv_nsec = nanos % 999999999}; - - nanosleep(&t, &t); -#endif -} - -static const char *log_type_strings[] = { - "INFO", - "ERROR", - "DEBUG", - "TRACE", - "WARNING" -}; - -// Basic logging function -void Log(int type, const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - - printf("[%s] ", log_type_strings[type]); - vprintf(fmt, args); - printf("\n"); - - va_end(args); -} diff --git a/examples/echo_bytes/shared.h b/examples/echo_bytes/shared.h deleted file mode 100644 index eeebe02..0000000 --- a/examples/echo_bytes/shared.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#ifndef ECHO_EXAMPLE_SHARED_H -#define ECHO_EXAMPLE_SHARED_H - -#define ECHO_PROTOCOL_NAME "echo-example" -#define ECHO_EXAMPLE_PORT 42042 -#define ECHO_MESSAGE_TYPE 0 -#define ECHO_TICK_RATE 30 - -// An arbitrary chosen code used when rejecting a client to let it know that another client is already connected -#define ECHO_SERVER_BUSY_CODE 42 - -// nbnet logging -// nbnet does not implement any logging capabilities, you need to provide your own -enum -{ - LOG_INFO, - LOG_ERROR, - LOG_DEBUG, - LOG_TRACE, - LOG_WARNING -}; - -#define NBN_LogInfo(...) Log(LOG_INFO, __VA_ARGS__) -#define NBN_LogError(...) Log(LOG_ERROR, __VA_ARGS__) -#define NBN_LogDebug(...) Log(LOG_DEBUG, __VA_ARGS__) -#define NBN_LogTrace(...) Log(LOG_TRACE, __VA_ARGS__) -#define NBN_LogWarning(...) Log(LOG_WARNING, __VA_ARGS__) - -void Log(int, const char *, ...); - -#include "../../nbnet.h" - -#ifdef __EMSCRIPTEN__ -#include "../../net_drivers/webrtc.h" -#else -#include "../../net_drivers/udp.h" -#endif - -void EchoSleep(double); - -#endif /* ECHO_EXAMPLE_SHARED_H */ diff --git a/examples/raylib/CMakeLists.txt b/examples/raylib/CMakeLists.txt index 7b302c0..f198242 100644 --- a/examples/raylib/CMakeLists.txt +++ b/examples/raylib/CMakeLists.txt @@ -85,4 +85,5 @@ endif() if (APPLE) target_link_libraries(raylib_client "-framework OpenGL -framework Cocoa -framework IOKit -framework CoreAudio -framework CoreVideo") + target_link_libraries(raylib_server "-framework OpenGL -framework Cocoa -framework IOKit -framework CoreAudio -framework CoreVideo") endif (APPLE) diff --git a/examples/raylib/client.c b/examples/raylib/client.c index c84c41c..b20c857 100644 --- a/examples/raylib/client.c +++ b/examples/raylib/client.c @@ -60,7 +60,7 @@ Color client_colors_to_raylib_colors[] = { static void SpawnLocalClient(int x, int y, uint32_t client_id) { - TraceLog(LOG_INFO, "Received spawn message, position: (%d, %d), client id: %d", x, y, client_id); + TraceLog(LOG_INFO, "Spawning at (%d, %d), client id: %d", x, y, client_id); // Update the local client state based on spawn info sent by the server local_client_state.client_id = client_id; @@ -70,25 +70,32 @@ static void SpawnLocalClient(int x, int y, uint32_t client_id) spawned = true; } -static void HandleConnection(void) +static int HandleConnection(void) { - uint8_t data[32]; - unsigned int data_len = NBN_GameClient_ReadServerData(data); - NBN_ReadStream rs; + TraceLog(LOG_INFO, "Connected, reading connection data..."); - NBN_ReadStream_Init(&rs, data, data_len); + uint32_t x, y, client_id; + NBN_Reader *reader = NBN_GameClient_GetServerDataReader(); - unsigned int x = 0; - unsigned int y = 0; - unsigned int client_id = 0; + if (NBN_Reader_ReadUInt32(reader, &x) < 0) + { + return -1; + } - NBN_SerializeUInt(((NBN_Stream *)&rs), x, 0, GAME_WIDTH); - NBN_SerializeUInt(((NBN_Stream *)&rs), y, 0, GAME_HEIGHT); - NBN_SerializeUInt(((NBN_Stream *)&rs), client_id, 0, UINT_MAX); + if (NBN_Reader_ReadUInt32(reader, &y) < 0) + { + return -1; + } + + if (NBN_Reader_ReadUInt32(reader, &client_id) < 0) + { + return -1; + } SpawnLocalClient(x, y, client_id); connected = true; + return 0; } static void HandleDisconnection(void) @@ -211,37 +218,43 @@ static void DestroyDisconnectedClients(void) } } -static void HandleGameStateMessage(GameStateMessage *msg) +static void HandleGameStateMessage(void) { - if (!spawned) - return; + if (!spawned) return; // Start by resetting the updated client ids array for (int i = 0; i < MAX_CLIENTS; i++) + { updated_ids[i] = -1; + } + + static GameState recv_game_state; + + // Read the game state from the received GAME_STATE_MESSAGE message + NBN_Reader *reader = NBN_GameClient_GetMessageReader(); - // Loop over the received client states - for (unsigned int i = 0; i < msg->client_count; i++) + GameStateMessage_Read(reader, &recv_game_state); + + // Loop over the received client states and update the clients + for (unsigned int i = 0; i < recv_game_state.client_count; i++) { - ClientState state = msg->client_states[i]; + ClientState cli_state = recv_game_state.client_states[i]; // Ignore the state of the local client - if (state.client_id != local_client_state.client_id) + if (cli_state.client_id != local_client_state.client_id) { // If the client already exists we update it with the latest received state - if (ClientExists(state.client_id)) - UpdateClient(state); + if (ClientExists(cli_state.client_id)) + UpdateClient(cli_state); else // If the client does not exist, we create it - CreateClient(state); + CreateClient(cli_state); - updated_ids[i] = state.client_id; + updated_ids[i] = cli_state.client_id; } } // Destroy disconnected clients DestroyDisconnectedClients(); - - GameStateMessage_Destroy(msg); } static void HandleReceivedMessage(void) @@ -253,58 +266,71 @@ static void HandleReceivedMessage(void) { // We received the latest game state from the server case GAME_STATE_MESSAGE: - HandleGameStateMessage(msg_info.data); - break; + HandleGameStateMessage(); + return; } + + TraceLog(LOG_ERROR, "Received unexpected message"); + abort(); } static void HandleGameClientEvent(int ev) { switch (ev) { - case NBN_CONNECTED: - // We are connected to the server - HandleConnection(); - break; - - case NBN_DISCONNECTED: - // The server has closed our connection - HandleDisconnection(); - break; - - case NBN_MESSAGE_RECEIVED: - // We received a message from the server - HandleReceivedMessage(); - break; + case NBN_CONNECTED: + // We are connected to the server + if (HandleConnection() < 0) + { + TraceLog(LOG_ERROR, "Failed to handle connection"); + abort(); + } + break; + + case NBN_DISCONNECTED: + // The server has closed our connection + HandleDisconnection(); + break; + + case NBN_MESSAGE_RECEIVED: + // We received a message from the server + HandleReceivedMessage(); + break; } } -static int SendPositionUpdate(void) +static int SendStateUpdate(void) { - UpdateStateMessage *msg = UpdateStateMessage_Create(); + // Create a new UPDATE_STATE_MESSAGE unreliable message + NBN_GameClient_CreateUnreliableMessage(UPDATE_STATE_MESSAGE); + NBN_Writer *writer = NBN_GameClient_GetMessageWriter(); - // Fill message data - msg->x = local_client_state.x; - msg->y = local_client_state.y; - msg->val = local_client_state.val; + // Write the local client state to the message + UpdateClientStateMessage_Write(writer, local_client_state); - // Unreliably send it to the server - if (NBN_GameClient_SendUnreliableMessage(UPDATE_STATE_MESSAGE, msg) < 0) + // Send the message to the server + if (NBN_GameClient_SendMessage() < 0) + { return -1; + } return 0; } static int SendColorUpdate(void) { - ChangeColorMessage *msg = ChangeColorMessage_Create(); + // Create a new CHANGE_COLOR_MESSAGE reliable message + NBN_GameClient_CreateReliableMessage(CHANGE_COLOR_MESSAGE); + NBN_Writer *writer = NBN_GameClient_GetMessageWriter(); - // Fill message data - msg->color = local_client_state.color; + // Write the new client color to the message + ChangeColorMessage_Write(writer, local_client_state.color); - // Reliably send it to the server - if (NBN_GameClient_SendReliableMessage(CHANGE_COLOR_MESSAGE, msg) < 0) + // Send the message to the server + if (NBN_GameClient_SendMessage() < 0) + { return -1; + } return 0; } @@ -354,7 +380,7 @@ static int Update(void) local_client_state.val = MAX(MIN_FLOAT_VAL, local_client_state.val - 0.005); // Send the latest local client state to the server - if (SendPositionUpdate() < 0) + if (SendStateUpdate() < 0) { TraceLog(LOG_WARNING, "Failed to send client state update"); @@ -502,7 +528,7 @@ int main(int argc, char *argv[]) (void)argv; #endif - SetTraceLogLevel(LOG_DEBUG); + SetTraceLogLevel(LOG_TRACE); InitWindow(GAME_WIDTH, GAME_HEIGHT, "raylib client"); // Set target FPS to 100 when we are not running in a web browser @@ -516,35 +542,25 @@ int main(int argc, char *argv[]) NBN_UDP_Register(); // Register the UDP driver #endif // __EMSCRIPTEN__ - // Initialize the client with a protocol name (must be the same than the one used by the server), the server ip address and port + // Create a client configuration with a protocol name, the server host and the server port + // protocol name has to be the same as the one used by the server + NBN_GameClient_Config config = NBN_GameClient_CreateConfig( + RAYLIB_EXAMPLE_PROTOCOL_NAME, "127.0.0.1", RAYLIB_EXAMPLE_PORT); - // Start the client with a protocol name (must be the same than the one used by the server) - // the server host and port - if (NBN_GameClient_StartEx(RAYLIB_EXAMPLE_PROTOCOL_NAME, "127.0.0.1", RAYLIB_EXAMPLE_PORT, NULL, 0) < 0) + // Register all the messages of our protocol + // both server and clients have to register the same messages + NBN_GameClient_RegisterMessageType(&config, CHANGE_COLOR_MESSAGE, CHANGE_COLOR_MESSAGE_MAX_LENGTH); + NBN_GameClient_RegisterMessageType(&config, UPDATE_STATE_MESSAGE, UPDATE_STATE_MESSAGE_MAX_LENGTH); + NBN_GameClient_RegisterMessageType(&config, GAME_STATE_MESSAGE, GAME_STATE_MESSAGE_MAX_LENGTH); + + // Start the client with the configuration + if (NBN_GameClient_Start(config) < 0) { TraceLog(LOG_WARNING, "Game client failed to start. Exit"); return 1; } - // Register messages, have to be done after NBN_GameClient_StartEx - // Messages need to be registered on both client and server side - NBN_GameClient_RegisterMessage( - CHANGE_COLOR_MESSAGE, - (NBN_MessageBuilder)ChangeColorMessage_Create, - (NBN_MessageDestructor)ChangeColorMessage_Destroy, - (NBN_MessageSerializer)ChangeColorMessage_Serialize); - NBN_GameClient_RegisterMessage( - UPDATE_STATE_MESSAGE, - (NBN_MessageBuilder)UpdateStateMessage_Create, - (NBN_MessageDestructor)UpdateStateMessage_Destroy, - (NBN_MessageSerializer)UpdateStateMessage_Serialize); - NBN_GameClient_RegisterMessage( - GAME_STATE_MESSAGE, - (NBN_MessageBuilder)GameStateMessage_Create, - (NBN_MessageDestructor)GameStateMessage_Destroy, - (NBN_MessageSerializer)GameStateMessage_Serialize); - // Network conditions simulated variables (read from the command line, default is always 0) NBN_GameClient_SetPing(GetOptions().ping); NBN_GameClient_SetJitter(GetOptions().jitter); diff --git a/examples/raylib/server.c b/examples/raylib/server.c index 1e3bb95..b4e1222 100644 --- a/examples/raylib/server.c +++ b/examples/raylib/server.c @@ -38,8 +38,8 @@ // A simple structure to represent connected clients typedef struct { - // Underlying nbnet connection handle, used to send messages to that particular client - NBN_ConnectionHandle client_handle; + // Underlying nbnet connection, used to send messages to that particular client + NBN_Connection *conn; // Client state ClientState state; @@ -59,19 +59,17 @@ static Vector2 spawns[] = { (Vector2){GAME_WIDTH - 100, GAME_HEIGHT - 100} }; -static void AcceptConnection(unsigned int x, unsigned int y, NBN_ConnectionHandle conn) +static void AcceptConnection(Vector2 spawn, NBN_Connection *conn) { - NBN_WriteStream ws; - uint8_t data[32]; + // Accept the connection with some data + // this data can be read by the client upon processing the connection event + NBN_Writer *writer = NBN_GameServer_GetConnectionDataWriter(); - NBN_WriteStream_Init(&ws, data, sizeof(data)); + NBN_Writer_WriteUInt32(writer, (uint32_t)spawn.x); + NBN_Writer_WriteUInt32(writer, (uint32_t)spawn.y); + NBN_Writer_WriteUInt32(writer, conn->id); - NBN_SerializeUInt((NBN_Stream *)&ws, x, 0, GAME_WIDTH); - NBN_SerializeUInt((NBN_Stream *)&ws, y, 0, GAME_HEIGHT); - NBN_SerializeUInt((NBN_Stream *)&ws, conn, 0, UINT_MAX); - - // Accept the connection - NBN_GameServer_AcceptIncomingConnectionWithData(data, sizeof(data)); + NBN_GameServer_AcceptIncomingConnection(); } static int HandleNewConnection(void) @@ -90,21 +88,18 @@ static int HandleNewConnection(void) // Otherwise... - NBN_ConnectionHandle client_handle; + NBN_Connection *conn; - client_handle = NBN_GameServer_GetIncomingConnection(); + conn = NBN_GameServer_GetIncomingConnection(); // Get a spawning position for the client - Vector2 spawn = spawns[client_handle % MAX_CLIENTS]; + Vector2 spawn = spawns[conn->id % MAX_CLIENTS]; // Build some "initial" data that will be sent to the connected client - unsigned int x = (unsigned int)spawn.x; - unsigned int y = (unsigned int)spawn.y; - - AcceptConnection(x, y, client_handle); + AcceptConnection(spawn, conn); - TraceLog(LOG_INFO, "Connection accepted (ID: %d)", client_handle); + TraceLog(LOG_INFO, "Connection accepted (ID: %d)", conn->id); Client *client = NULL; @@ -122,27 +117,17 @@ static int HandleNewConnection(void) assert(client != NULL); - client->client_handle = client_handle; // Store the nbnet connection ID + client->conn = conn; + conn->user_data = client; // Fill the client state with initial spawning data - client->state = (ClientState){.client_id = client_handle, .x = 200, .y = 400, .color = CLI_RED, .val = 0}; + client->state = (ClientState){.client_id = conn->id, .x = 200, .y = 400, .color = CLI_RED, .val = 0}; client_count++; return 0; } -static Client *FindClientById(uint32_t client_id) -{ - for (int i = 0; i < MAX_CLIENTS; i++) - { - if (clients[i] && clients[i]->state.client_id == client_id) - return clients[i]; - } - - return NULL; -} - static void DestroyClient(Client *client) { for (int i = 0; i < MAX_CLIENTS; i++) @@ -158,61 +143,57 @@ static void DestroyClient(Client *client) free(client); } -static void HandleClientDisconnection() +static void HandleClientDisconnection(void) { - NBN_ConnectionHandle client_handle = NBN_GameServer_GetDisconnectedClient(); // Get the disconnected client + NBN_DisconnectionInfo info = NBN_GameServer_GetDisconnectionInfo(); - TraceLog(LOG_INFO, "Client has disconnected (id: %d)", client_handle); + TraceLog(LOG_INFO, "Client has disconnected (id: %d)", info.conn_id); - Client *client = FindClientById(client_handle); + Client *client = info.user_data; assert(client); - DestroyClient(client); client_count--; } -static void HandleUpdateStateMessage(UpdateStateMessage *msg, Client *sender) +static int HandleUpdateStateMessage(Client *sender) { - // Update the state of the client with the data from the received UpdateStateMessage message - sender->state.x = msg->x; - sender->state.y = msg->y; - sender->state.val = msg->val; + // Update the state of the client with the data from the received UPDATE_STATE_MESSAGE message + NBN_Reader *reader = NBN_GameServer_GetMessageReader(); - UpdateStateMessage_Destroy(msg); + return UpdateClientStateMessage_Read(reader, &sender->state); } -static void HandleChangeColorMessage(ChangeColorMessage *msg, Client *sender) +static int HandleChangeColorMessage(Client *sender) { // Update the client color - sender->state.color = msg->color; + NBN_Reader *reader = NBN_GameServer_GetMessageReader(); - ChangeColorMessage_Destroy(msg); + return ChangeColorMessage_Read(reader, &sender->state.color); } -static void HandleReceivedMessage(void) +static int HandleReceivedMessage(void) { // Fetch info about the last received message NBN_MessageInfo msg_info = NBN_GameServer_GetMessageInfo(); - - // Find the client that sent the message - Client *sender = FindClientById(msg_info.sender); - + assert(msg_info.sender != NULL); + Client *sender = msg_info.sender->user_data; assert(sender != NULL); switch (msg_info.type) { case UPDATE_STATE_MESSAGE: // The server received a client state update - HandleUpdateStateMessage(msg_info.data, sender); - break; + return HandleUpdateStateMessage(sender); case CHANGE_COLOR_MESSAGE: // The server received a client switch color action - HandleChangeColorMessage(msg_info.data, sender); - break; + return HandleChangeColorMessage(sender); } + + // Received an unexpected message + return -1; } static int HandleGameServerEvent(int ev) @@ -232,7 +213,11 @@ static int HandleGameServerEvent(int ev) case NBN_CLIENT_MESSAGE_RECEIVED: // A message from a client has been received - HandleReceivedMessage(); + if (HandleReceivedMessage() < 0) + { + // TODO: kick client + return -1; + } break; } @@ -242,48 +227,52 @@ static int HandleGameServerEvent(int ev) // Broadcasts the latest game state to all connected clients static int BroadcastGameState(void) { - ClientState client_states[MAX_CLIENTS]; + static GameState game_state; unsigned int client_index = 0; - // Loop over the clients and build an array of ClientState + // Loop over the clients and build the game state for (int i = 0; i < MAX_CLIENTS; i++) { Client *client = clients[i]; if (client == NULL) continue; - client_states[client_index] = (ClientState) { - .client_id = client->state.client_id, - .x = client->state.x, - .y = client->state.y, - .val = client->state.val, - .color = client->state.color + game_state.client_states[client_index] = (ClientState) { + .client_id = client->state.client_id, + .x = client->state.x, + .y = client->state.y, + .val = client->state.val, + .color = client->state.color }; client_index++; } assert(client_index == client_count); - // Broadcast the game state to all clients + game_state.client_count = client_count; + + // Create a unreliable message GAME_STATE_MESSAGE and write to it + NBN_GameServer_CreateUnreliableMessage(GAME_STATE_MESSAGE); + NBN_Writer *writer = NBN_GameServer_GetMessageWriter(); + + GameStateMessage_Write(writer, &game_state); + for (int i = 0; i < MAX_CLIENTS; i++) { Client *client = clients[i]; if (client == NULL) continue; - GameStateMessage *msg = GameStateMessage_Create(); - - // Fill message data - msg->client_count = client_count; - memcpy(msg->client_states, client_states, sizeof(ClientState) * client_count); - - // Unreliably send the message to all connected clients - NBN_GameServer_SendUnreliableMessageTo(client->client_handle, GAME_STATE_MESSAGE, msg); - - // TraceLog(LOG_DEBUG, "Sent game state to client %d (%d, %d)", client->client_id, client_count, client_index); + if (NBN_GameServer_SendMessageTo(client->conn) < 0) + { + return -1; + } } return 0; + + // Broadcast the message to all connected clients + //return NBN_GameServer_BroadcastMessage(); } static bool running = true; @@ -313,7 +302,7 @@ int main(int argc, char *argv[]) } // Even though we do not display anything we still use raylib logging capacibilities - SetTraceLogLevel(LOG_DEBUG); + SetTraceLogLevel(LOG_TRACE); #ifdef __EMSCRIPTEN__ NBN_WebRTC_Register(); // Register the WebRTC driver @@ -322,30 +311,24 @@ int main(int argc, char *argv[]) #endif // __EMSCRIPTEN__ - // Start the server with a protocol name and a port - if (NBN_GameServer_StartEx(RAYLIB_EXAMPLE_PROTOCOL_NAME, RAYLIB_EXAMPLE_PORT) < 0) + // Create a server configuration with a protocol name and a port + // protocol name has to match between the server and the clients + NBN_GameServer_Config config = NBN_GameServer_CreateConfig( + RAYLIB_EXAMPLE_PROTOCOL_NAME, RAYLIB_EXAMPLE_PORT); + + // Register all the messages of our protocol + // both server and clients have to register the same messages + NBN_GameServer_RegisterMessageType(&config, CHANGE_COLOR_MESSAGE, CHANGE_COLOR_MESSAGE_MAX_LENGTH); + NBN_GameServer_RegisterMessageType(&config, UPDATE_STATE_MESSAGE, UPDATE_STATE_MESSAGE_MAX_LENGTH); + NBN_GameServer_RegisterMessageType(&config, GAME_STATE_MESSAGE, GAME_STATE_MESSAGE_MAX_LENGTH); + + // Start the server with the configuration + if (NBN_GameServer_Start(config) < 0) { TraceLog(LOG_ERROR, "Game server failed to start. Exit"); return 1; - } - - // Register messages, have to be done after NBN_GameServer_StartEx - NBN_GameServer_RegisterMessage( - CHANGE_COLOR_MESSAGE, - (NBN_MessageBuilder)ChangeColorMessage_Create, - (NBN_MessageDestructor)ChangeColorMessage_Destroy, - (NBN_MessageSerializer)ChangeColorMessage_Serialize); - NBN_GameServer_RegisterMessage( - UPDATE_STATE_MESSAGE, - (NBN_MessageBuilder)UpdateStateMessage_Create, - (NBN_MessageDestructor)UpdateStateMessage_Destroy, - (NBN_MessageSerializer)UpdateStateMessage_Serialize); - NBN_GameServer_RegisterMessage( - GAME_STATE_MESSAGE, - (NBN_MessageBuilder)GameStateMessage_Create, - (NBN_MessageDestructor)GameStateMessage_Destroy, - (NBN_MessageSerializer)GameStateMessage_Serialize); + } // Network conditions simulated variables (read from the command line, default is always 0) NBN_GameServer_SetPing(GetOptions().ping); @@ -373,7 +356,6 @@ int main(int argc, char *argv[]) break; } - // Broadcast latest game state if (BroadcastGameState() < 0) { TraceLog(LOG_ERROR, "An occured while broadcasting game states. Exit"); diff --git a/examples/raylib/shared.c b/examples/raylib/shared.c index 26fb281..553cc78 100644 --- a/examples/raylib/shared.c +++ b/examples/raylib/shared.c @@ -41,63 +41,99 @@ enum static Options options = {0}; -ChangeColorMessage *ChangeColorMessage_Create(void) +void ChangeColorMessage_Write(NBN_Writer *writer, ClientColor color) { - return malloc(sizeof(ChangeColorMessage)); + NBN_Writer_WriteUInt32(writer, (uint32_t)color); } -void ChangeColorMessage_Destroy(ChangeColorMessage *msg) +int ChangeColorMessage_Read(NBN_Reader *reader, ClientColor *color) { - free(msg); + return NBN_Reader_ReadUInt32(reader, (uint32_t *)color); } -int ChangeColorMessage_Serialize(ChangeColorMessage *msg, NBN_Stream *stream) +void UpdateClientStateMessage_Write(NBN_Writer *writer, ClientState state) { - NBN_SerializeUInt(stream, msg->color, 0, MAX_COLORS - 1); - - return 0; + NBN_Writer_WriteInt32(writer, state.x); + NBN_Writer_WriteInt32(writer, state.y); + NBN_Writer_WriteFloat(writer, state.val); } -UpdateStateMessage *UpdateStateMessage_Create(void) +int UpdateClientStateMessage_Read(NBN_Reader *reader, ClientState *state) { - return malloc(sizeof(UpdateStateMessage)); -} + if (NBN_Reader_ReadInt32(reader, &state->x) < 0) + { + return -1; + } -void UpdateStateMessage_Destroy(UpdateStateMessage *msg) -{ - free(msg); -} + if (NBN_Reader_ReadInt32(reader, &state->y) < 0) + { + return -1; + } -int UpdateStateMessage_Serialize(UpdateStateMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->x, 0, GAME_WIDTH); - NBN_SerializeUInt(stream, msg->y, 0, GAME_HEIGHT); - NBN_SerializeFloat(stream, msg->val, MIN_FLOAT_VAL, MAX_FLOAT_VAL, 3); + if (NBN_Reader_ReadFloat(reader, &state->val) < 0) + { + return -1; + } return 0; } -GameStateMessage *GameStateMessage_Create(void) +void GameStateMessage_Write(NBN_Writer *writer, GameState *state) { - return malloc(sizeof(GameStateMessage)); -} + NBN_Writer_WriteUInt32(writer, state->client_count); -void GameStateMessage_Destroy(GameStateMessage *msg) -{ - free(msg); + for (unsigned int i = 0; i < state->client_count; i++) + { + ClientState cli_state = state->client_states[i]; + + NBN_Writer_WriteUInt32(writer, cli_state.client_id); + NBN_Writer_WriteUInt32(writer, (uint32_t)cli_state.color); + NBN_Writer_WriteInt32(writer, cli_state.x); + NBN_Writer_WriteInt32(writer, cli_state.y); + NBN_Writer_WriteFloat(writer, cli_state.val); + } } -int GameStateMessage_Serialize(GameStateMessage *msg, NBN_Stream *stream) +int GameStateMessage_Read(NBN_Reader *reader, GameState *state) { - NBN_SerializeUInt(stream, msg->client_count, 0, MAX_CLIENTS); + if (NBN_Reader_ReadUInt32(reader, &state->client_count) < 0) + { + return -1; + } - for (unsigned int i = 0; i < msg->client_count; i++) + if (state->client_count > MAX_CLIENTS) { - NBN_SerializeUInt(stream, msg->client_states[i].client_id, 0, UINT_MAX); - NBN_SerializeUInt(stream, msg->client_states[i].color, 0, MAX_COLORS - 1); - NBN_SerializeUInt(stream, msg->client_states[i].x, 0, GAME_WIDTH); - NBN_SerializeUInt(stream, msg->client_states[i].y, 0, GAME_HEIGHT); - NBN_SerializeFloat(stream, msg->client_states[i].val, MIN_FLOAT_VAL, MAX_FLOAT_VAL, 3); + return -1; + } + + for (unsigned int i = 0; i < state->client_count; i++) + { + ClientState *cli_state = &state->client_states[i]; + + if (NBN_Reader_ReadUInt32(reader, &cli_state->client_id) < 0) + { + return -1; + } + + if (NBN_Reader_ReadUInt32(reader, (uint32_t *)&cli_state->color) < 0) + { + return -1; + } + + if (NBN_Reader_ReadInt32(reader, &cli_state->x) < 0) + { + return -1; + } + + if (NBN_Reader_ReadInt32(reader, &cli_state->y) < 0) + { + return -1; + } + + if (NBN_Reader_ReadFloat(reader, &cli_state->val) < 0) + { + return -1; + } } return 0; diff --git a/examples/raylib/shared.h b/examples/raylib/shared.h index a8f3617..d85e6d3 100644 --- a/examples/raylib/shared.h +++ b/examples/raylib/shared.h @@ -99,11 +99,6 @@ typedef struct tagMSG *LPMSG; #include "../../net_drivers/webrtc.h" #else #include "../../net_drivers/udp.h" - -#ifdef SOAK_WEBRTC_C_DRIVER -#include "../../net_drivers/webrtc_c.h" -#endif - #endif // __EMSCRIPTEN__ #define TICK_RATE 60 // Simulation tick rate @@ -124,6 +119,10 @@ typedef struct tagMSG *LPMSG; // A code passed by the server when closing a client connection due to being full (max client count reached) #define SERVER_FULL_CODE 42 +#define CHANGE_COLOR_MESSAGE_MAX_LENGTH 4 +#define UPDATE_STATE_MESSAGE_MAX_LENGTH 20 // ClientState is 20 bytes +#define GAME_STATE_MESSAGE_MAX_LENGTH ((20 * MAX_CLIENTS) + 4) + // Message ids enum { @@ -132,15 +131,6 @@ enum GAME_STATE_MESSAGE }; -// Messages - -typedef struct -{ - int x; - int y; - float val; -} UpdateStateMessage; - // Client colors used for ChangeColorMessage and GameStateMessage messages typedef enum { @@ -153,11 +143,6 @@ typedef enum CLI_PINK } ClientColor; -typedef struct -{ - ClientColor color; -} ChangeColorMessage; - // Client state, represents a client over the network typedef struct { @@ -168,11 +153,12 @@ typedef struct ClientColor color; } ClientState; +// Represents the state of all clients typedef struct { unsigned int client_count; ClientState client_states[MAX_CLIENTS]; -} GameStateMessage; +} GameState; // Store all options from the command line typedef struct @@ -183,19 +169,13 @@ typedef struct float jitter; } Options; -ChangeColorMessage *ChangeColorMessage_Create(void); -void ChangeColorMessage_Destroy(ChangeColorMessage *); -int ChangeColorMessage_Serialize(ChangeColorMessage *msg, NBN_Stream *); - -UpdateStateMessage *UpdateStateMessage_Create(void); -void UpdateStateMessage_Destroy(UpdateStateMessage *); -int UpdateStateMessage_Serialize(UpdateStateMessage *, NBN_Stream *); - -GameStateMessage *GameStateMessage_Create(void); -void GameStateMessage_Destroy(GameStateMessage *); -int GameStateMessage_Serialize(GameStateMessage *, NBN_Stream *); - int ReadCommandLine(int, char *[]); Options GetOptions(void); +void ChangeColorMessage_Write(NBN_Writer *writer, ClientColor color); +int ChangeColorMessage_Read(NBN_Reader *reader, ClientColor *color); +void UpdateClientStateMessage_Write(NBN_Writer *writer, ClientState state); +int UpdateClientStateMessage_Read(NBN_Reader *reader, ClientState *state); +void GameStateMessage_Write(NBN_Writer *writer, GameState *state); +int GameStateMessage_Read(NBN_Reader *reader, GameState *state); #endif /* RAYLIB_EXAMPLE_SHARED_H */ diff --git a/examples/rpc/CMakeLists.txt b/examples/rpc/CMakeLists.txt deleted file mode 100644 index 2eb13d3..0000000 --- a/examples/rpc/CMakeLists.txt +++ /dev/null @@ -1,59 +0,0 @@ -cmake_minimum_required(VERSION 3.1) - -project(rpc) - -option(CPP_COMPILE OFF) - -# allow to compile as cpp -if (CPP_COMPILE) - file(GLOB_RECURSE CFILES "${CMAKE_SOURCE_DIR}/*.c") - SET_SOURCE_FILES_PROPERTIES(${CFILES} PROPERTIES LANGUAGE CXX) - set (CMAKE_CXX_STANDARD 20) -endif (CPP_COMPILE) - -unset(CPP_COMPILE) - -add_executable(rpc_client client.c shared.c) -add_executable(rpc_server server.c shared.c) - -add_compile_options(-Wall -Wextra -Wpedantic) - -target_compile_definitions(rpc_client PUBLIC NBN_DEBUG) -target_compile_definitions(rpc_server PUBLIC NBN_DEBUG) - -option(WEBRTC_DRIVER_C OFF) - -if(WIN32) - target_link_libraries(rpc_client wsock32 ws2_32) - target_link_libraries(rpc_server wsock32 ws2_32) -else() - # link with pthread when we are not on windows - target_link_libraries(rpc_client pthread) - target_link_libraries(rpc_server pthread) -endif(WIN32) - -if (UNIX) - # link with libm on unix - target_link_libraries(rpc_client m) - target_link_libraries(rpc_server m) -endif (UNIX) - -if (EMSCRIPTEN) - set(ASYNCIFY_IMPORTS "[\"__js_game_server_start\", \"__js_game_client_start\", \"__js_game_client_close\"]") - - set_target_properties(rpc_server PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/api.js \ - -s ALLOW_MEMORY_GROWTH=1 \ - -s TOTAL_MEMORY=30MB \ - -s EXIT_RUNTIME=1 \ - -s ASSERTIONS=1 \ - -s ASYNCIFY \ - -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") - - set_target_properties(rpc_client PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/api.js \ - -s ALLOW_MEMORY_GROWTH=1 \ - -s TOTAL_MEMORY=30MB \ - -s EXIT_RUNTIME=1 \ - -s ASSERTIONS=1 \ - -s ASYNCIFY \ - -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") -endif() diff --git a/examples/rpc/README.md b/examples/rpc/README.md deleted file mode 100644 index e6d7dd6..0000000 --- a/examples/rpc/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# RPC - -Simple example demonstrating how to use RPCs. - -Use the CMake script to compile the example: - -``` -cmake . -make -``` - -To run the server simply do: - -`./rpc_server` - -and to run the client: - -`./rpc_client` - -## WebRTC - -Use the CMake script to compile the example: - -``` -EMSCRIPTEN=1 cmake . -make -``` - -To run this example you need to have nodejs installed (see the package.json file). - -To run the server simply do: - -`npm run server` - -and to run the client: - -`npm run client` diff --git a/examples/rpc/client.c b/examples/rpc/client.c deleted file mode 100644 index f27fe55..0000000 --- a/examples/rpc/client.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#include -#include -#include - -// Has to be defined in exactly *one* source file before including the nbnet header -#define NBNET_IMPL - -#include "shared.h" - -static bool running = true; -static bool connected = false; -static bool disconnected = false; - -static void TestRPC2(unsigned int param_count, NBN_RPC_Param params[NBN_RPC_MAX_PARAM_COUNT], NBN_ConnectionHandle sender) -{ - Log(LOG_INFO, "TestRPC2 called !"); - Log(LOG_INFO, "Parameter 1 (float): %f", NBN_RPC_GetFloat(params, 0)); - Log(LOG_INFO, "Parameter 2 (string): %s", NBN_RPC_GetString(params, 1)); -} - -void OnConnected(void) -{ - Log(LOG_INFO, "Connected"); - - connected = true; - - int ret = NBN_GameClient_CallRPC(TEST_RPC_ID, 4242, -1234.5678f, true); - - assert(ret == 0); -} - -void OnDisconnected(void) -{ - Log(LOG_INFO, "Disconnected"); - - // Stop the main loop - disconnected = true; - running = false; -} - -int main(int argc, char *argv[]) -{ -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register(); // Register the WebRTC driver -#else - NBN_UDP_Register(); // Register the UDP driver -#endif // __EMSCRIPTEN__ - - // Initialize the client with a protocol name (must be the same than the one used by the server), the server ip address and port - - if (NBN_GameClient_StartEx(RPC_PROTOCOL_NAME, "127.0.0.1", RPC_EXAMPLE_PORT, NULL, 0) < 0) - { - Log(LOG_ERROR, "Failed to start client"); - -// Error, quit the client application -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(1); -#else - return 1; -#endif - } - - int ret = NBN_GameClient_RegisterRPC(TEST_RPC_ID, TEST_RPC_SIGNATURE, NULL); - - assert(ret == 0); - - ret = NBN_GameClient_RegisterRPC(TEST_RPC_2_ID, TEST_RPC_2_SIGNATURE, TestRPC2); - - assert(ret == 0); - - // Number of seconds between client ticks - double dt = 1.0 / RPC_TICK_RATE; - - while (running) - { - int ev; - - // Poll for client events - while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { - Log(LOG_ERROR, "An error occured while polling client events. Exit"); - - // Stop main loop - running = false; - break; - } - - switch (ev) - { - // Client is connected to the server - case NBN_CONNECTED: - OnConnected(); - break; - - // Client has disconnected from the server - case NBN_DISCONNECTED: - OnDisconnected(); - break; - } - } - - if (disconnected) - break; - - // Pack all enqueued messages as packets and send them - if (NBN_GameClient_SendPackets() < 0) - { - Log(LOG_ERROR, "Failed to send packets. Exit"); - - // Stop main loop - running = false; - break; - } - - // Cap the client tick rate - ExampleSleep(dt); - } - - // Stop the client - NBN_GameClient_Stop(); - -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(0); -#else - return 0; -#endif -} diff --git a/examples/rpc/package-lock.json b/examples/rpc/package-lock.json deleted file mode 100644 index 4e06a55..0000000 --- a/examples/rpc/package-lock.json +++ /dev/null @@ -1,865 +0,0 @@ -{ - "requires": true, - "lockfileVersion": 1, - "dependencies": { - "nbnet": { - "version": "file:../../net_drivers/webrtc", - "requires": { - "websocket": "^1.0.31", - "winston": "^3.2.1", - "wrtc": "^0.4.2" - }, - "dependencies": { - "@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "requires": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "optional": true, - "requires": { - "webidl-conversions": "^4.0.2" - } - }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "requires": { - "type": "^2.0.0" - }, - "dependencies": { - "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - } - } - }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "requires": { - "minipass": "^2.6.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "requires": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "needle": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", - "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==" - }, - "node-pre-gyp": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", - "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "requires": { - "fn.name": "1.x.x" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "optional": true - }, - "websocket": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.32.tgz", - "integrity": "sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q==", - "requires": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - } - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "requires": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - } - }, - "winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "requires": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "wrtc": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.6.tgz", - "integrity": "sha512-4uD+oFoY2yuo3AV9fum3cXUXR6v8YQHZlqBrKkCRGjW1BvKrVHtLNH4UaNLBLiJu9DL89WqUWmbzsQ9RxMzANw==", - "requires": { - "domexception": "^1.0.1", - "node-pre-gyp": "^0.13.0" - } - }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - } - } -} diff --git a/examples/rpc/package.json b/examples/rpc/package.json deleted file mode 100644 index fe7f3c6..0000000 --- a/examples/rpc/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "scripts": { - "server": "node rpc_server.js", - "client": "node rpc_client.js" - }, - "dependencies": { - "nbnet": "file:../../net_drivers/webrtc" - } -} diff --git a/examples/rpc/server.c b/examples/rpc/server.c deleted file mode 100644 index ea28581..0000000 --- a/examples/rpc/server.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#include -#include -#include - -// Has to be defined in exactly *one* source file before including the nbnet header -#define NBNET_IMPL - -#include "shared.h" - -static NBN_Connection *client = NULL; - -static bool error = false; - -static void TestRPC(unsigned int param_count, NBN_RPC_Param params[NBN_RPC_MAX_PARAM_COUNT], NBN_ConnectionHandle sender) -{ - Log(LOG_INFO, "TestRPC called ! (Sender: %d)", sender); - Log(LOG_INFO, "Parameter 1 (int): %d", NBN_RPC_GetInt(params, 0)); - Log(LOG_INFO, "Parameter 2 (float): %f", NBN_RPC_GetFloat(params, 1)); - Log(LOG_INFO, "Parameter 3 (bool): %d", NBN_RPC_GetBool(params, 2)); - - NBN_GameServer_CallRPC( - TEST_RPC_2_ID, - sender, - NBN_RPC_GetInt(params, 0) * NBN_RPC_GetFloat(params, 1), - "Some test string"); -} - -int main(void) -{ -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register(); // Register the WebRTC driver -#else - NBN_UDP_Register(); // Register the UDP driver -#endif // __EMSCRIPTEN__ - - if (NBN_GameServer_StartEx(RPC_PROTOCOL_NAME, RPC_EXAMPLE_PORT) < 0) - { - Log(LOG_ERROR, "Failed to start the server"); - - // Error, quit the server application -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(1); -#else - return 1; -#endif - } - - int ret = NBN_GameServer_RegisterRPC(TEST_RPC_ID, TEST_RPC_SIGNATURE, TestRPC); - - assert(ret == 0); - - ret = NBN_GameServer_RegisterRPC(TEST_RPC_2_ID, TEST_RPC_2_SIGNATURE, NULL); - - assert(ret == 0); - - // Number of seconds between server ticks - double dt = 1.0 / RPC_TICK_RATE; - - while (true) - { - int ev; - - // Poll for server events - while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { - Log(LOG_ERROR, "Something went wrong"); - - // Error, quit the server application - error = true; - break; - } - - switch (ev) - { - // New connection request... - case NBN_NEW_CONNECTION: - NBN_GameServer_AcceptIncomingConnection(); - - break; - - // A client has disconnected - case NBN_CLIENT_DISCONNECTED: - break; - } - } - - // Pack all enqueued messages as packets and send them - if (NBN_GameServer_SendPackets() < 0) - { - Log(LOG_ERROR, "Failed to send packets"); - - // Error, quit the server application - error = true; - break; - } - - // Cap the server tick rate - ExampleSleep(dt); - } - - // Stop the server - NBN_GameServer_Stop(); - - ret = error ? 1 : 0; - -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(ret); -#else - return ret; -#endif -} diff --git a/examples/rpc/shared.c b/examples/rpc/shared.c deleted file mode 100644 index 88becd9..0000000 --- a/examples/rpc/shared.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#include -#include -#include - -// Sleep function -#if defined(__EMSCRIPTEN__) -#include -#elif defined(_WIN32) || defined(_WIN64) -#include -#include -#include -#else -#include -#endif - -#include "shared.h" - -// Sleep for a given amount of seconds -// Used to limit client and server tick rate -void ExampleSleep(double sec) -{ -#if defined(__EMSCRIPTEN__) - emscripten_sleep(sec * 1000); -#elif defined(_WIN32) || defined(_WIN64) - Sleep(sec * 1000); -#else /* UNIX / OSX */ - long nanos = sec * 1e9; - struct timespec t = {.tv_sec = nanos / 999999999, .tv_nsec = nanos % 999999999}; - - nanosleep(&t, &t); -#endif -} - -static const char *log_type_strings[] = { - "INFO", - "ERROR", - "DEBUG", - "TRACE" -}; - -// Basic logging function -void Log(int type, const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - - printf("[%s] ", log_type_strings[type]); - vprintf(fmt, args); - printf("\n"); - - va_end(args); -} diff --git a/examples/rpc/shared.h b/examples/rpc/shared.h deleted file mode 100644 index 7dfc074..0000000 --- a/examples/rpc/shared.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#ifndef RPC_EXAMPLE_SHARED_H -#define RPC_EXAMPLE_SHARED_H - -#if defined(_WIN32) || defined(_WIN64) - -/* - * The following defines are meant to avoid conflicts between raylib and windows.h - * https://github.com/raysan5/raylib/issues/857 - */ - -// If defined, the following flags inhibit definition of the indicated items -#define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_ -#define NOVIRTUALKEYCODES // VK_* -#define NOWINMESSAGES // WM_*, EM_*, LB_*, CB_* -#define NOWINSTYLES // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_* -#define NOSYSMETRICS // SM_* -#define NOMENUS // MF_* -#define NOICONS // IDI_* -#define NOKEYSTATES // MK_* -#define NOSYSCOMMANDS // SC_* -#define NORASTEROPS // Binary and Tertiary raster ops -#define NOSHOWWINDOW // SW_* -#define OEMRESOURCE // OEM Resource values -#define NOATOM // Atom Manager routines -#define NOCLIPBOARD // Clipboard routines -#define NOCOLOR // Screen colors -#define NOCTLMGR // Control and Dialog routines -#define NODRAWTEXT // DrawText() and DT_* -#define NOGDI // All GDI defines and routines -#define NOKERNEL // All KERNEL defines and routines -#define NOUSER // All USER defines and routines -/*#define NONLS // All NLS defines and routines*/ -#define NOMB // MB_* and MessageBox() -#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines -#define NOMETAFILE // typedef METAFILEPICT -#define NOMINMAX // Macros min(a,b) and max(a,b) -#define NOMSG // typedef MSG and associated routines -#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_* -#define NOSCROLL // SB_* and scrolling routines -#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc. -#define NOSOUND // Sound driver routines -#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines -#define NOWH // SetWindowsHook and WH_* -#define NOWINOFFSETS // GWL_*, GCL_*, associated routines -#define NOCOMM // COMM driver routines -#define NOKANJI // Kanji support stuff. -#define NOHELP // Help engine interface. -#define NOPROFILER // Profiler interface. -#define NODEFERWINDOWPOS // DeferWindowPos routines -#define NOMCX // Modem Configuration Extensions - -// Type required before windows.h inclusion -typedef struct tagMSG *LPMSG; - -#include // Has to be included before windows.h -#include - -#endif // WINDOWS - -#define RPC_PROTOCOL_NAME "rpc-example" -#define RPC_EXAMPLE_PORT 42042 -#define RPC_TICK_RATE 30 -#define TEST_RPC_ID 0 -#define TEST_RPC_2_ID 1 -#define TEST_RPC_SIGNATURE NBN_RPC_BuildSignature(3, NBN_RPC_PARAM_INT, NBN_RPC_PARAM_FLOAT, NBN_RPC_PARAM_BOOL) -#define TEST_RPC_2_SIGNATURE NBN_RPC_BuildSignature(2, NBN_RPC_PARAM_FLOAT, NBN_RPC_PARAM_STRING) - -// nbnet logging -// nbnet does not implement any logging capabilities, you need to provide your own -enum -{ - LOG_INFO, - LOG_ERROR, - LOG_DEBUG, - LOG_TRACE, - LOG_WARNING -}; - -#define NBN_LogInfo(...) Log(LOG_INFO, __VA_ARGS__) -#define NBN_LogError(...) Log(LOG_ERROR, __VA_ARGS__) -#define NBN_LogDebug(...) Log(LOG_DEBUG, __VA_ARGS__) -#define NBN_LogWarning(...) Log(LOG_WARNING, __VA_ARGS__) -#define NBN_LogTrace(...) (void)0; - -void Log(int, const char *, ...); - -#include "../../nbnet.h" - -#ifdef __EMSCRIPTEN__ -#include "../../net_drivers/webrtc.h" -#else -#include "../../net_drivers/udp.h" -#endif - -void ExampleSleep(double); - -#endif /* RPC_EXAMPLE_SHARED_H */ diff --git a/nbnet.h b/nbnet.h index 0eb9ac7..a9ac42b 100644 --- a/nbnet.h +++ b/nbnet.h @@ -25,20 +25,17 @@ #ifndef NBNET_H #define NBNET_H -#include -#include +#include #include -#include +#include +#include #include -#include -#include -#include #if defined(_WIN32) || defined(_WIN64) -#include -#include #include +#include +#include #define NBNET_WINDOWS @@ -70,33 +67,43 @@ #endif #ifndef NBN_Assert -#define NBN_Assert(cond) \ -{ \ - if (!(cond)) { \ - NBN_LogError(#cond); \ - NBN_Abort(); \ - } \ -} +#define NBN_Assert(cond) \ + { \ + if (!(cond)) { \ + NBN_LogError(#cond); \ + NBN_Abort(); \ + } \ + } #endif #ifndef NBN_LogError -#define NBN_LogError(...) do {} while (0) +#define NBN_LogError(...) \ + do { \ + } while (0) #endif #ifndef NBN_LogInfo -#define NBN_LogInfo(...) do {} while (0) +#define NBN_LogInfo(...) \ + do { \ + } while (0) #endif #ifndef NBN_LogDebug -#define NBN_LogDebug(...) do {} while (0) +#define NBN_LogDebug(...) \ + do { \ + } while (0) #endif #ifndef NBN_LogWarning -#define NBN_LogWarning(...) do {} while (0) +#define NBN_LogWarning(...) \ + do { \ + } while (0) #endif #ifndef NBN_LogTrace -#define NBN_LogTrace(...) do {} while (0) +#define NBN_LogTrace(...) \ + do { \ + } while (0) #endif #define NBN_ERROR -1 @@ -108,8 +115,7 @@ typedef struct NBN_Driver NBN_Driver; #pragma region NBN_ConnectionVector -typedef struct NBN_ConnectionVector -{ +typedef struct NBN_ConnectionVector { NBN_Connection **connections; unsigned int count; unsigned int capacity; @@ -117,56 +123,8 @@ typedef struct NBN_ConnectionVector #pragma endregion // NBN_ConnectionVector -#pragma region NBN_ConnectionTable - -typedef struct NBN_ConnectionTable -{ - NBN_Connection **connections; - unsigned int capacity; - unsigned int count; - float load_factor; -} NBN_ConnectionTable; - -#pragma endregion // NBN_ConnectionTable - -#pragma region Memory management - -enum -{ - NBN_MEM_MESSAGE_CHUNK, - NBN_MEM_CONNECTION, - -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - NBN_MEM_PACKET_SIMULATOR_ENTRY -#endif -}; - -typedef struct NBN_MemPoolFreeBlock -{ - struct NBN_MemPoolFreeBlock *next; -} NBN_MemPoolFreeBlock; - -typedef struct NBN_MemPool -{ - uint8_t **blocks; - size_t block_size; - unsigned int block_count; - unsigned int block_idx; - NBN_MemPoolFreeBlock *free; -} NBN_MemPool; - -typedef struct NBN_MemoryManager -{ -#ifdef NBN_DISABLE_MEMORY_POOLING - size_t mem_sizes[16]; -#else - NBN_MemPool mem_pools[16]; -#endif /* NBN_DISABLE_MEMORY_POOLING */ -} NBN_MemoryManager; - -extern NBN_MemoryManager nbn_mem_manager; - -#pragma endregion /* Memory management */ +typedef uint8_t *(*NBN_MessageAllocator)(uint8_t type, uint16_t *length); +typedef void (*NBN_MessageDeallocator)(uint8_t type, uint8_t *data); #pragma region Serialization @@ -176,15 +134,13 @@ extern NBN_MemoryManager nbn_mem_manager; #define B_IS_SET(mask, n) ((B_MASK(n) & mask) == B_MASK(n)) #define B_IS_UNSET(mask, n) ((B_MASK(n) & mask) == 0) -typedef struct NBN_Writer -{ +typedef struct NBN_Writer { uint8_t *buffer; unsigned int length; unsigned int position; } NBN_Writer; -typedef struct NBN_Reader -{ +typedef struct NBN_Reader { uint8_t *buffer; unsigned int length; unsigned int position; @@ -210,28 +166,24 @@ int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length #pragma region NBN_Message -#define NBN_MAX_CHANNELS 3 // TODO: find a way to support more user-defined channels -#define NBN_MAX_MESSAGE_TYPES 255 /* Maximum value of uint8_t, see message header */ +#define NBN_MAX_MESSAGE_TYPES UINT8_MAX #define NBN_RESERVED_MESSAGE_TYPES 4 /* Number of message types reserved for the library */ -/* Range of message types that can be used by user code */ -#define NBN_USER_MESSAGE_TYPE_RANGE (NBN_MAX_MESSAGE_TYPES - NBN_RESERVED_MESSAGE_TYPES) #define NBN_MESSAGE_RESEND_DELAY 0.1 /* Number of seconds before a message is resent (reliable messages redundancy) */ -#define NBN_MESSAGE_HEADER_SIZE 6 /* See NBN_MessageHeader struct */ -#define NBN_LIBRARY_MESSAGE_POOL_INITIAL_SIZE 32 +#define NBN_MESSAGE_HEADER_SIZE 6 /* See NBN_MessageHeader struct */ +#define NBN_RESERVED_MESSAGE_BUFFER_LEN 32 /* Fixed size used for all library reserved messages' buffer */ +#define NBN_SCRATCH_WRITE_BUFFER_LEN 256 // IMPORTANT: make sure you update NBN_MESSAGE_HEADER_SIZE if you modify NBN_MessageHeader struct -typedef struct NBN_MessageHeader -{ +typedef struct NBN_MessageHeader { uint16_t id; uint16_t length; uint8_t type; uint8_t channel_id; } NBN_MessageHeader; -typedef enum { NBN_OUTGOING_MESSAGE, NBN_INCOMING_MSG } NBN_MessageType; +typedef enum { NBN_OUTGOING_MESSAGE, NBN_INCOMING_MESSAGE } NBN_MessageType; -typedef struct NBN_Message -{ +typedef struct NBN_Message { NBN_MessageHeader header; NBN_MessageType type; NBN_Connection *sender; @@ -239,15 +191,13 @@ typedef struct NBN_Message uint8_t *data; } NBN_Message; -typedef struct NBN_OutgoingMessage -{ +typedef struct NBN_OutgoingMessage { NBN_Message *message; uint16_t id; double last_send_time; } NBN_OutgoingMessage; -typedef struct NBN_IncomingMessage -{ +typedef struct NBN_IncomingMessage { NBN_Message message; bool free; } NBN_IncomingMessage; @@ -255,8 +205,7 @@ typedef struct NBN_IncomingMessage /** * Information about a received message. */ -typedef struct NBN_MessageInfo -{ +typedef struct NBN_MessageInfo { /** User defined message type */ uint8_t type; @@ -271,9 +220,9 @@ typedef struct NBN_MessageInfo /** * The message's sender. - * + * * On the client side, it will always be NULL as all received messages come from the game server - */ + */ NBN_Connection *sender; } NBN_MessageInfo; @@ -281,10 +230,10 @@ typedef struct NBN_MessageInfo #pragma region NBN_Packet -/* +/* * Maximum allowed packet size (including header) in bytes. * The 1400 value has been chosen based on this statement: - * + * * With the IPv4 header being 20 bytes and the UDP header being 8 bytes, the payload * of a UDP packet should be no larger than 1500 - 20 - 8 = 1472 bytes to avoid fragmentation. */ @@ -296,34 +245,27 @@ typedef struct NBN_MessageInfo /* Maximum size of packet's data (NBN_PACKET_MAX_DATA_SIZE + NBN_PACKET_HEADER_SIZE = NBN_PACKET_MAX_SIZE) */ #define NBN_PACKET_MAX_DATA_SIZE (NBN_PACKET_MAX_SIZE - NBN_PACKET_HEADER_SIZE) -enum -{ +enum { NBN_PACKET_WRITE_ERROR = -1, NBN_PACKET_WRITE_OK, NBN_PACKET_WRITE_NO_SPACE, }; -typedef enum NBN_PacketMode -{ - NBN_PACKET_MODE_WRITE = 1, - NBN_PACKET_MODE_READ -} NBN_PacketMode; +typedef enum NBN_PacketMode { NBN_PACKET_MODE_WRITE = 1, NBN_PACKET_MODE_READ } NBN_PacketMode; // IMPORTANT: don't forget to update NBN_PACKET_HEADER_SIZE after modifying this structure -typedef struct NBN_PacketHeader -{ +typedef struct NBN_PacketHeader { uint32_t protocol_id; uint32_t ack_bits; uint16_t seq_number; uint16_t ack; - uint8_t messages_count; + uint8_t messages_count; } NBN_PacketHeader; -typedef struct NBN_Packet -{ +typedef struct NBN_Packet { NBN_PacketHeader header; NBN_PacketMode mode; - struct NBN_Connection *sender; /* not serialized, fill by the network driver upon reception */ + struct NBN_Connection *sender; /* not serialized, filled by the network driver upon reception */ uint8_t buffer[NBN_PACKET_MAX_SIZE]; unsigned int size; /* in bytes */ bool sealed; @@ -331,7 +273,7 @@ typedef struct NBN_Packet void NBN_Packet_InitWrite(NBN_Packet *, uint32_t, uint16_t, uint16_t, uint32_t); int NBN_Packet_WriteMessage(NBN_Packet *, NBN_OutgoingMessage *); -int NBN_Packet_Seal(NBN_Packet *, NBN_Connection *); +int NBN_Packet_Seal(NBN_Packet *); int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); #pragma endregion /* NBN_Packet */ @@ -344,12 +286,6 @@ int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); #define NBN_DISCONNECTION_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 2) #define NBN_CONNECTION_REQUEST_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 3) -/* - * Chunk max size is the number of bytes of data a packet can hold minus the size of a message header minus 2 bytes - * to hold the chunk id and total number of chunks. - */ -#define NBN_MESSAGE_CHUNK_SIZE (NBN_PACKET_MAX_DATA_SIZE - NBN_MESSAGE_HEADER_SIZE - 2) - #define NBN_SERVER_DATA_MAX_SIZE 256 #define NBN_CONNECTION_DATA_MAX_SIZE 256 @@ -357,90 +293,41 @@ int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); #pragma region NBN_Channel +#define NBN_MAX_CUSTOM_CHANNELS 8 #define NBN_CHANNEL_BUFFER_SIZE 1024 -#define NBN_CHANNEL_CHUNKS_BUFFER_SIZE 255 -#define NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE 2048 #define NBN_CHANNEL_OUTGOING_MESSAGE_POOL_SIZE 512 -/* IMPORTANT: if you add a library reserved channel below, make sure to update NBN_LIBRARY_RESERVED_CHANNELS */ - /* Library reserved unreliable ordered channel */ -#define NBN_CHANNEL_RESERVED_UNRELIABLE 0 +#define NBN_CHANNEL_RESERVED_UNRELIABLE 0 /* Library reserved reliable ordered channel */ #define NBN_CHANNEL_RESERVED_RELIABLE 1 -typedef NBN_Channel *(*NBN_ChannelBuilder)(void); -typedef void (*NBN_ChannelDestructor)(NBN_Endpoint *, NBN_Channel *); +typedef enum NBN_ChannelType { NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_RELIABLE } NBN_ChannelType; -struct NBN_Channel -{ +struct NBN_Channel { uint8_t id; - // uint8_t *write_chunk_buffer; + NBN_ChannelType type; uint16_t next_outgoing_message_id; uint16_t next_recv_message_id; + uint16_t oldest_unacked_message_id; + uint16_t most_recent_message_id; + uint16_t last_received_message_id; + unsigned int next_outgoing_message_slot; unsigned int outgoing_message_count; - unsigned int chunk_count; - unsigned int write_chunk_buffer_size; - unsigned int read_chunk_buffer_size; - unsigned int next_outgoing_chunked_message; - int last_received_chunk_id; - // uint8_t *read_chunk_buffer; - NBN_ChannelDestructor destructor; - NBN_Connection *connection; NBN_OutgoingMessage outgoing_messages_buffer[NBN_CHANNEL_BUFFER_SIZE]; NBN_IncomingMessage incoming_messages_buffer[NBN_CHANNEL_BUFFER_SIZE]; - // NBN_MessageChunk *recv_chunk_buffer[NBN_CHANNEL_CHUNKS_BUFFER_SIZE]; - - bool (*AddReceivedMessage)(NBN_Endpoint *, NBN_Channel *, NBN_Message *); - bool (*AddOutgoingMessage)(NBN_Channel *, NBN_Message *); - NBN_Message *(*GetNextRecvedMessage)(NBN_Channel *); - bool (*GetNextOutgoingMessage)(NBN_Channel *, NBN_OutgoingMessage *, double); - int (*OnOutgoingMessageAcked)(NBN_Endpoint *, NBN_Channel *, uint16_t); - int (*OnOutgoingMessageSent)(NBN_Endpoint *, NBN_Channel *, NBN_Message *); + bool ack_buffer[NBN_CHANNEL_BUFFER_SIZE]; }; void NBN_Channel_Destroy(NBN_Endpoint *, NBN_Channel *); -// bool NBN_Channel_AddChunk(NBN_Channel *, NBN_Message *); -int NBN_Channel_ReconstructMessageFromChunks(NBN_Channel *, NBN_Connection *, NBN_Message *); -/*void NBN_Channel_ResizeWriteChunkBuffer(NBN_Channel *, unsigned int);*/ -/*void NBN_Channel_ResizeReadChunkBuffer(NBN_Channel *, unsigned int);*/ static void Channel_UpdateMessageSendTime(NBN_Channel *channel, uint16_t msg_id, double time); -/* - Unreliable ordered - - Guarantee that messages will be received in order, does not however guarantee that all message will be received when - packets get lost. This is meant to be used for time critical messages when it does not matter that much if they - end up getting lost. A good example would be game snaphosts when any newly received snapshot is more up to date - than the previous one. - */ -typedef struct NBN_UnreliableOrderedChannel -{ +typedef struct NBN_UnreliableOrderedChannel { NBN_Channel base; - uint16_t last_received_message_id; - unsigned int next_outgoing_message_slot; } NBN_UnreliableOrderedChannel; -NBN_UnreliableOrderedChannel *NBN_UnreliableOrderedChannel_Create(void); - -/* - Reliable ordered - - Will guarantee that all messages will be received in order. Use this when you want to make sure a message - will be received, for example for chat messages or initial game world state. - */ -typedef struct NBN_ReliableOrderedChannel -{ - NBN_Channel base; - uint16_t oldest_unacked_message_id; - uint16_t most_recent_message_id; - bool ack_buffer[NBN_CHANNEL_BUFFER_SIZE]; -} NBN_ReliableOrderedChannel; - -NBN_ReliableOrderedChannel *NBN_ReliableOrderedChannel_Create(void); - #pragma endregion /* NBN_Channel */ #pragma region NBN_Connection @@ -450,20 +337,18 @@ NBN_ReliableOrderedChannel *NBN_ReliableOrderedChannel_Create(void); /* Maximum number of packets that can be sent in a single flush * * IMPORTANT: do not increase this, it will break packet acks -*/ + */ #define NBN_CONNECTION_MAX_SENT_PACKET_COUNT 16 /* Number of seconds before the connection is considered stale and get closed */ #define NBN_CONNECTION_STALE_TIME_THRESHOLD 3 -typedef struct NBN_MessageEntry -{ +typedef struct NBN_MessageEntry { uint16_t id; uint8_t channel_id; } NBN_MessageEntry; -typedef struct NBN_PacketEntry -{ +typedef struct NBN_PacketEntry { bool acked; bool flagged_as_lost; unsigned int messages_count; @@ -471,8 +356,7 @@ typedef struct NBN_PacketEntry NBN_MessageEntry messages[NBN_MAX_MESSAGES_PER_PACKET]; } NBN_PacketEntry; -typedef struct NBN_ConnectionStats -{ +typedef struct NBN_ConnectionStats { double ping; unsigned int total_lost_packets; float packet_loss; @@ -482,34 +366,34 @@ typedef struct NBN_ConnectionStats #ifdef NBN_DEBUG -typedef enum NBN_ConnectionDebugCallback -{ - NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE -} NBN_ConnectionDebugCallback; +typedef enum NBN_ConnectionDebugCallback { NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE } NBN_ConnectionDebugCallback; #endif /* NBN_DEBUG */ -struct NBN_Connection -{ +struct NBN_Connection { uint32_t id; - double last_recv_packet_time; /* Used to detect stale connections */ - double last_flush_time; /* Last time the send queue was flushed */ + double last_recv_packet_time; /* Used to detect stale connections */ + double last_flush_time; /* Last time the send queue was flushed */ double last_read_packets_time; /* Last time packets were read from the network driver */ - unsigned int downloaded_bytes; /* Keep track of bytes read from the socket (used for download bandwith calculation) */ - int vector_pos; /* Position of the connection in the connections vector */ - uint8_t is_accepted : 1; - uint8_t is_stale : 1; - uint8_t is_closed : 1; + unsigned int + downloaded_bytes; /* Keep track of bytes read from the socket (used for download bandwith calculation) */ + int vector_pos; /* Position of the connection in the connections vector */ + uint8_t is_accepted : 1; + uint8_t is_stale : 1; + uint8_t is_closed : 1; struct NBN_Endpoint *endpoint; NBN_Driver *driver; /* Network driver used for that connection */ - NBN_Channel *channels[NBN_MAX_CHANNELS]; /* Messages channeling (sending & receiving) */ + // TODO: do we need to allocate channels on the heap? + NBN_Channel **channels; /* Message channels (sending & receiving) */ + unsigned int channel_count; NBN_ConnectionStats stats; void *driver_data; /* Data attached to the connection by the underlying driver */ + void *user_data; /* Pointer to user-defined data */ #ifdef NBN_DEBUG /* Debug callbacks */ void (*OnMessageAddedToRecvQueue)(struct NBN_Connection *, NBN_Message *); // TODO: rename this function pointer -#endif /* NBN_DEBUG */ +#endif /* NBN_DEBUG */ /* * Packet sequencing & acking @@ -524,8 +408,7 @@ struct NBN_Connection typedef struct NBN_ConnectionListNode NBN_ConnectionListNode; /* Linked list of connections */ -struct NBN_ConnectionListNode -{ +struct NBN_ConnectionListNode { NBN_Connection *conn; NBN_ConnectionListNode *next; NBN_ConnectionListNode *prev; @@ -538,31 +421,33 @@ NBN_Connection *NBN_Connection_Create(uint32_t, NBN_Endpoint *, NBN_Driver *, vo void NBN_Connection_Destroy(NBN_Endpoint *, NBN_Connection *); int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); int NBN_Connection_FlushChannels(NBN_Endpoint *, NBN_Connection *, uint32_t, double); -int NBN_Connection_InitChannel(NBN_Connection *, NBN_Channel *); bool NBN_Connection_CheckIfStale(NBN_Connection *, double); #pragma endregion /* NBN_Connection */ #pragma region NBN_EventQueue -#define NBN_NO_EVENT 0 /* No event left in the events queue */ +#define NBN_NO_EVENT 0 /* No event left in the events queue */ #define NBN_SKIP_EVENT 1 /* Indicates that the event should be skipped */ #define NBN_EVENT_QUEUE_CAPACITY 1024 -typedef union NBN_EventData -{ +typedef struct NBN_DisconnectionInfo { + uint32_t conn_id; /* ID if the disconnected connection */ + void *user_data; /* User-defined data associated with this connection */ +} NBN_DisconnectionInfo; + +typedef union NBN_EventData { NBN_MessageInfo message_info; NBN_Connection *connection; + NBN_DisconnectionInfo disconnection; } NBN_EventData; -typedef struct NBN_Event -{ +typedef struct NBN_Event { int type; NBN_EventData data; } NBN_Event; -typedef struct NBN_EventQueue -{ +typedef struct NBN_EventQueue { NBN_Event events[NBN_EVENT_QUEUE_CAPACITY]; unsigned int head; unsigned int tail; @@ -589,22 +474,29 @@ bool NBN_EventQueue_IsEmpty(NBN_EventQueue *); */ #ifndef NBNET_WINDOWS #include // Threading -#endif /* NBNET_WINDOWS */ - -#define NBN_GameClient_SetPing(v) { nbn_game_client.endpoint.packet_simulator.ping = v; } -#define NBN_GameClient_SetJitter(v) { nbn_game_client.endpoint.packet_simulator.jitter = v; } -#define NBN_GameClient_SetPacketLoss(v) { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } -#define NBN_GameClient_SetPacketDuplication(v) { nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; } - -#define NBN_GameServer_SetPing(v) { nbn_game_server.endpoint.packet_simulator.ping = v; } -#define NBN_GameServer_SetJitter(v) { nbn_game_server.endpoint.packet_simulator.jitter = v; } -#define NBN_GameServer_SetPacketLoss(v) { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } -#define NBN_GameServer_SetPacketDuplication(v) { nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; } +#endif /* NBNET_WINDOWS */ + +#define NBN_GameClient_SetPing(v) \ + { nbn_game_client.endpoint.packet_simulator.ping = v; } +#define NBN_GameClient_SetJitter(v) \ + { nbn_game_client.endpoint.packet_simulator.jitter = v; } +#define NBN_GameClient_SetPacketLoss(v) \ + { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } +#define NBN_GameClient_SetPacketDuplication(v) \ + { nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; } + +#define NBN_GameServer_SetPing(v) \ + { nbn_game_server.endpoint.packet_simulator.ping = v; } +#define NBN_GameServer_SetJitter(v) \ + { nbn_game_server.endpoint.packet_simulator.jitter = v; } +#define NBN_GameServer_SetPacketLoss(v) \ + { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } +#define NBN_GameServer_SetPacketDuplication(v) \ + { nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; } typedef struct NBN_PacketSimulatorEntry NBN_PacketSimulatorEntry; -struct NBN_PacketSimulatorEntry -{ +struct NBN_PacketSimulatorEntry { NBN_Packet packet; NBN_Connection *receiver; double delay; @@ -613,8 +505,7 @@ struct NBN_PacketSimulatorEntry struct NBN_PacketSimulatorEntry *prev; }; -typedef struct NBN_PacketSimulator -{ +typedef struct NBN_PacketSimulator { NBN_Endpoint *endpoint; NBN_PacketSimulatorEntry *head_packet; NBN_PacketSimulatorEntry *tail_packet; @@ -622,7 +513,7 @@ typedef struct NBN_PacketSimulator #ifdef NBNET_WINDOWS HANDLE queue_mutex; - HANDLE thread; + HANDLE thread; #else pthread_mutex_t queue_mutex; pthread_t thread; @@ -649,12 +540,14 @@ void NBN_PacketSimulator_Stop(NBN_PacketSimulator *); #define NBN_GameClient_SetPing(v) NBN_LogInfo("NBN_Debug_SetPing: packet simulator is not enabled, ignore") #define NBN_GameClient_SetJitter(v) NBN_LogInfo("NBN_Debug_SetJitter: packet simulator is not enabled, ignore") #define NBN_GameClient_SetPacketLoss(v) NBN_LogInfo("NBN_Debug_SetPacketLoss: packet simulator is not enabled, ignore") -#define NBN_GameClient_SetPacketDuplication(v) NBN_LogInfo("NBN_Debug_SetPacketDuplication: packet simulator is not enabled, ignore") +#define NBN_GameClient_SetPacketDuplication(v) \ + NBN_LogInfo("NBN_Debug_SetPacketDuplication: packet simulator is not enabled, ignore") #define NBN_GameServer_SetPing(v) NBN_LogInfo("NBN_Debug_SetPing: packet simulator is not enabled, ignore") #define NBN_GameServer_SetJitter(v) NBN_LogInfo("NBN_Debug_SetJitter: packet simulator is not enabled, ignore") #define NBN_GameServer_SetPacketLoss(v) NBN_LogInfo("NBN_Debug_SetPacketLoss: packet simulator is not enabled, ignore") -#define NBN_GameServer_SetPacketDuplication(v) NBN_LogInfo("NBN_Debug_SetPacketDuplication: packet simulator is not enabled, ignore") +#define NBN_GameServer_SetPacketDuplication(v) \ + NBN_LogInfo("NBN_Debug_SetPacketDuplication: packet simulator is not enabled, ignore") #endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ @@ -662,25 +555,32 @@ void NBN_PacketSimulator_Stop(NBN_PacketSimulator *); #pragma region NBN_Endpoint -typedef struct NBN_UserMessageDef { - int type; - unsigned int max_length; -} NBN_UserMessageDef; +typedef struct NBN_Endpoint_Config { + const char *protocol_name; + uint16_t port; + unsigned int custom_reliable_channels; + NBN_MessageAllocator msg_allocator; + NBN_MessageDeallocator msg_deallocator; +} NBN_Endpoint_Config; -struct NBN_Endpoint -{ - NBN_ChannelBuilder channel_builders[NBN_MAX_CHANNELS]; - NBN_ChannelDestructor channel_destructors[NBN_MAX_CHANNELS]; +struct NBN_Endpoint { NBN_EventQueue event_queue; uint32_t protocol_id; + unsigned int custom_reliable_channels; + unsigned int active_out_msg_count; + unsigned int active_inc_msg_buffer_count; + unsigned int active_out_msg_buffer_count; bool is_server; double time; - NBN_MemPool *outgoing_msg_pool; - NBN_MemPool *msg_buffer_pools[NBN_MAX_MESSAGE_TYPES + 1]; NBN_Message *write_message; unsigned int write_message_buffer_len; NBN_Writer message_writer; NBN_Reader message_reader; + uint8_t scratch_write_buffer[NBN_SCRATCH_WRITE_BUFFER_LEN]; + NBN_Message last_received_message; + bool last_message_need_free; + NBN_MessageAllocator msg_allocator; + NBN_MessageDeallocator msg_deallocator; #ifdef NBN_DEBUG /* Debug callbacks */ @@ -696,8 +596,7 @@ struct NBN_Endpoint #pragma region NBN_GameClient -enum -{ +enum { /* Client is connected to server */ NBN_CONNECTED = 2, @@ -708,16 +607,12 @@ enum NBN_MESSAGE_RECEIVED }; -typedef struct NBN_GameClient_Config -{ - const char *protocol_name; +typedef struct NBN_GameClient_Config { + NBN_Endpoint_Config endpoint; const char *host; - uint16_t port; - NBN_UserMessageDef user_message_defs[NBN_USER_MESSAGE_TYPE_RANGE + 1]; } NBN_GameClient_Config; -typedef struct NBN_GameClient -{ +typedef struct NBN_GameClient { NBN_Endpoint endpoint; NBN_Connection *server_connection; bool is_connected; @@ -732,19 +627,23 @@ typedef struct NBN_GameClient extern NBN_GameClient nbn_game_client; +// TODO: add doc about msg allocators /** * Create a minimal configuration to start a client. * - * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be able to communicate + * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be able + * to communicate * @param host Host to connect to * @param port Port to connect to * * @return 0 when successully started, -1 otherwise */ -NBN_GameClient_Config NBN_GameClient_CreateConfig(const char *protocol_name, const char *host, uint16_t port); +NBN_GameClient_Config NBN_GameClient_CreateConfig(const char *protocol_name, const char *host, uint16_t port, + NBN_MessageAllocator msg_allocator, + NBN_MessageDeallocator msg_deallocator); // TODO: doc -void NBN_GameClient_RegisterMessageType(NBN_GameClient_Config *config, uint8_t type, unsigned int max_length); +void NBN_GameClient_EnableCustomChannels(NBN_GameClient_Config *config, unsigned int count); // TODO: doc NBN_Writer *NBN_GameClient_GetConnectionDataWriter(void); @@ -759,7 +658,8 @@ NBN_Writer *NBN_GameClient_GetConnectionDataWriter(void); int NBN_GameClient_Start(NBN_GameClient_Config config); /** - * Disconnect from the server. The client can be restarted by calling NBN_GameClient_Start or NBN_GameClient_StartWithData again. + * Disconnect from the server. The client can be restarted by calling NBN_GameClient_Start or + * NBN_GameClient_StartWithData again. */ void NBN_GameClient_Stop(void); @@ -768,19 +668,19 @@ NBN_Reader *NBN_GameClient_GetServerDataReader(void); /** * Poll game client events. - * + * * This function should be called in a loop until it returns NBN_NO_EVENT. - * + * * @return The code of the polled event or NBN_NO_EVENT when there is no more events. */ int NBN_GameClient_Poll(void); /** * Pack all enqueued messages into packets and send them. - * + * * This should be called at a relatively high frequency, probably at the end of * every game tick. - * + * * @return 0 when successful, -1 otherwise */ int NBN_GameClient_SendPackets(void); @@ -797,16 +697,22 @@ int NBN_GameClient_SendMessage(void); // TODO: doc NBN_Writer *NBN_GameClient_GetMessageWriter(void); + // TODO: doc NBN_Reader *NBN_GameClient_GetMessageReader(void); +int NBN_GameClient_GetActiveOutgoingMessageCount(void); + +int NBN_GameClient_GetActiveOutgoingMessageBufferCount(void); +int NBN_GameClient_GetActiveIncomingMessageBufferCount(void); + /** * Send a message to the server, unreliably. - * + * * @param type The type of message to send * @param data A pointer to the message data buffer (managed by user code) * @param length The length of the message data buffer in bytes - * + * * @return 0 when successful, -1 otherwise */ int NBN_GameClient_SendUnreliableMessage(uint8_t type, uint8_t *data, uint16_t length); @@ -829,26 +735,26 @@ NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *drive /** * Retrieve the info about the last received message. - * + * * Call this function when receiveing a NBN_MESSAGE_RECEIVED event to access * information about the message. - * + * * @return A structure containing information about the received message */ NBN_MessageInfo NBN_GameClient_GetMessageInfo(void); /** * Retrieve network stats about the game client. - * + * * @return A structure containing network related stats about the game client */ NBN_ConnectionStats NBN_GameClient_GetStats(void); /** * Retrieve the code sent by the server when closing the connection. - * + * * Call this function when receiving a NBN_DISCONNECTED event. - * + * * @return The code used by the server when closing the connection or -1 (the default code) */ int NBN_GameClient_GetServerCloseCode(void); @@ -870,8 +776,7 @@ void NBN_GameClient_Debug_RegisterCallback(NBN_ConnectionDebugCallback, void *); #define NBN_MAX_CLIENTS 1024 -enum -{ +enum { /* A new client has connected */ NBN_NEW_CONNECTION = 2, @@ -882,21 +787,16 @@ enum NBN_CLIENT_MESSAGE_RECEIVED }; -typedef struct NBN_GameServerStats -{ - float upload_bandwidth; /* Total upload bandwith of the game server */ +typedef struct NBN_GameServerStats { + float upload_bandwidth; /* Total upload bandwith of the game server */ float download_bandwidth; /* Total download bandwith of the game server */ } NBN_GameServerStats; -typedef struct NBN_GameServer_Config -{ - const char *protocol_name; - uint16_t port; - NBN_UserMessageDef user_message_defs[NBN_USER_MESSAGE_TYPE_RANGE + 1]; +typedef struct NBN_GameServer_Config { + NBN_Endpoint_Config endpoint; } NBN_GameServer_Config; -typedef struct NBN_GameServer -{ +typedef struct NBN_GameServer { NBN_Endpoint endpoint; NBN_ConnectionVector *clients; /* Vector of clients connections */ NBN_ConnectionListNode *closed_clients_head; @@ -911,22 +811,26 @@ typedef struct NBN_GameServer extern NBN_GameServer nbn_game_server; +// TODO: add doc about msg allocators /** * Create a minimal configuration to start a server. * - * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be able to communicate + * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be able + * to communicate * @param port The port clients will connect to * * @return 0 when successully started, -1 otherwise */ -NBN_GameServer_Config NBN_GameServer_CreateConfig(const char *protocol_name, uint16_t port); +NBN_GameServer_Config NBN_GameServer_CreateConfig(const char *protocol_name, uint16_t port, + NBN_MessageAllocator msg_allocator, + NBN_MessageDeallocator msg_deallocator); // TODO: doc -void NBN_GameServer_RegisterMessageType(NBN_GameServer_Config *config, uint8_t type, unsigned int max_length); +void NBN_GameServer_EnableCustomChannels(NBN_GameServer_Config *config, unsigned int count); /** * Start the game server with the provided configuration. - * + * * @param config the configuration to use to start the server * * @return 0 when successfully started, -1 otherwise @@ -949,10 +853,10 @@ int NBN_GameServer_Poll(void); /** * Pack all enqueued messages into packets and send them. - * + * * This should be called at a relatively high frequency, probably at the end of * every game tick. - * + * * @return 0 when successful, -1 otherwise */ int NBN_GameServer_SendPackets(void); @@ -966,19 +870,19 @@ NBN_Connection *NBN_GameServer_CreateClientConnection(int, void *, uint32_t); * Close a client's connection without a specific code (default code is -1) * * @param conn The connection to close - * + * * @return 0 when successful, -1 otherwise */ int NBN_GameServer_CloseClient(NBN_Connection *conn); /** * Close a client's connection with a specific code. - * + * * The code is an arbitrary integer to let the client knows * why his connection was closed. * * @param conn The connection to close - * + * * @return 0 when successful, -1 otherwise */ int NBN_GameServer_CloseClientWithCode(NBN_Connection *conn, int code); @@ -1007,30 +911,30 @@ int NBN_GameServer_AcceptIncomingConnection(void); /** * Reject the last client connection request with a specific code. - * + * * The code is an arbitrary integer to let the client knows why his connection * was rejected. - * + * * Call this function after receiving a NBN_NEW_CONNECTION event. - * + * * @return 0 when successful, -1 otherwise */ int NBN_GameServer_RejectIncomingConnectionWithCode(int code); /** * Reject the last client connection request without any specific code (default code is -1) - * + * * Call this function after receiving a NBN_NEW_CONNECTION event. - * + * * @return 0 when successful, -1 otherwise */ int NBN_GameServer_RejectIncomingConnection(void); /** * Retrieve the last connection to the game server. - * + * * Call this function after receiving a NBN_NEW_CONNECTION event. - * + * * @return A pointer to a NBN_Connection representing the new connection */ NBN_Connection *NBN_GameServer_GetIncomingConnection(void); @@ -1039,27 +943,28 @@ NBN_Connection *NBN_GameServer_GetIncomingConnection(void); NBN_Reader *NBN_GameServer_GetConnectionDataReader(void); /** - * Return the last disconnected client's connection. - * - * Call this function after receiving a NBN_CLIENT_DISCONNECTED event - * - * @return the last disconnected client's connection + * Return the information about the last disconnected client. + * + * Call this function after receiving a NBN_CLIENT_DISCONNECTED event. + * See NBN_DisconnectionInfo struct. + * + * @return information about the last disconnected client */ -NBN_Connection *NBN_GameServer_GetDisconnectedClient(void); +NBN_DisconnectionInfo NBN_GameServer_GetDisconnectionInfo(void); /** * Retrieve the info about the last received message. - * + * * Call this function when receiving a NBN_CLIENT_MESSAGE_RECEIVED event to access * information about the message. - * + * * @return A structure containing information about the received message */ NBN_MessageInfo NBN_GameServer_GetMessageInfo(void); /** * Retrieve network stats about the game server. - * + * * @return A structure containing network related stats about the game server */ NBN_GameServerStats NBN_GameServer_GetStats(void); @@ -1076,8 +981,7 @@ void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback, void *); #define NBN_MAX_DRIVERS 4 -typedef enum NBN_DriverEvent -{ +typedef enum NBN_DriverEvent { // Client events NBN_DRIVER_CLI_PACKET_RECEIVED, @@ -1096,8 +1000,7 @@ typedef int (*NBN_Driver_ServerStartFunc)(uint32_t, uint16_t); typedef int (*NBN_Driver_ServerSendPacketToFunc)(NBN_Packet *, NBN_Connection *); typedef void (*NBN_Driver_ServerRemoveConnection)(NBN_Connection *); -typedef struct NBN_DriverImplementation -{ +typedef struct NBN_DriverImplementation { /* Client functions */ NBN_Driver_ClientStartFunc cli_start; NBN_Driver_StopFunc cli_stop; @@ -1112,8 +1015,7 @@ typedef struct NBN_DriverImplementation NBN_Driver_ServerRemoveConnection serv_remove_connection; } NBN_DriverImplementation; -struct NBN_Driver -{ +struct NBN_Driver { int id; const char *name; NBN_DriverImplementation impl; @@ -1121,12 +1023,7 @@ struct NBN_Driver #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-field-initializers" -static NBN_Driver nbn_drivers[NBN_MAX_DRIVERS] = { - {-1, NULL}, - {-1, NULL}, - {-1, NULL}, - {-1, NULL} -}; +static NBN_Driver nbn_drivers[NBN_MAX_DRIVERS] = {{-1, NULL}, {-1, NULL}, {-1, NULL}, {-1, NULL}}; #pragma clang diagnostic pop static unsigned int nbn_driver_count = 0; @@ -1145,7 +1042,7 @@ void NBN_Driver_Register(int id, const char *name, NBN_DriverImplementation impl * * @param ev Event type * @param data Arbitrary data about the event -*/ + */ int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data); #pragma endregion /* Network driver */ @@ -1164,11 +1061,11 @@ int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data); #define ABS(v) (((v) > 0) ? (v) : -(v)) #endif -#define SEQUENCE_NUMBER_GT(seq1, seq2) \ +#define SEQUENCE_NUMBER_GT(seq1, seq2) \ ((seq1 > seq2 && (seq1 - seq2) <= 32767) || (seq1 < seq2 && (seq2 - seq1) >= 32767)) -#define SEQUENCE_NUMBER_GTE(seq1, seq2) \ +#define SEQUENCE_NUMBER_GTE(seq1, seq2) \ ((seq1 >= seq2 && (seq1 - seq2) <= 32767) || (seq1 <= seq2 && (seq2 - seq1) >= 32767)) -#define SEQUENCE_NUMBER_LT(seq1, seq2) \ +#define SEQUENCE_NUMBER_LT(seq1, seq2) \ ((seq1 < seq2 && (seq2 - seq1) <= 32767) || (seq1 > seq2 && (seq1 - seq2) >= 32767)) #pragma endregion /* Utils */ @@ -1187,8 +1084,7 @@ int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data); static void NBN_ConnectionVector_Grow(NBN_ConnectionVector *vector, unsigned int new_capacity); -static NBN_ConnectionVector *NBN_ConnectionVector_Create(void) -{ +static NBN_ConnectionVector *NBN_ConnectionVector_Create(void) { NBN_ConnectionVector *vector = (NBN_ConnectionVector *)NBN_Allocator(sizeof(NBN_ConnectionVector)); vector->connections = NULL; @@ -1199,25 +1095,21 @@ static NBN_ConnectionVector *NBN_ConnectionVector_Create(void) return vector; } -static void NBN_ConnectionVector_Destroy(NBN_ConnectionVector *vector) -{ +static void NBN_ConnectionVector_Destroy(NBN_ConnectionVector *vector) { NBN_Deallocator(vector->connections); NBN_Deallocator(vector); } -static void NBN_ConnectionVector_Add(NBN_ConnectionVector *vector, NBN_Connection *conn) -{ +static void NBN_ConnectionVector_Add(NBN_ConnectionVector *vector, NBN_Connection *conn) { NBN_Assert(conn->vector_pos == -1); - if (vector->count >= vector->capacity) - { + if (vector->count >= vector->capacity) { NBN_ConnectionVector_Grow(vector, vector->capacity * 2); } unsigned int position = vector->count; - if (vector->connections[position]) - { + if (vector->connections[position]) { NBN_LogError("Failed to add connection (id: %d) to vector: position %d is not empty", conn->id, position); NBN_Abort(); } @@ -1227,11 +1119,11 @@ static void NBN_ConnectionVector_Add(NBN_ConnectionVector *vector, NBN_Connectio vector->count++; } -static uint32_t NBN_ConnectionVector_RemoveAt(NBN_ConnectionVector *vector, unsigned int position) -{ +static uint32_t NBN_ConnectionVector_RemoveAt(NBN_ConnectionVector *vector, unsigned int position) { NBN_Connection *conn = vector->connections[position]; - if (conn == NULL) return 0; + if (conn == NULL) + return 0; // Make sure that connections are stored contiguously in memory @@ -1245,12 +1137,11 @@ static uint32_t NBN_ConnectionVector_RemoveAt(NBN_ConnectionVector *vector, unsi return conn->id; } -static void NBN_ConnectionVector_Grow(NBN_ConnectionVector *vector, unsigned int new_capacity) -{ - vector->connections = (NBN_Connection **)NBN_Reallocator(vector->connections, sizeof(NBN_Connection *) * new_capacity); +static void NBN_ConnectionVector_Grow(NBN_ConnectionVector *vector, unsigned int new_capacity) { + vector->connections = + (NBN_Connection **)NBN_Reallocator(vector->connections, sizeof(NBN_Connection *) * new_capacity); - if (vector->connections == NULL) - { + if (vector->connections == NULL) { NBN_LogError("Failed to allocate memory to grow the connection vector"); NBN_Abort(); } @@ -1263,610 +1154,541 @@ static void NBN_ConnectionVector_Grow(NBN_ConnectionVector *vector, unsigned int #pragma endregion // NBN_ConnectionVector -#pragma region NBN_ConnectionTable - -#define NBN_CONNECTION_TABLE_INITIAL_CAPACITY 32 -#define NBN_CONNECTION_TABLE_LOAD_FACTOR 0.75f - -static void NBN_ConnectionTable_InsertEntry(NBN_ConnectionTable *table, unsigned int slot, NBN_Connection *conn); -static void NBN_ConnectionTable_RemoveEntry(NBN_ConnectionTable *table, unsigned int slot); -static unsigned int NBN_ConnectionTable_Hash(int hash); -static void NBN_ConnectionTable_Grow(NBN_ConnectionTable *table, unsigned int new_capacity); - -static NBN_ConnectionTable *NBN_ConnectionTable_Create(void) -{ - NBN_ConnectionTable *table = (NBN_ConnectionTable *)NBN_Allocator(sizeof(NBN_ConnectionTable)); +#pragma region Serialization - table->connections = NULL; - table->capacity = 0; +void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length) { + writer->buffer = buffer; + writer->length = length; + writer->position = 0; +} - NBN_ConnectionTable_Grow(table, NBN_CONNECTION_TABLE_INITIAL_CAPACITY); +void NBN_Writer_WriteInt32(NBN_Writer *writer, int32_t value) { NBN_Writer_WriteUInt32(writer, value); } - for (unsigned int i = 0; i < table->capacity; i++) - table->connections[i] = NULL; +void NBN_Writer_WriteUInt16(NBN_Writer *writer, uint16_t value) { + NBN_Assert(writer->position + 2 <= writer->length); - return table; + *((uint16_t *)(writer->buffer + writer->position)) = htons(value); + writer->position += 2; } -static void NBN_ConnectionTable_Destroy(NBN_ConnectionTable *table) -{ - // Make sure we don't destroy the actual connections as they will be destroyed with the connection vector - NBN_Deallocator(table->connections); - NBN_Deallocator(table); -} +void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value) { + NBN_Assert(writer->position + 4 <= writer->length); -static void NBN_ConnectionTable_Add(NBN_ConnectionTable *table, NBN_Connection *conn) -{ - unsigned int hash = NBN_ConnectionTable_Hash(conn->id); - unsigned int slot = hash % table->capacity; + *((uint32_t *)(writer->buffer + writer->position)) = htonl(value); + writer->position += 4; +} - NBN_Connection *entry = table->connections[slot]; +void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value) { + NBN_Assert(writer->position + 1 <= writer->length); - if (entry == NULL) - { - // no collision, just insert in slot - NBN_ConnectionTable_InsertEntry(table, slot, conn); - return; - } + writer->buffer[writer->position] = value; + writer->position++; +} - // collision, do quadratic probing until we find a free slot +void NBN_Writer_WriteFloat(NBN_Writer *writer, float value) { + NBN_Assert(writer->position + 4 <= writer->length); - unsigned int i = 0; + uint32_t *val_u = (uint32_t *)&value; + NBN_Writer_WriteUInt32(writer, htonl(*val_u)); +} - do - { - slot = (hash + (int)pow(i, 2)) % table->capacity; - entry = table->connections[slot]; +void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int length) { + NBN_Assert(writer->position + length <= writer->length); - i++; - } while (entry != NULL); + memcpy(writer->buffer + writer->position, bytes, length); + writer->position += length; +} - NBN_ConnectionTable_InsertEntry(table, slot, conn); +void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length) { + reader->buffer = buffer; + reader->length = length; + reader->position = 0; } -static bool NBN_ConnectionTable_Remove(NBN_ConnectionTable *table, uint32_t id) -{ - unsigned int hash = NBN_ConnectionTable_Hash(id); - unsigned int slot = hash % table->capacity; - NBN_Connection *conn = table->connections[slot]; +int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value) { + return NBN_Reader_ReadUInt32(reader, (uint32_t *)value); +} - if (conn->id == id) - { - NBN_ConnectionTable_RemoveEntry(table, slot); - return true; +int NBN_Reader_ReadUInt16(NBN_Reader *reader, uint16_t *value) { + if (reader->position + 2 > reader->length) { + return NBN_ERROR; } - // quadratic probing - - unsigned int i = 0; + *value = ntohs(*((uint16_t *)(reader->buffer + reader->position))); + reader->position += 2; - do - { - slot = (hash + (int)pow(i, 2)) % table->capacity; - conn = table->connections[slot]; + return 0; +} - if (conn != NULL && conn->id == id) - { - NBN_ConnectionTable_RemoveEntry(table, slot); - return true; - } +int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value) { + if (reader->position + 4 > reader->length) { + return NBN_ERROR; + } - i++; - } while (i < table->capacity); + *value = ntohl(*((uint32_t *)(reader->buffer + reader->position))); + reader->position += 4; - return false; + return 0; } -static void NBN_ConnectionTable_InsertEntry(NBN_ConnectionTable *table, unsigned int slot, NBN_Connection *conn) -{ - table->connections[slot] = conn; - table->count++; - table->load_factor = (float)table->count / table->capacity; - - if (table->load_factor > NBN_CONNECTION_TABLE_LOAD_FACTOR) - { - NBN_ConnectionTable_Grow(table, table->capacity * 2); +int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value) { + if (reader->position + 1 > reader->length) { + return NBN_ERROR; } -} -static void NBN_ConnectionTable_RemoveEntry(NBN_ConnectionTable *table, unsigned int slot) -{ - table->connections[slot] = NULL; - table->count--; - table->load_factor = (float)table->count / table->capacity; + *value = reader->buffer[reader->position]; + reader->position++; + + return 0; } -static NBN_Connection *NBN_ConnectionTable_Get(NBN_ConnectionTable *table, uint32_t id) -{ - unsigned int hash = NBN_ConnectionTable_Hash(id); - unsigned int slot = hash % table->capacity; - NBN_Connection *conn = table->connections[slot]; +int NBN_Reader_ReadFloat(NBN_Reader *reader, float *value) { + if (NBN_Reader_ReadUInt32(reader, (uint32_t *)value) < 0) { + return -1; + } - if (conn && conn->id == id) - return conn; + return 0; +} - // quadratic probing +int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length) { + if (reader->position + length > reader->length) { + NBN_LogError("reader->position = %d, length = %d, reader->length = %d", reader->position, length, + reader->length); + return NBN_ERROR; + } - unsigned int i = 0; + memcpy(bytes, reader->buffer + reader->position, length); + reader->position += length; - do - { - slot = (hash + (int)pow(i, 2)) % table->capacity; - conn = table->connections[slot]; + return 0; +} - if (conn != NULL && conn->id == id) - { - return conn; - } +#pragma endregion /* Serialization */ - i++; - } while (i < table->capacity); +#pragma region NBN_Packet - return NULL; -} +void NBN_Packet_InitWrite(NBN_Packet *packet, uint32_t protocol_id, uint16_t seq_number, uint16_t ack, + uint32_t ack_bits) { + packet->header.protocol_id = protocol_id; + packet->header.messages_count = 0; + packet->header.seq_number = seq_number; + packet->header.ack = ack; + packet->header.ack_bits = ack_bits; -static unsigned int NBN_ConnectionTable_Hash(int hash) -{ - hash = ((hash >> 16) ^ hash) * 0x45d9f3b; - hash = ((hash >> 16) ^ hash) * 0x45d9f3b; - hash = (hash >> 16) ^ hash; + packet->mode = NBN_PACKET_MODE_WRITE; + packet->sender = NULL; + packet->size = NBN_PACKET_HEADER_SIZE; + packet->sealed = false; - return hash; + memset(packet->buffer, 0, sizeof(packet->buffer)); } -static void NBN_ConnectionTable_Grow(NBN_ConnectionTable *table, unsigned int new_capacity) -{ - unsigned int old_capacity = table->capacity; - NBN_Connection** old_connections_array = table->connections; - NBN_Connection** new_connections_array = (NBN_Connection **)NBN_Allocator(sizeof(NBN_Connection *) * new_capacity); +int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_OutgoingMessage *out_msg) { + NBN_Message *message = out_msg->message; - for (unsigned int i = 0; i < new_capacity; i++) - { - new_connections_array[i] = NULL; - } + NBN_LogTrace("Write message %d (type: %d, length: %d) to packet %d", out_msg->id, message->header.type, + message->header.length, packet->header.seq_number); - table->connections = new_connections_array; - table->capacity = new_capacity; - table->count = 0; - table->load_factor = 0; + NBN_Assert((message->data != NULL && message->header.length > 0) || + (message->data == NULL && message->header.length == 0)); - // rehash + if (packet->mode != NBN_PACKET_MODE_WRITE || packet->sealed) + return NBN_PACKET_WRITE_ERROR; - NBN_Assert(old_connections_array || old_capacity == 0); + unsigned int message_size = NBN_MESSAGE_HEADER_SIZE + message->header.length; - for (unsigned int i = 0; i < old_capacity; i++) - { - if (old_connections_array[i]) - { - NBN_ConnectionTable_Add(table, old_connections_array[i]); - } + if (packet->header.messages_count >= NBN_MAX_MESSAGES_PER_PACKET || + packet->size + message_size > NBN_PACKET_MAX_SIZE) { + return NBN_PACKET_WRITE_NO_SPACE; } - if (old_connections_array) NBN_Deallocator(old_connections_array); -} - -#pragma region // NBN_ConnectionTable + NBN_Writer writer; -#pragma region Memory management + NBN_Writer_Init(&writer, packet->buffer + packet->size, sizeof(packet->buffer) - packet->size); -NBN_MemoryManager nbn_mem_manager; + NBN_Writer_WriteUInt16(&writer, out_msg->id); + NBN_Writer_WriteUInt16(&writer, message->header.length); + NBN_Writer_WriteUInt8(&writer, message->header.type); + NBN_Writer_WriteUInt8(&writer, message->header.channel_id); -static void MemoryManager_Init(void); -static void MemoryManager_Deinit(void); -static void *MemoryManager_Alloc(unsigned int); -static void MemoryManager_Dealloc(void *, unsigned int); + NBN_Assert(writer.position == NBN_MESSAGE_HEADER_SIZE); -#if !defined(NBN_DISABLE_MEMORY_POOLING) + if (message->data) { + NBN_Assert(message->header.length > 0); + NBN_Writer_WriteBytes(&writer, message->data, message->header.length); + } -static void MemPool_Init(NBN_MemPool *, size_t, unsigned int); -static void MemPool_Deinit(NBN_MemPool *); -static void MemPool_Grow(NBN_MemPool *, unsigned int); -static void *MemPool_Alloc(NBN_MemPool *); -static void MemPool_Dealloc(NBN_MemPool *, void *); + NBN_Assert(writer.position == message_size); -#endif /* NBN_DISABLE_MEMORY_POOLING */ + packet->size += writer.position; + packet->header.messages_count++; -static void MemoryManager_Init(void) -{ -#ifdef NBN_DISABLE_MEMORY_POOLING - NBN_LogDebug("MemoryManager_Init without pooling!"); + return NBN_PACKET_WRITE_OK; +} - nbn_mem_manager.mem_sizes[NBN_MEM_MESSAGE_CHUNK] = sizeof(NBN_MessageChunk); - nbn_mem_manager.mem_sizes[NBN_MEM_CONNECTION] = sizeof(NBN_Connection); +int NBN_Packet_Seal(NBN_Packet *packet) { + if (packet->mode != NBN_PACKET_MODE_WRITE) + return NBN_ERROR; -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - nbn_mem_manager.mem_sizes[NBN_MEM_PACKET_SIMULATOR_ENTRY] = sizeof(NBN_PacketSimulatorEntry); -#endif -#else - NBN_LogDebug("MemoryManager_Init with pooling!"); + NBN_Writer writer; - // MemPool_Init(&nbn_mem_manager.mem_pools[NBN_MEM_MESSAGE_CHUNK], sizeof(NBN_MessageChunk), 256); - MemPool_Init(&nbn_mem_manager.mem_pools[NBN_MEM_CONNECTION], sizeof(NBN_Connection), 16); + NBN_Writer_Init(&writer, packet->buffer, NBN_PACKET_HEADER_SIZE); -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - MemPool_Init(&nbn_mem_manager.mem_pools[NBN_MEM_PACKET_SIMULATOR_ENTRY], sizeof(NBN_PacketSimulatorEntry), 32); -#endif -#endif /* NBN_DISABLE_MEMORY_POOLING */ -} + NBN_Writer_WriteUInt32(&writer, packet->header.protocol_id); + NBN_Writer_WriteUInt32(&writer, packet->header.ack_bits); + NBN_Writer_WriteUInt16(&writer, packet->header.seq_number); + NBN_Writer_WriteUInt16(&writer, packet->header.ack); + NBN_Writer_WriteUInt8(&writer, packet->header.messages_count); -static void MemoryManager_Deinit(void) -{ -#if !defined(NBN_DISABLE_MEMORY_POOLING) - MemPool_Deinit(&nbn_mem_manager.mem_pools[NBN_MEM_MESSAGE_CHUNK]); - MemPool_Deinit(&nbn_mem_manager.mem_pools[NBN_MEM_CONNECTION]); + NBN_Assert(writer.position == NBN_PACKET_HEADER_SIZE); -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - MemPool_Deinit(&nbn_mem_manager.mem_pools[NBN_MEM_PACKET_SIMULATOR_ENTRY]); -#endif -#endif /* NBN_DISABLE_MEMORY_POOLING */ -} + packet->sealed = true; -static void *MemoryManager_Alloc(unsigned int mem_tag) -{ -#ifdef NBN_DISABLE_MEMORY_POOLING - return NBN_Allocator(nbn_mem_manager.mem_sizes[mem_tag]); -#else - return MemPool_Alloc(&nbn_mem_manager.mem_pools[mem_tag]); -#endif /* NBN_DISABLE_MEMORY_POOLING */ + return 0; } -static void MemoryManager_Dealloc(void *ptr, unsigned int mem_tag) -{ -#ifdef NBN_DISABLE_MEMORY_POOLING - (void)mem_tag; - - NBN_Deallocator(ptr); -#else - MemPool_Dealloc(&nbn_mem_manager.mem_pools[mem_tag], ptr); -#endif /* NBN_DISABLE_MEMORY_POOLING */ -} +int NBN_Packet_InitRead(NBN_Packet *packet, uint32_t protocol_id, unsigned int size) { + NBN_Assert(size >= NBN_PACKET_HEADER_SIZE); -#if !defined(NBN_DISABLE_MEMORY_POOLING) + packet->mode = NBN_PACKET_MODE_READ; + packet->size = size; + packet->sender = NULL; // IMPORTANT: must be set by the drivers + packet->sealed = false; -static void MemPool_Init(NBN_MemPool *pool, size_t block_size, unsigned int initial_block_count) -{ - pool->block_size = MAX(block_size, sizeof(NBN_MemPoolFreeBlock)); - pool->block_idx = 0; - pool->block_count = 0; - pool->free = NULL; - pool->blocks = NULL; + NBN_Reader reader; - MemPool_Grow(pool, initial_block_count); -} + NBN_Reader_Init(&reader, packet->buffer, NBN_PACKET_HEADER_SIZE); -static void MemPool_Deinit(NBN_MemPool *pool) -{ - for (unsigned int i = 0; i < pool->block_count; i++) - NBN_Deallocator(pool->blocks[i]); + if (NBN_Reader_ReadUInt32(&reader, &packet->header.protocol_id) < 0) { + NBN_LogDebug("Failed to read packet's protocol id"); + return NBN_ERROR; + } - NBN_Deallocator(pool->blocks); -} + if (packet->header.protocol_id != protocol_id) { + NBN_LogDebug("Packet's protocol id did not match (expected: %d, received: %d)", protocol_id, + packet->header.protocol_id); + return NBN_ERROR; + } -static void *MemPool_Alloc(NBN_MemPool *pool) -{ - if (pool->free) - { - void *block = pool->free; + if (NBN_Reader_ReadUInt32(&reader, &packet->header.ack_bits) < 0) { + NBN_LogDebug("Failed to read packet's acked bits"); + return NBN_ERROR; + } - pool->free = pool->free->next; + if (NBN_Reader_ReadUInt16(&reader, &packet->header.seq_number) < 0) { + NBN_LogDebug("Failed to read packet's sequence number"); + return NBN_ERROR; + } - return block; + if (NBN_Reader_ReadUInt16(&reader, &packet->header.ack) < 0) { + NBN_LogDebug("Failed to read packet's ack"); + return NBN_ERROR; } - if (pool->block_idx == pool->block_count) - MemPool_Grow(pool, pool->block_count * 2); + if (NBN_Reader_ReadUInt8(&reader, &packet->header.messages_count) < 0) { + NBN_LogDebug("Failed to read packet's message count"); + return NBN_ERROR; + } - void *block = pool->blocks[pool->block_idx]; + return 0; +} - pool->block_idx++; +#pragma endregion /* NBN_Packet */ - return block; -} +#pragma region NBN_Channel -static void MemPool_Dealloc(NBN_MemPool *pool, void *ptr) -{ - NBN_MemPoolFreeBlock *free = pool->free; +static void Endpoint_FreeMessage(NBN_Endpoint *endpoint, NBN_Message *message); +static unsigned int Channel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2); - pool->free = (NBN_MemPoolFreeBlock*)ptr; - pool->free->next = free; -} +void NBN_Channel_Destroy(NBN_Endpoint *endpoint, NBN_Channel *channel) { + for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) { + NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[i]; -static void MemPool_Grow(NBN_MemPool *pool, unsigned int block_count) -{ - pool->blocks = (uint8_t**)NBN_Reallocator(pool->blocks, sizeof(uint8_t *) * block_count); + if (!inc_msg->free) { + Endpoint_FreeMessage(endpoint, &inc_msg->message); + } - for (unsigned int i = 0; i < block_count - pool->block_count; i++) - { - uint8_t *block = (uint8_t*)NBN_Allocator(pool->block_size); + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[i]; - memset(block, 0, pool->block_size); - pool->blocks[pool->block_idx + i] = block; + if (out_msg->message) { + Endpoint_FreeMessage(endpoint, out_msg->message); + } } - pool->block_count = block_count; + NBN_Deallocator(channel); } -#endif /* NBN_DISABLE_MEMORY_POOLING */ +static NBN_Channel *Channel_Create(uint8_t id, NBN_ChannelType type) { + NBN_Channel *channel = NBN_Allocator(sizeof(NBN_Channel)); -#pragma endregion /* Memory management */ + channel->id = id; + channel->type = type; + channel->next_outgoing_message_id = 0; + channel->next_recv_message_id = 0; + channel->outgoing_message_count = 0; + channel->last_received_message_id = 0; + channel->next_outgoing_message_slot = 0; + channel->oldest_unacked_message_id = 0; + channel->most_recent_message_id = 0; -#pragma region Serialization + for (unsigned int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) { + channel->incoming_messages_buffer[i].free = true; + channel->outgoing_messages_buffer[i].message = NULL; + } -void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length) -{ - writer->buffer = buffer; - writer->length = length; - writer->position = 0; -} + for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) + channel->ack_buffer[i] = false; -void NBN_Writer_WriteInt32(NBN_Writer *writer, int32_t value) -{ - NBN_Writer_WriteUInt32(writer, value); + return channel; } -void NBN_Writer_WriteUInt16(NBN_Writer *writer, uint16_t value) -{ - NBN_Assert(writer->position + 2 <= writer->length); +static void Channel_UpdateMessageSendTime(NBN_Channel *channel, uint16_t msg_id, double time) { + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; - *((uint16_t *)(writer->buffer + writer->position)) = htons(value); - writer->position += 2; + NBN_Assert(msg_id == out_msg->id); + out_msg->last_send_time = time; } -void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value) -{ - NBN_Assert(writer->position + 4 <= writer->length); +static bool Channel_AddReceivedMessage(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) { + if (channel->type == NBN_CHANNEL_UNRELIABLE) { + if (SEQUENCE_NUMBER_GT(message->header.id, channel->last_received_message_id)) { + NBN_IncomingMessage *inc_msg = + &channel->incoming_messages_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; - *((uint32_t *)(writer->buffer + writer->position)) = htonl(value); - writer->position += 4; -} + inc_msg->message = *message; + inc_msg->free = false; -void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value) -{ - NBN_Assert(writer->position + 1 <= writer->length); + channel->last_received_message_id = message->header.id; - writer->buffer[writer->position] = value; - writer->position++; -} + NBN_LogTrace("Add incomoing message %d of type %d to unreliable channel %d (last received msg id: %d)", + message->header.id, message->header.type, channel->id, channel->last_received_message_id); -void NBN_Writer_WriteFloat(NBN_Writer *writer, float value) -{ - NBN_Assert(writer->position + 4 <= writer->length); + return true; + } - uint32_t *val_u = (uint32_t *)&value; - NBN_Writer_WriteUInt32(writer, htonl(*val_u)); -} + return false; + } else if (channel->type == NBN_CHANNEL_RELIABLE) { + unsigned int dt = Channel_ComputeMessageIdDelta(message->header.id, channel->most_recent_message_id); -void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int length) -{ - NBN_Assert(writer->position + length <= writer->length); + NBN_LogTrace("Add incomoing message %d of type %d to reliable channel %d (most recent msg id: %d, dt: %d)", + message->header.id, message->header.type, channel->id, channel->most_recent_message_id, dt); - memcpy(writer->buffer + writer->position, bytes, length); - writer->position += length; -} + if (SEQUENCE_NUMBER_GT(message->header.id, channel->most_recent_message_id)) { + NBN_Assert(dt < NBN_CHANNEL_BUFFER_SIZE); -void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length) -{ - reader->buffer = buffer; - reader->length = length; - reader->position = 0; -} + channel->most_recent_message_id = message->header.id; + } else { + /* This is an old message that has already been received, probably coming from + an out of order late packet. */ + if (dt >= NBN_CHANNEL_BUFFER_SIZE) + return false; -int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value) -{ - return NBN_Reader_ReadUInt32(reader, (uint32_t *)value); -} + if (SEQUENCE_NUMBER_LT(message->header.id, channel->next_recv_message_id)) { + return false; + } + } -int NBN_Reader_ReadUInt16(NBN_Reader *reader, uint16_t *value) -{ - if (reader->position + 2 > reader->length) - { - return NBN_ERROR; - } + NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; - *value = ntohs(*((uint16_t *)(reader->buffer + reader->position))); - reader->position += 2; + if (!inc_msg->free) { + Endpoint_FreeMessage(endpoint, &inc_msg->message); + } - return 0; -} + inc_msg->message = *message; + inc_msg->free = false; -int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value) -{ - if (reader->position + 4 > reader->length) - { - return NBN_ERROR; + return true; } - *value = ntohl(*((uint32_t *)(reader->buffer + reader->position))); - reader->position += 4; - - return 0; + NBN_Abort(); } -int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value) -{ - if (reader->position + 1 > reader->length) - { - return NBN_ERROR; - } +static bool Channel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *message) { + uint16_t msg_id = channel->next_outgoing_message_id; + int index = msg_id % NBN_CHANNEL_BUFFER_SIZE; + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[index]; - *value = reader->buffer[reader->position]; - reader->position++; + if (channel->type == NBN_CHANNEL_UNRELIABLE) { + // make sure this buffer slot is not already in use + if (out_msg->message) { + NBN_LogError("No outgoing message available in unreliable channel %d (outgoing message count: %d)", + channel->id, channel->outgoing_message_count); - return 0; -} + return false; + } -int NBN_Reader_ReadFloat(NBN_Reader *reader, float *value) -{ - if (NBN_Reader_ReadUInt32(reader, (uint32_t *)value) < 0) - { - return -1; - } + out_msg->id = msg_id; + out_msg->message = message; + out_msg->last_send_time = -1; - return 0; -} + channel->next_outgoing_message_id++; + channel->outgoing_message_count++; -int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length) -{ - if (reader->position + length > reader->length) - { - NBN_LogError("reader->position = %d, length = %d, reader->length = %d", - reader->position, length, reader->length); - return NBN_ERROR; - } + return true; + } else if (channel->type == NBN_CHANNEL_RELIABLE) { + // make sure the outgoing message is not already in use + if (out_msg->message) { + NBN_LogError("No outgoing message available in reliable channel %d (outgoing message count: %d)", + channel->id, channel->outgoing_message_count); +#ifdef NBN_DEBUG + NBN_Abort(); +#endif - memcpy(bytes, reader->buffer + reader->position, length); - reader->position += length; + return false; + } - return 0; + out_msg->message = message; + out_msg->id = msg_id; + out_msg->last_send_time = -1; + + channel->next_outgoing_message_id++; + channel->outgoing_message_count++; + + NBN_LogTrace("Added outgoing message %d of type %d to reliable channel %d (index: %d)", msg_id, + message->header.type, channel->id, index); + + return true; + } else { + NBN_Abort(); + } } -#pragma endregion /* Serialization */ +static NBN_Message *Channel_GetNextRecvedMessage(NBN_Channel *channel) { + NBN_IncomingMessage *inc_msg = + &channel->incoming_messages_buffer[channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; -#pragma region NBN_Packet + if (channel->type == NBN_CHANNEL_UNRELIABLE) { + while (SEQUENCE_NUMBER_LT(channel->next_recv_message_id, channel->last_received_message_id)) { + if (!inc_msg->free && inc_msg->message.header.id == channel->next_recv_message_id) { + inc_msg->free = true; -void NBN_Packet_InitWrite(NBN_Packet *packet, uint32_t protocol_id, uint16_t seq_number, uint16_t ack, uint32_t ack_bits) -{ - packet->header.protocol_id = protocol_id; - packet->header.messages_count = 0; - packet->header.seq_number = seq_number; - packet->header.ack = ack; - packet->header.ack_bits = ack_bits; + return &inc_msg->message; + } - packet->mode = NBN_PACKET_MODE_WRITE; - packet->sender = NULL; - packet->size = NBN_PACKET_HEADER_SIZE; - packet->sealed = false; + channel->next_recv_message_id++; + } - memset(packet->buffer, 0, sizeof(packet->buffer)); -} + return NULL; + } else if (channel->type == NBN_CHANNEL_RELIABLE) { + if (!inc_msg->free && inc_msg->message.header.id == channel->next_recv_message_id) { + inc_msg->free = true; + channel->next_recv_message_id++; -int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_OutgoingMessage *out_msg) -{ - NBN_Message *message = out_msg->message; + return &inc_msg->message; + } - NBN_LogTrace("Write message %d (type: %d, length: %d) to packet %d", - out_msg->id, message->header.type, message->header.length, - packet->header.seq_number); + return NULL; + } else { + NBN_Abort(); + } +} - NBN_Assert( - (message->data != NULL && message->header.length > 0) || - (message->data == NULL && message->header.length == 0)); +static bool Channel_GetNextOutgoingMessage(NBN_Channel *channel, NBN_OutgoingMessage *res_out_msg, double time) { + if (channel->type == NBN_CHANNEL_UNRELIABLE) { + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[channel->next_outgoing_message_slot]; - if (packet->mode != NBN_PACKET_MODE_WRITE || packet->sealed) return NBN_PACKET_WRITE_ERROR; + if (out_msg->message == NULL) + return false; - unsigned int message_size = NBN_MESSAGE_HEADER_SIZE + message->header.length; + *res_out_msg = *out_msg; + out_msg->message = NULL; + channel->next_outgoing_message_slot++; + channel->next_outgoing_message_slot %= NBN_CHANNEL_BUFFER_SIZE; - if ( - packet->header.messages_count >= NBN_MAX_MESSAGES_PER_PACKET || - packet->size + message_size > NBN_PACKET_MAX_SIZE) - { - return NBN_PACKET_WRITE_NO_SPACE; - } + return true; + } else if (channel->type == NBN_CHANNEL_RELIABLE) { + int max_message_id = (channel->oldest_unacked_message_id + (NBN_CHANNEL_BUFFER_SIZE - 1)) % (0xFFFF + 1); - NBN_Writer writer; + if (SEQUENCE_NUMBER_LT(channel->next_outgoing_message_id, max_message_id)) + max_message_id = channel->next_outgoing_message_id; - NBN_Writer_Init(&writer, packet->buffer + packet->size, sizeof(packet->buffer) - packet->size); + uint16_t msg_id = channel->oldest_unacked_message_id; - NBN_Writer_WriteUInt16(&writer, out_msg->id); - NBN_Writer_WriteUInt16(&writer, message->header.length); - NBN_Writer_WriteUInt8(&writer, message->header.type); - NBN_Writer_WriteUInt8(&writer, message->header.channel_id); + while (SEQUENCE_NUMBER_LT(msg_id, max_message_id)) { + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; - NBN_Assert(writer.position == NBN_MESSAGE_HEADER_SIZE); + if (out_msg->message && + (out_msg->last_send_time < 0 || time - out_msg->last_send_time >= NBN_MESSAGE_RESEND_DELAY)) { + *res_out_msg = *out_msg; + return true; + } - if (message->data) - { - NBN_Assert(message->header.length > 0); - NBN_Writer_WriteBytes(&writer, message->data, message->header.length); + msg_id++; + } + + return false; + } else { + NBN_Abort(); } +} - NBN_Assert(writer.position == message_size); +static int Channel_OnMessageSent(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) { + if (channel->type != NBN_CHANNEL_UNRELIABLE) { + return 0; + } - packet->size += writer.position; - packet->header.messages_count++; + Endpoint_FreeMessage(endpoint, message); + channel->outgoing_message_count--; - return NBN_PACKET_WRITE_OK; + return 0; } -int NBN_Packet_Seal(NBN_Packet *packet, NBN_Connection *connection) -{ - if (packet->mode != NBN_PACKET_MODE_WRITE) - return NBN_ERROR; - - NBN_Writer writer; - - NBN_Writer_Init(&writer, packet->buffer, NBN_PACKET_HEADER_SIZE); - - NBN_Writer_WriteUInt32(&writer, packet->header.protocol_id); - NBN_Writer_WriteUInt32(&writer, packet->header.ack_bits); - NBN_Writer_WriteUInt16(&writer, packet->header.seq_number); - NBN_Writer_WriteUInt16(&writer, packet->header.ack); - NBN_Writer_WriteUInt8(&writer, packet->header.messages_count); - - NBN_Assert(writer.position == NBN_PACKET_HEADER_SIZE); - - packet->sealed = true; - - return 0; -} - -int NBN_Packet_InitRead(NBN_Packet *packet, uint32_t protocol_id, unsigned int size) -{ - NBN_Assert(size >= NBN_PACKET_HEADER_SIZE); +static int Channel_OnOutgoingMessageAcked(NBN_Endpoint *endpoint, NBN_Channel *channel, uint16_t msg_id) { + if (channel->type != NBN_CHANNEL_RELIABLE) { + return 0; + } - packet->mode = NBN_PACKET_MODE_READ; - packet->size = size; - packet->sender = NULL; // IMPORTANT: must be set by the drivers - packet->sealed = false; + int index = msg_id % NBN_CHANNEL_BUFFER_SIZE; + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[index]; - NBN_Reader reader; + if (out_msg->message == NULL || out_msg->id != msg_id) + return 0; - NBN_Reader_Init(&reader, packet->buffer, NBN_PACKET_HEADER_SIZE); + NBN_Message *message = out_msg->message; + out_msg->message = NULL; - if (NBN_Reader_ReadUInt32(&reader, &packet->header.protocol_id) < 0) - { - NBN_LogDebug("Failed to read packet's protocol id"); - return NBN_ERROR; - } + NBN_LogTrace("Message %d acked on channel %d (buffer index: %d, oldest unacked: %d)", msg_id, channel->id, index, + channel->oldest_unacked_message_id); - if (packet->header.protocol_id != protocol_id) - { - NBN_LogDebug("Packet's protocol id did not match (expected: %d, received: %d)", protocol_id, packet->header.protocol_id); - return NBN_ERROR; - } + channel->ack_buffer[index] = true; + channel->outgoing_message_count--; - if (NBN_Reader_ReadUInt32(&reader, &packet->header.ack_bits) < 0) - { - NBN_LogDebug("Failed to read packet's acked bits"); - return NBN_ERROR; - } + if (msg_id == channel->oldest_unacked_message_id) { + for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) { + uint16_t ack_msg_id = msg_id + i; + int index = ack_msg_id % NBN_CHANNEL_BUFFER_SIZE; - if (NBN_Reader_ReadUInt16(&reader, &packet->header.seq_number) < 0) - { - NBN_LogDebug("Failed to read packet's sequence number"); - return NBN_ERROR; - } + if (channel->ack_buffer[index]) { + channel->ack_buffer[index] = false; + channel->oldest_unacked_message_id++; + } else { + break; + } + } - if (NBN_Reader_ReadUInt16(&reader, &packet->header.ack) < 0) - { - NBN_LogDebug("Failed to read packet's ack"); - return NBN_ERROR; + NBN_LogTrace("Updated oldest unacked message id on channel %d: %d", channel->id, + channel->oldest_unacked_message_id); } - if (NBN_Reader_ReadUInt8(&reader, &packet->header.messages_count) < 0) - { - NBN_LogDebug("Failed to read packet's message count"); - return NBN_ERROR; - } + Endpoint_FreeMessage(endpoint, message); return 0; } -#pragma endregion /* NBN_Packet */ +static unsigned int Channel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2) { + if (SEQUENCE_NUMBER_GT(id1, id2)) + return (id1 >= id2) ? id1 - id2 : ((0xFFFF + 1) - id2) + id1; + else + return (id2 >= id1) ? id2 - id1 : (((0xFFFF + 1) - id1) + id2) % 0xFFFF; +} + +#pragma endregion /* NBN_Channel */ #pragma region NBN_Connection static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *, uint8_t, uint8_t); +static uint8_t *Endpoint_AllocateMessage(NBN_Endpoint *, uint8_t type, uint16_t *length); +static void Endpoint_DeallocateMessage(NBN_Endpoint *endpoint, uint8_t type, uint8_t *data); static uint32_t Connection_BuildPacketAckBits(NBN_Connection *); static int Connection_DecodePacketHeader(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); @@ -1878,15 +1700,14 @@ static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *, uint16_ static bool Connection_IsPacketReceived(NBN_Connection *, uint16_t); static int Connection_SendPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry *, double); static int Connection_ReadNextMessageFromBuffer(NBN_Endpoint *, NBN_Reader *, NBN_Message *); -static void Connection_ReleaseMessage(NBN_Endpoint *, NBN_Channel *, NBN_Message *); static void Connection_UpdateAveragePing(NBN_Connection *, double); static void Connection_UpdateAveragePacketLoss(NBN_Connection *, uint16_t); static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *, float); static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *, double); -NBN_Connection *NBN_Connection_Create(uint32_t id, NBN_Endpoint *endpoint, NBN_Driver *driver, void *driver_data) -{ - NBN_Connection *connection = (NBN_Connection*)MemoryManager_Alloc(NBN_MEM_CONNECTION); +// TODO: move this code into Endpoint_CreateConnection +NBN_Connection *NBN_Connection_Create(uint32_t id, NBN_Endpoint *endpoint, NBN_Driver *driver, void *driver_data) { + NBN_Connection *connection = (NBN_Connection *)NBN_Allocator(sizeof(NBN_Connection)); connection->id = id; connection->endpoint = endpoint; @@ -1900,17 +1721,15 @@ NBN_Connection *NBN_Connection_Create(uint32_t id, NBN_Endpoint *endpoint, NBN_D connection->is_stale = false; connection->is_closed = false; connection->vector_pos = -1; + connection->user_data = NULL; + connection->channels = NULL; - for (int i = 0; i < NBN_MAX_CHANNELS; i++) - connection->channels[i] = NULL; - - for (int i = 0; i < NBN_MAX_PACKET_ENTRIES; i++) - { + for (int i = 0; i < NBN_MAX_PACKET_ENTRIES; i++) { connection->packet_send_seq_buffer[i] = 0xFFFFFFFF; connection->packet_recv_seq_buffer[i] = 0xFFFFFFFF; } - NBN_ConnectionStats stats = { 0 }; + NBN_ConnectionStats stats = {0}; connection->stats = stats; connection->driver = driver; @@ -1919,27 +1738,19 @@ NBN_Connection *NBN_Connection_Create(uint32_t id, NBN_Endpoint *endpoint, NBN_D return connection; } -void NBN_Connection_Destroy(NBN_Endpoint *endpoint, NBN_Connection *connection) -{ - for (int i = 0; i < NBN_MAX_CHANNELS; i++) - { +void NBN_Connection_Destroy(NBN_Endpoint *endpoint, NBN_Connection *connection) { + for (unsigned int i = 0; i < connection->channel_count; i++) { NBN_Channel *channel = connection->channels[i]; - if (channel) - { - NBN_Assert(channel->destructor); - channel->destructor(endpoint, channel); - } + NBN_Channel_Destroy(endpoint, channel); } - MemoryManager_Dealloc(connection, NBN_MEM_CONNECTION); + NBN_Deallocator(connection); } -int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, - NBN_Packet *packet, double time) -{ - if (Connection_DecodePacketHeader(endpoint, connection, packet, time) < 0) - { +int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, + double time) { + if (Connection_DecodePacketHeader(endpoint, connection, packet, time) < 0) { NBN_LogError("Failed to decode packet %d header", packet->header.seq_number); return NBN_ERROR; @@ -1957,19 +1768,17 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection NBN_Reader_Init(&msg_reader, packet->buffer + NBN_PACKET_HEADER_SIZE, packet->size - NBN_PACKET_HEADER_SIZE); - NBN_LogDebug("Processing received packet %d (message count: %d)", packet->header.seq_number, packet->header.messages_count); + NBN_LogTrace("Processing received packet %d (message count: %d)", packet->header.seq_number, + packet->header.messages_count); - for (int i = 0; i < packet->header.messages_count; i++) - { + for (int i = 0; i < packet->header.messages_count; i++) { NBN_LogTrace("Reading message number %d from packet %d", i, packet->header.seq_number); NBN_Message message = {0}; - message.type = NBN_INCOMING_MSG; - message.ref_count = 1; + message.type = NBN_INCOMING_MESSAGE; int msg_len = Connection_ReadNextMessageFromBuffer(endpoint, &msg_reader, &message); - if (msg_len < 0) - { + if (msg_len < 0) { NBN_LogError("Failed to read packet, invalid data"); return NBN_ERROR; @@ -1977,29 +1786,26 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection uint8_t channel_id = message.header.channel_id; - if (channel_id > NBN_MAX_CHANNELS - 1 || connection->channels[channel_id] == NULL) - { - NBN_LogError("Failed to read packet, messages on invalid channels"); + if (channel_id > connection->channel_count - 1) { + NBN_LogError("Failed to read packet, message had invalid channel"); return NBN_ERROR; } NBN_Channel *channel = connection->channels[channel_id]; - if (channel->AddReceivedMessage(endpoint, channel, &message)) - { - NBN_LogDebug("Received message %d (type: %d) on channel %d", - message.header.id, message.header.type, channel->id); + if (Channel_AddReceivedMessage(endpoint, channel, &message)) { + NBN_LogTrace("Received message %d (type: %d) on channel %d", message.header.id, message.header.type, + channel->id); #ifdef NBN_DEBUG - if (connection->OnMessageAddedToRecvQueue) connection->OnMessageAddedToRecvQueue(connection, &message); + if (connection->OnMessageAddedToRecvQueue) + connection->OnMessageAddedToRecvQueue(connection, &message); #endif - } - else - { + } else { NBN_LogDebug("Received message %d : discarded", message.header.id); - Connection_ReleaseMessage(endpoint, channel, &message); + Endpoint_FreeMessage(endpoint, &message); } } @@ -2007,8 +1813,7 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection } int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connection, uint32_t protocol_id, - double time) -{ + double time) { NBN_LogTrace("Flushing all channels"); NBN_Packet packet = {0}; @@ -2018,38 +1823,26 @@ int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connect Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); - for (unsigned int i = 0; i < NBN_MAX_CHANNELS; i++) - { + for (unsigned int i = 0; i < connection->channel_count; i++) { NBN_Channel *channel = connection->channels[i]; - if (channel == NULL) - continue; - NBN_LogTrace("Flushing channel %d (message count: %d)", channel->id, channel->outgoing_message_count); NBN_OutgoingMessage out_msg; unsigned int j = 0; // TODO: use bandwidth to determine how many packets to send at most - while ( - j < channel->outgoing_message_count && - sent_packet_count < NBN_CONNECTION_MAX_SENT_PACKET_COUNT && - channel->GetNextOutgoingMessage(channel, &out_msg, time) - ) - { + while (j < channel->outgoing_message_count && sent_packet_count < NBN_CONNECTION_MAX_SENT_PACKET_COUNT && + Channel_GetNextOutgoingMessage(channel, &out_msg, time)) { NBN_Message *message = out_msg.message; uint16_t msg_id = out_msg.id; bool message_sent = false; int ret = NBN_Packet_WriteMessage(&packet, &out_msg); - if (ret == NBN_PACKET_WRITE_OK) - { + if (ret == NBN_PACKET_WRITE_OK) { message_sent = true; - } - else if (ret == NBN_PACKET_WRITE_NO_SPACE) - { - if (Connection_SendPacket(connection, &packet, packet_entry, time) < 0) - { + } else if (ret == NBN_PACKET_WRITE_NO_SPACE) { + if (Connection_SendPacket(connection, &packet, packet_entry, time) < 0) { NBN_LogError("Failed to send packet %d", packet.header.seq_number); return NBN_ERROR; @@ -2062,47 +1855,36 @@ int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connect int ret = NBN_Packet_WriteMessage(&packet, &out_msg); - if (ret != NBN_PACKET_WRITE_OK) - { + if (ret != NBN_PACKET_WRITE_OK) { NBN_LogError("Failed to send packet %d", packet.header.seq_number); return NBN_ERROR; } message_sent = true; - } - else if (ret == NBN_PACKET_WRITE_ERROR) - { - NBN_LogError("Failed to write message %d of type %d to packet %d", - msg_id, message->header.type, packet.header.seq_number); + } else if (ret == NBN_PACKET_WRITE_ERROR) { + NBN_LogError("Failed to write message %d of type %d to packet %d", msg_id, message->header.type, + packet.header.seq_number); return NBN_ERROR; } - if (message_sent) - { - NBN_LogTrace("Message %d added to packet %d (length: %d, type: %d)", - msg_id, packet.header.seq_number, - message->header.length, message->header.type); + if (message_sent) { + NBN_LogTrace("Message %d added to packet %d (length: %d, type: %d)", msg_id, packet.header.seq_number, + message->header.length, message->header.type); Channel_UpdateMessageSendTime(channel, msg_id, time); - packet_entry->messages[packet_entry->messages_count++] = (NBN_MessageEntry){ - msg_id, channel->id - }; + packet_entry->messages[packet_entry->messages_count++] = (NBN_MessageEntry){msg_id, channel->id}; - if (channel->OnOutgoingMessageSent) - { - channel->OnOutgoingMessageSent(endpoint, channel, message); - } + Channel_OnMessageSent(endpoint, channel, message); } j++; } } - if (Connection_SendPacket(connection, &packet, packet_entry, time) < 0) - { + if (Connection_SendPacket(connection, &packet, packet_entry, time) < 0) { NBN_LogError("Failed to send packet %d to connection %d", packet.header.seq_number, connection->id); return NBN_ERROR; @@ -2113,43 +1895,15 @@ int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connect double t = time - connection->last_flush_time; - if (t > 0) Connection_UpdateAverageUploadBandwidth(connection, sent_bytes / t); + if (t > 0) + Connection_UpdateAverageUploadBandwidth(connection, sent_bytes / t); connection->last_flush_time = time; return 0; } -int NBN_Connection_InitChannel(NBN_Connection *connection, NBN_Channel *channel) -{ - channel->connection = connection; - // channel->read_chunk_buffer = (uint8_t*)NBN_Allocator(NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE); - // channel->write_chunk_buffer = (uint8_t*)NBN_Allocator(NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE); - channel->read_chunk_buffer_size = NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE; - channel->write_chunk_buffer_size = NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE; - channel->next_outgoing_chunked_message = 0; - channel->next_outgoing_message_id = 0; - channel->next_recv_message_id = 0; - channel->outgoing_message_count = 0; - channel->chunk_count = 0; - channel->last_received_chunk_id = -1; - - for (unsigned int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) - { - channel->incoming_messages_buffer[i].free = true; - channel->outgoing_messages_buffer[i].message = NULL; - } - - // TODO - /*for (int i = 0; i < NBN_CHANNEL_CHUNKS_BUFFER_SIZE; i++)*/ - /* channel->recv_chunk_buffer[i] = NULL;*/ - /**/ - NBN_LogDebug("Initialized channel %d for connection %d", channel->id, connection->id); - return 0; -} - -bool NBN_Connection_CheckIfStale(NBN_Connection *connection, double time) -{ +bool NBN_Connection_CheckIfStale(NBN_Connection *connection, double time) { #if defined(NBN_DEBUG) && defined(NBN_DISABLE_STALE_CONNECTION_DETECTION) /* When testing under bad network conditions (in soak test for instance), we don't want to deal with stale connections */ @@ -2159,23 +1913,19 @@ bool NBN_Connection_CheckIfStale(NBN_Connection *connection, double time) #endif } -static int Connection_DecodePacketHeader(NBN_Endpoint *endpoint, NBN_Connection *connection, - NBN_Packet *packet, double time) -{ - if (Connection_AckPacket(endpoint, connection, packet->header.ack, time) < 0) - { +static int Connection_DecodePacketHeader(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, + double time) { + if (Connection_AckPacket(endpoint, connection, packet->header.ack, time) < 0) { NBN_LogError("Failed to ack packet %d", packet->header.seq_number); return NBN_ERROR; } - for (unsigned int i = 0; i < 32; i++) - { + for (unsigned int i = 0; i < 32; i++) { if (B_IS_UNSET(packet->header.ack_bits, i)) continue; - if (Connection_AckPacket(endpoint, connection, packet->header.ack - (i + 1), time) < 0) - { + if (Connection_AckPacket(endpoint, connection, packet->header.ack - (i + 1), time) < 0) { NBN_LogError("Failed to ack packet %d", packet->header.seq_number); return NBN_ERROR; @@ -2185,13 +1935,11 @@ static int Connection_DecodePacketHeader(NBN_Endpoint *endpoint, NBN_Connection return 0; } -static uint32_t Connection_BuildPacketAckBits(NBN_Connection *connection) -{ +static uint32_t Connection_BuildPacketAckBits(NBN_Connection *connection) { uint32_t ack_bits = 0; - for (int i = 0; i < 32; i++) - { - /* + for (int i = 0; i < 32; i++) { + /* when last_received_packet_seq_number is lower than 32, the value of acked_packet_seq_number will eventually wrap around, which means the packets from before the wrap around will naturally be acked */ @@ -2205,796 +1953,252 @@ static uint32_t Connection_BuildPacketAckBits(NBN_Connection *connection) return ack_bits; } -static int Connection_AckPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, uint16_t ack_packet_seq_number, double time) -{ +static int Connection_AckPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, uint16_t ack_packet_seq_number, + double time) { NBN_PacketEntry *packet_entry = Connection_FindSendPacketEntry(connection, ack_packet_seq_number); - if (packet_entry && !packet_entry->acked) - { + if (packet_entry && !packet_entry->acked) { NBN_LogTrace("Packet %d acked (connection: %d)", ack_packet_seq_number, connection->id); packet_entry->acked = true; Connection_UpdateAveragePing(connection, time - packet_entry->send_time); - for (unsigned int i = 0; i < packet_entry->messages_count; i++) - { + for (unsigned int i = 0; i < packet_entry->messages_count; i++) { NBN_MessageEntry *msg_entry = &packet_entry->messages[i]; NBN_Channel *channel = connection->channels[msg_entry->channel_id]; NBN_Assert(channel != NULL); - if (channel->OnOutgoingMessageAcked) - { - if (channel->OnOutgoingMessageAcked(endpoint, channel, msg_entry->id) < 0) - return NBN_ERROR; - } - } - } - - return 0; -} - -static void Connection_InitOutgoingPacket( - NBN_Connection *connection, uint32_t protocol_id, NBN_Packet *outgoing_packet, NBN_PacketEntry **packet_entry) -{ - NBN_Packet_InitWrite( - outgoing_packet, - protocol_id, - connection->next_packet_seq_number++, - connection->last_received_packet_seq_number, - Connection_BuildPacketAckBits(connection)); - - *packet_entry = Connection_InsertOutgoingPacketEntry(connection, outgoing_packet->header.seq_number); -} - -static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *connection, uint16_t seq_number) -{ - uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; - NBN_PacketEntry entry = { - .acked = false, - .flagged_as_lost = false, - .messages_count = 0, - .send_time = 0, - .messages = { {0, 0} } - }; - - connection->packet_send_seq_buffer[index] = seq_number; - connection->packet_send_buffer[index] = entry; - - return &connection->packet_send_buffer[index]; -} - -static bool Connection_InsertReceivedPacketEntry(NBN_Connection *connection, uint16_t seq_number) -{ - uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; - - /* Ignore duplicated packets */ - if (connection->packet_recv_seq_buffer[index] != 0xFFFFFFFF && - connection->packet_recv_seq_buffer[index] == seq_number) - return false; - - /* - Clear entries between the previous highest sequence numbers and new highest one - to avoid entries staying inside the sequence buffer from before the sequence wrap around - and break the packet acking logic. - */ - if (SEQUENCE_NUMBER_GT(seq_number, connection->last_received_packet_seq_number)) - { - for (uint16_t seq = connection->last_received_packet_seq_number + 1; SEQUENCE_NUMBER_LT(seq, seq_number); seq++) - connection->packet_recv_seq_buffer[seq % NBN_MAX_PACKET_ENTRIES] = 0xFFFFFFFF; - } - - connection->packet_recv_seq_buffer[index] = seq_number; - - return true; -} - -static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *connection, uint16_t seq_number) -{ - uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; - - if (connection->packet_send_seq_buffer[index] == seq_number) - return &connection->packet_send_buffer[index]; - - return NULL; -} - -static bool Connection_IsPacketReceived(NBN_Connection *connection, uint16_t packet_seq_number) -{ - uint16_t index = packet_seq_number % NBN_MAX_PACKET_ENTRIES; - - return connection->packet_recv_seq_buffer[index] == packet_seq_number; -} - -static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, NBN_PacketEntry *packet_entry, double time) -{ - NBN_LogTrace("Send packet %d to connection %d (messages count: %d)", - packet->header.seq_number, connection->id, packet->header.messages_count); - - NBN_Assert(packet_entry->messages_count == packet->header.messages_count); - - if (NBN_Packet_Seal(packet, connection) < 0) - { - NBN_LogError("Failed to seal packet"); - - return NBN_ERROR; - } - - packet_entry->send_time = time; - - if (connection->endpoint->is_server) - { -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - return NBN_PacketSimulator_EnqueuePacket(&nbn_game_server.endpoint.packet_simulator, packet, connection); -#else - if (connection->is_stale) - return 0; - - return connection->driver->impl.serv_send_packet_to(packet, connection); -#endif - } - else - { -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - return NBN_PacketSimulator_EnqueuePacket(&nbn_game_client.endpoint.packet_simulator, packet, connection); -#else - NBN_Driver *driver = nbn_game_client.server_connection->driver; - - return driver->impl.cli_send_packet(packet); -#endif - } -} - -static int Connection_ReadNextMessageFromBuffer(NBN_Endpoint *endpoint, NBN_Reader *reader, NBN_Message *message) -{ - if (NBN_Reader_ReadUInt16(reader, &message->header.id) < 0) - { - NBN_LogError("Failed to read message id"); - - return NBN_ERROR; - } - - if (NBN_Reader_ReadUInt16(reader, &message->header.length) < 0) - { - NBN_LogError("Failed to read message length"); - - return NBN_ERROR; - } - - if (NBN_Reader_ReadUInt8(reader, &message->header.type) < 0) - { - NBN_LogError("Failed to read message type"); - - return NBN_ERROR; - } - - if (NBN_Reader_ReadUInt8(reader, &message->header.channel_id) < 0) - { - NBN_LogError("Failed to read message channel"); - - return NBN_ERROR; - } - - if (message->header.length > 0) - { - NBN_MemPool *msg_buffer_pool = endpoint->msg_buffer_pools[message->header.type]; - - if (msg_buffer_pool == NULL) - { - NBN_LogError("No message buffer pool for message of type %d", message->header.type); - - return NBN_ERROR; - } - - if (message->header.length > msg_buffer_pool->block_size) - { - NBN_LogError("Message of type %d is too big for buffer pool", message->header.type); - - return NBN_ERROR; - } - - message->data = MemPool_Alloc(msg_buffer_pool); - - if (NBN_Reader_ReadBytes(reader, message->data, message->header.length) < 0) - { - NBN_LogError("Failed to read message data"); - - return NBN_ERROR; - } - } - - return 0; -} - -static void Connection_ReleaseMessage(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) -{ - NBN_Assert(message->ref_count > 0); - - message->ref_count--; - - if (message->ref_count > 0) - { - return; - } - - if (message->type == NBN_OUTGOING_MESSAGE) - { - if (message->header.length > 0) - { - NBN_MemPool *msg_buffer_pool = endpoint->msg_buffer_pools[message->header.type]; - - NBN_Assert(msg_buffer_pool != NULL); - MemPool_Dealloc(msg_buffer_pool, message->data); - } - - MemPool_Dealloc(endpoint->outgoing_msg_pool, message); - } - else if (message->type == NBN_INCOMING_MSG) - { - if (message->header.length > 0) - { - NBN_MemPool *msg_buffer_pool = endpoint->msg_buffer_pools[message->header.type]; - - NBN_Assert(msg_buffer_pool != NULL); - MemPool_Dealloc(msg_buffer_pool, message->data); - } - } - else - { - NBN_LogError("Invalid internal message type: %d", message->type); - NBN_Abort(); - } - - // TODO: chunk releasing - // if (message->ref_count == 0) - // { - // - // /*if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) - // { - // NBN_MessageChunk *chunk = (NBN_MessageChunk*)message->data; - // - // if (chunk->outgoing_msg) - // { - // NBN_Assert(chunk->outgoing_msg->ref_count > 0); - // - // if (--chunk->outgoing_msg->ref_count == 0) - // { - // // TODO - // } - // } - // }*/ - // - // NBN_Deallocator(message->data); - // } -} - -static void Connection_UpdateAveragePing(NBN_Connection *connection, double ping) -{ - /* exponential smoothing with a factor of 0.05 */ - connection->stats.ping = connection->stats.ping + .05f * (ping - connection->stats.ping); -} - -static void Connection_UpdateAveragePacketLoss(NBN_Connection *connection, uint16_t seq) -{ - unsigned int lost_packet_count = 0; - uint16_t start_seq = seq - 64; - - for (int i = 0; i < 100; i++) - { - uint16_t s = start_seq - i; - NBN_PacketEntry *entry = Connection_FindSendPacketEntry(connection, s); - - if (entry && !entry->acked) - { - lost_packet_count++; - - if (!entry->flagged_as_lost) - { - entry->flagged_as_lost = true; - connection->stats.total_lost_packets++; + if (Channel_OnOutgoingMessageAcked(endpoint, channel, msg_entry->id) < 0) { + return NBN_ERROR; } } } - float packet_loss = lost_packet_count / 100.f; - - /* exponential smoothing with a factor of 0.1 */ - connection->stats.packet_loss = connection->stats.packet_loss + .1f * (packet_loss - connection->stats.packet_loss); -} - -static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *connection, float bytes_per_sec) -{ - /* exponential smoothing with a factor of 0.1 */ - connection->stats.upload_bandwidth = - connection->stats.upload_bandwidth + .1f * (bytes_per_sec - connection->stats.upload_bandwidth); -} - -static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *connection, double time) -{ - double t = time - connection->last_read_packets_time; - - if (t == 0) - return; - - float bytes_per_sec = connection->downloaded_bytes / t; - - /* exponential smoothing with a factor of 0.1 */ - connection->stats.download_bandwidth = - connection->stats.download_bandwidth + .1f * (bytes_per_sec - connection->stats.download_bandwidth); - - connection->downloaded_bytes = 0; -} - -#pragma endregion /* NBN_Connection */ - -#pragma region NBN_Channel - -void NBN_Channel_Destroy(NBN_Endpoint *endpoint, NBN_Channel *channel) -{ - for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) - { - NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[i]; - - if (!inc_msg->free) - { - Connection_ReleaseMessage(endpoint, channel, &inc_msg->message); - } - - NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[i]; - - if (out_msg->message) - { - Connection_ReleaseMessage(endpoint, channel, out_msg->message); - } - } - - /*NBN_Deallocator(channel->read_chunk_buffer);*/ - /*NBN_Deallocator(channel->write_chunk_buffer);*/ - - NBN_Deallocator(channel); -} - -/*bool NBN_Channel_AddChunk(NBN_Channel *channel, NBN_Message *chunk_msg)*/ -/*{*/ -/* NBN_Assert(chunk_msg->header.type == NBN_MESSAGE_CHUNK_TYPE);*/ -/**/ -/* NBN_MessageChunk *chunk = (NBN_MessageChunk *)chunk_msg->data;*/ -/**/ -/* NBN_LogTrace("Add chunk %d to channel %d (current chunk count: %d, last recved chunk id: %d)",*/ -/* chunk->id, channel->id, channel->chunk_count, channel->last_received_chunk_id);*/ -/**/ -/* if (chunk->id == channel->last_received_chunk_id + 1)*/ -/* {*/ -/* NBN_Assert(channel->recv_chunk_buffer[chunk->id] == NULL);*/ -/**/ -/* channel->recv_chunk_buffer[chunk->id] = chunk;*/ -/* channel->last_received_chunk_id++;*/ -/* channel->chunk_count++;*/ -/**/ -/* NBN_LogTrace("Chunk added (%d/%d)", channel->chunk_count, chunk->total);*/ -/**/ -/* if (channel->chunk_count == chunk->total)*/ -/* {*/ -/* channel->last_received_chunk_id = -1;*/ -/**/ -/* return true;*/ -/* }*/ -/**/ -/* return false;*/ -/* }*/ -/* else*/ -/* {*/ -/* NBN_LogTrace("Chunk ignored");*/ -/* }*/ -/**/ -/* for (unsigned int i = 0; i < channel->chunk_count; i++)*/ -/* {*/ -/* NBN_Assert(channel->recv_chunk_buffer[i] != NULL);*/ -/**/ -/* NBN_MessageChunk_Destroy(channel->recv_chunk_buffer[i]);*/ -/**/ -/* channel->recv_chunk_buffer[i] = NULL;*/ -/* }*/ -/**/ -/* channel->chunk_count = 0;*/ -/* channel->last_received_chunk_id = -1;*/ -/**/ -/* if (chunk->id == 0)*/ -/* return NBN_Channel_AddChunk(channel, chunk_msg);*/ -/**/ -/* return false;*/ -/*}*/ - -int NBN_Channel_ReconstructMessageFromChunks(NBN_Channel *channel, NBN_Connection *connection, NBN_Message *message) -{ - // TODO - - /*unsigned int message_size = channel->chunk_count * NBN_MESSAGE_CHUNK_SIZE; - - if (message_size > channel->read_chunk_buffer_size) - NBN_Channel_ResizeReadChunkBuffer(channel, message_size); - - NBN_LogTrace("Reconstructing message (chunk count: %d, size: %d) from channel %d", - channel->chunk_count, message_size, channel->id); - - for (unsigned int i = 0; i < channel->chunk_count; i++) - { - NBN_MessageChunk *chunk = channel->recv_chunk_buffer[i]; - - memcpy(channel->read_chunk_buffer + i * NBN_MESSAGE_CHUNK_SIZE, chunk->data, NBN_MESSAGE_CHUNK_SIZE); - - NBN_MessageChunk_Destroy(chunk); - - channel->recv_chunk_buffer[i] = NULL; - } - - channel->chunk_count = 0; - - NBN_ReadStream r_stream; - - NBN_ReadStream_Init(&r_stream, channel->read_chunk_buffer, message_size); - - if (Connection_ReadNextMessageFromStream(connection, &r_stream, message) < 0) - return NBN_ERROR; - - NBN_LogTrace("Reconstructed message %d of type %d", message->header.id, message->header.type);*/ - - return 0; -} - -/*void NBN_Channel_ResizeWriteChunkBuffer(NBN_Channel *channel, unsigned int size)*/ -/*{*/ -/* channel->write_chunk_buffer = (uint8_t *)NBN_Reallocator(channel->write_chunk_buffer, size);*/ -/**/ -/* channel->write_chunk_buffer_size = size;*/ -/*}*/ -/**/ -/*void NBN_Channel_ResizeReadChunkBuffer(NBN_Channel *channel, unsigned int size)*/ -/*{*/ -/* channel->read_chunk_buffer = (uint8_t *)NBN_Reallocator(channel->read_chunk_buffer, size);*/ -/**/ -/* channel->read_chunk_buffer_size = size;*/ -/*}*/ - -static void Channel_UpdateMessageSendTime(NBN_Channel *channel, uint16_t msg_id, double time) -{ - NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; - NBN_Assert(msg_id == out_msg->id); - - out_msg->last_send_time = time; -} - -/* Unreliable ordered */ - -static bool UnreliableOrderedChannel_AddReceivedMessage(NBN_Endpoint *, NBN_Channel *, NBN_Message *); -static bool UnreliableOrderedChannel_AddOutgoingMessage(NBN_Channel *, NBN_Message *); -static NBN_Message *UnreliableOrderedChannel_GetNextRecvedMessage(NBN_Channel *); -static bool UnreliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *, NBN_OutgoingMessage *, double); -static int UnreliableOrderedChannel_OnMessageSent(NBN_Endpoint *, NBN_Channel *, NBN_Message *); - -NBN_UnreliableOrderedChannel *NBN_UnreliableOrderedChannel_Create(void) -{ - NBN_UnreliableOrderedChannel *channel = (NBN_UnreliableOrderedChannel *)NBN_Allocator(sizeof(NBN_UnreliableOrderedChannel)); - - channel->base.AddReceivedMessage = UnreliableOrderedChannel_AddReceivedMessage; - channel->base.AddOutgoingMessage = UnreliableOrderedChannel_AddOutgoingMessage; - channel->base.GetNextRecvedMessage = UnreliableOrderedChannel_GetNextRecvedMessage; - channel->base.GetNextOutgoingMessage = UnreliableOrderedChannel_GetNextOutgoingMessage; - channel->base.OnOutgoingMessageAcked = NULL; - channel->base.OnOutgoingMessageSent = UnreliableOrderedChannel_OnMessageSent; - - channel->last_received_message_id = 0; - channel->next_outgoing_message_slot = 0; - - return channel; -} - -static bool UnreliableOrderedChannel_AddReceivedMessage(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) -{ - NBN_UnreliableOrderedChannel *unreliable_ordered_channel = (NBN_UnreliableOrderedChannel *)channel; - - if (SEQUENCE_NUMBER_GT(message->header.id, unreliable_ordered_channel->last_received_message_id)) - { - NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[ - message->header.id % NBN_CHANNEL_BUFFER_SIZE]; - - inc_msg->message = *message; - inc_msg->free = false; - - unreliable_ordered_channel->last_received_message_id = message->header.id; - - NBN_LogTrace("Add incomoing message %d of type %d to unreliable channel %d (last received msg id: %d)", - message->header.id, message->header.type, channel->id, unreliable_ordered_channel->last_received_message_id); - - return true; - } - - return false; -} - -static bool UnreliableOrderedChannel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *message) -{ - uint16_t msg_id = channel->next_outgoing_message_id; - NBN_OutgoingMessage *out_msg = - &channel->outgoing_messages_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; - - // make sure this buffer slot is not already in use - if (out_msg->message) - { - NBN_LogError("No outgoing message available in unreliable channel %d (outgoing message count: %d)", - channel->id, channel->outgoing_message_count); - - return false; - } - - out_msg->id = msg_id; - out_msg->message = message; - out_msg->last_send_time = -1; - - channel->next_outgoing_message_id++; - channel->outgoing_message_count++; - - return true; -} - -static NBN_Message *UnreliableOrderedChannel_GetNextRecvedMessage(NBN_Channel *channel) -{ - NBN_UnreliableOrderedChannel *unreliable_ordered_channel = (NBN_UnreliableOrderedChannel *)channel; - - while (SEQUENCE_NUMBER_LT(channel->next_recv_message_id, unreliable_ordered_channel->last_received_message_id)) - { - NBN_IncomingMessage *inc_msg= &channel->incoming_messages_buffer[ - channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; - - if (!inc_msg->free && - inc_msg->message.header.id == channel->next_recv_message_id) - { - inc_msg->free = true; - - return &inc_msg->message; - } - - channel->next_recv_message_id++; - } - - return NULL; -} - -static bool UnreliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *channel, - NBN_OutgoingMessage *res_out_msg, double time) -{ - (void)time; - - NBN_UnreliableOrderedChannel *unreliable_ordered_channel = (NBN_UnreliableOrderedChannel *)channel; - NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[ - unreliable_ordered_channel->next_outgoing_message_slot]; - - if (out_msg->message == NULL) return false; + return 0; +} - *res_out_msg = *out_msg; - out_msg->message = NULL; - unreliable_ordered_channel->next_outgoing_message_slot++; - unreliable_ordered_channel->next_outgoing_message_slot %= NBN_CHANNEL_BUFFER_SIZE; +static void Connection_InitOutgoingPacket(NBN_Connection *connection, uint32_t protocol_id, NBN_Packet *outgoing_packet, + NBN_PacketEntry **packet_entry) { + NBN_Packet_InitWrite(outgoing_packet, protocol_id, connection->next_packet_seq_number++, + connection->last_received_packet_seq_number, Connection_BuildPacketAckBits(connection)); - return true; + *packet_entry = Connection_InsertOutgoingPacketEntry(connection, outgoing_packet->header.seq_number); } -static int UnreliableOrderedChannel_OnMessageSent(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) -{ - Connection_ReleaseMessage(endpoint, channel, message); - channel->outgoing_message_count--; +static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *connection, uint16_t seq_number) { + uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; + NBN_PacketEntry entry = { + .acked = false, .flagged_as_lost = false, .messages_count = 0, .send_time = 0, .messages = {{0, 0}}}; - return 0; + connection->packet_send_seq_buffer[index] = seq_number; + connection->packet_send_buffer[index] = entry; + + return &connection->packet_send_buffer[index]; } -/* Reliable ordered */ +static bool Connection_InsertReceivedPacketEntry(NBN_Connection *connection, uint16_t seq_number) { + uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; -static bool ReliableOrderedChannel_AddReceivedMessage(NBN_Endpoint *, NBN_Channel *, NBN_Message *); -static bool ReliableOrderedChannel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *); -static NBN_Message *ReliableOrderedChannel_GetNextRecvedMessage(NBN_Channel *); -static bool ReliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *, NBN_OutgoingMessage *, double); -static int ReliableOrderedChannel_OnOutgoingMessageAcked(NBN_Endpoint *, NBN_Channel *, uint16_t); + /* Ignore duplicated packets */ + if (connection->packet_recv_seq_buffer[index] != 0xFFFFFFFF && + connection->packet_recv_seq_buffer[index] == seq_number) + return false; -NBN_ReliableOrderedChannel *NBN_ReliableOrderedChannel_Create(void) -{ - NBN_ReliableOrderedChannel *channel = (NBN_ReliableOrderedChannel *)NBN_Allocator(sizeof(NBN_ReliableOrderedChannel)); + /* + Clear entries between the previous highest sequence numbers and new highest one + to avoid entries staying inside the sequence buffer from before the sequence wrap around + and break the packet acking logic. + */ + if (SEQUENCE_NUMBER_GT(seq_number, connection->last_received_packet_seq_number)) { + for (uint16_t seq = connection->last_received_packet_seq_number + 1; SEQUENCE_NUMBER_LT(seq, seq_number); seq++) + connection->packet_recv_seq_buffer[seq % NBN_MAX_PACKET_ENTRIES] = 0xFFFFFFFF; + } - channel->base.AddReceivedMessage = ReliableOrderedChannel_AddReceivedMessage; - channel->base.AddOutgoingMessage = ReliableOrderedChannel_AddOutgoingMessage; - channel->base.GetNextRecvedMessage = ReliableOrderedChannel_GetNextRecvedMessage; - channel->base.GetNextOutgoingMessage = ReliableOrderedChannel_GetNextOutgoingMessage; - channel->base.OnOutgoingMessageAcked = ReliableOrderedChannel_OnOutgoingMessageAcked; - channel->base.OnOutgoingMessageSent = NULL; + connection->packet_recv_seq_buffer[index] = seq_number; - memset(channel->base.incoming_messages_buffer, 0, sizeof(channel->base.incoming_messages_buffer)); + return true; +} - for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) - channel->ack_buffer[i] = false; +static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *connection, uint16_t seq_number) { + uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; - channel->oldest_unacked_message_id = 0; - channel->most_recent_message_id = 0; + if (connection->packet_send_seq_buffer[index] == seq_number) + return &connection->packet_send_buffer[index]; - return channel; + return NULL; } -static unsigned int ReliableOrderedChannel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2) -{ - if (SEQUENCE_NUMBER_GT(id1, id2)) - return (id1 >= id2) ? id1 - id2 : ((0xFFFF + 1) - id2) + id1; - else - return (id2 >= id1) ? id2 - id1 : (((0xFFFF + 1) - id1) + id2) % 0xFFFF; +static bool Connection_IsPacketReceived(NBN_Connection *connection, uint16_t packet_seq_number) { + uint16_t index = packet_seq_number % NBN_MAX_PACKET_ENTRIES; + + return connection->packet_recv_seq_buffer[index] == packet_seq_number; } -static bool ReliableOrderedChannel_AddReceivedMessage(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) -{ - NBN_ReliableOrderedChannel *reliable_ordered_channel = (NBN_ReliableOrderedChannel *)channel; - unsigned int dt = ReliableOrderedChannel_ComputeMessageIdDelta( - message->header.id, reliable_ordered_channel->most_recent_message_id); +static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, NBN_PacketEntry *packet_entry, + double time) { + NBN_LogTrace("Send packet %d to connection %d (messages count: %d)", packet->header.seq_number, connection->id, + packet->header.messages_count); - NBN_LogTrace("Add incomoing message %d of type %d to reliable channel %d (most recent msg id: %d, dt: %d)", - message->header.id, message->header.type, channel->id, reliable_ordered_channel->most_recent_message_id, dt); + NBN_Assert(packet_entry->messages_count == packet->header.messages_count); - if (SEQUENCE_NUMBER_GT(message->header.id, reliable_ordered_channel->most_recent_message_id)) - { - NBN_Assert(dt < NBN_CHANNEL_BUFFER_SIZE); + if (NBN_Packet_Seal(packet) < 0) { + NBN_LogError("Failed to seal packet"); - reliable_ordered_channel->most_recent_message_id = message->header.id; - } - else - { - /* This is an old message that has already been received, probably coming from - an out of order late packet. */ - if (dt >= NBN_CHANNEL_BUFFER_SIZE) return false; + return NBN_ERROR; } - NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[ - message->header.id % NBN_CHANNEL_BUFFER_SIZE]; + packet_entry->send_time = time; - if (!inc_msg->free) - { - Connection_ReleaseMessage(endpoint, channel, &inc_msg->message); - } + if (connection->endpoint->is_server) { +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + return NBN_PacketSimulator_EnqueuePacket(&nbn_game_server.endpoint.packet_simulator, packet, connection); +#else + if (connection->is_stale) + return 0; - inc_msg->message = *message; - inc_msg->free = false; + return connection->driver->impl.serv_send_packet_to(packet, connection); +#endif + } else { +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + return NBN_PacketSimulator_EnqueuePacket(&nbn_game_client.endpoint.packet_simulator, packet, connection); +#else + NBN_Driver *driver = nbn_game_client.server_connection->driver; - return true; + return driver->impl.cli_send_packet(packet); +#endif + } } -static bool ReliableOrderedChannel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *message) -{ - uint16_t msg_id = channel->next_outgoing_message_id; - int index = msg_id % NBN_CHANNEL_BUFFER_SIZE; - NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[index]; +static int Connection_ReadNextMessageFromBuffer(NBN_Endpoint *endpoint, NBN_Reader *reader, NBN_Message *message) { + if (NBN_Reader_ReadUInt16(reader, &message->header.id) < 0) { + NBN_LogError("Failed to read message id"); - // make sure the outgoing message is not already in use - if (out_msg->message) - { - NBN_LogError("No outgoing message available in reliable channel %d (outgoing message count: %d)", - channel->id, channel->outgoing_message_count); + return NBN_ERROR; + } - return false; + if (NBN_Reader_ReadUInt16(reader, &message->header.length) < 0) { + NBN_LogError("Failed to read message length"); + + return NBN_ERROR; } - out_msg->message = message; - out_msg->id = msg_id; - out_msg->last_send_time = -1; + if (NBN_Reader_ReadUInt8(reader, &message->header.type) < 0) { + NBN_LogError("Failed to read message type"); - channel->next_outgoing_message_id++; - channel->outgoing_message_count++; + return NBN_ERROR; + } - NBN_LogTrace("Added outgoing message %d of type %d to reliable channel %d (index: %d)", - msg_id, message->header.type, channel->id, index); + if (NBN_Reader_ReadUInt8(reader, &message->header.channel_id) < 0) { + NBN_LogError("Failed to read message channel"); - return true; -} + return NBN_ERROR; + } -static NBN_Message *ReliableOrderedChannel_GetNextRecvedMessage(NBN_Channel *channel) -{ - NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[ - channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; + if (message->header.length > 0) { + message->data = NBN_Allocator(message->header.length); + endpoint->active_inc_msg_buffer_count++; - if (!inc_msg->free && inc_msg->message.header.id == channel->next_recv_message_id) - { - inc_msg->free = true; - channel->next_recv_message_id++; + if (NBN_Reader_ReadBytes(reader, message->data, message->header.length) < 0) { + NBN_LogError("Failed to read message data"); + Endpoint_FreeMessage(endpoint, message); - return &inc_msg->message; + return NBN_ERROR; + } } - return NULL; + return 0; } -static bool ReliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *channel, - NBN_OutgoingMessage *res_out_msg, double time) -{ - NBN_ReliableOrderedChannel *reliable_ordered_channel = (NBN_ReliableOrderedChannel *)channel; - - int max_message_id = (reliable_ordered_channel->oldest_unacked_message_id + (NBN_CHANNEL_BUFFER_SIZE - 1)) % (0xFFFF + 1); +static void Endpoint_FreeMessage(NBN_Endpoint *endpoint, NBN_Message *message) { + NBN_Assert(message->type == NBN_OUTGOING_MESSAGE || message->type == NBN_INCOMING_MESSAGE); - if (SEQUENCE_NUMBER_LT(channel->next_outgoing_message_id, max_message_id)) - max_message_id = channel->next_outgoing_message_id; + if (message->type == NBN_OUTGOING_MESSAGE) { + NBN_Assert(message->ref_count > 0); - uint16_t msg_id = reliable_ordered_channel->oldest_unacked_message_id; + message->ref_count--; - while (SEQUENCE_NUMBER_LT(msg_id, max_message_id)) - { - NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; + if (message->ref_count > 0) { + return; + } - if ( - out_msg->message && - (out_msg->last_send_time < 0 || time - out_msg->last_send_time >= NBN_MESSAGE_RESEND_DELAY)) - { - *res_out_msg = *out_msg; - return true; + if (message->header.length > 0) { + NBN_Deallocator(message->data); + endpoint->active_out_msg_buffer_count--; } - msg_id++; + NBN_Deallocator(message); + endpoint->active_out_msg_count--; + } else if (message->type == NBN_INCOMING_MESSAGE) { + if (message->header.length > 0) { + NBN_Deallocator(message->data); + endpoint->active_inc_msg_buffer_count--; + } } +} - return false; +static void Connection_UpdateAveragePing(NBN_Connection *connection, double ping) { + /* exponential smoothing with a factor of 0.05 */ + connection->stats.ping = connection->stats.ping + .05f * (ping - connection->stats.ping); } -static int ReliableOrderedChannel_OnOutgoingMessageAcked(NBN_Endpoint *endpoint, NBN_Channel *channel, uint16_t msg_id) -{ - NBN_ReliableOrderedChannel *reliable_ordered_channel = (NBN_ReliableOrderedChannel *)channel; - int index = msg_id % NBN_CHANNEL_BUFFER_SIZE; - NBN_OutgoingMessage *out_msg = &reliable_ordered_channel-> - base.outgoing_messages_buffer[index]; +static void Connection_UpdateAveragePacketLoss(NBN_Connection *connection, uint16_t seq) { + unsigned int lost_packet_count = 0; + uint16_t start_seq = seq - 64; - if (out_msg->message == NULL || out_msg->id != msg_id) - return 0; + for (int i = 0; i < 100; i++) { + uint16_t s = start_seq - i; + NBN_PacketEntry *entry = Connection_FindSendPacketEntry(connection, s); - NBN_Message *message = out_msg->message; - out_msg->message = NULL; + if (entry && !entry->acked) { + lost_packet_count++; - NBN_LogTrace("Message %d acked on channel %d (buffer index: %d, oldest unacked: %d)", - msg_id, channel->id, index, reliable_ordered_channel->oldest_unacked_message_id); + if (!entry->flagged_as_lost) { + entry->flagged_as_lost = true; + connection->stats.total_lost_packets++; + } + } + } - reliable_ordered_channel->ack_buffer[index] = true; - channel->outgoing_message_count--; + float packet_loss = lost_packet_count / 100.f; - if (msg_id == reliable_ordered_channel->oldest_unacked_message_id) - { - for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) - { - uint16_t ack_msg_id = msg_id + i; - int index = ack_msg_id % NBN_CHANNEL_BUFFER_SIZE; + /* exponential smoothing with a factor of 0.1 */ + connection->stats.packet_loss = connection->stats.packet_loss + .1f * (packet_loss - connection->stats.packet_loss); +} - if (reliable_ordered_channel->ack_buffer[index]) - { - reliable_ordered_channel->ack_buffer[index] = false; - reliable_ordered_channel->oldest_unacked_message_id++; - } - else - { - break; - } - } +static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *connection, float bytes_per_sec) { + /* exponential smoothing with a factor of 0.1 */ + connection->stats.upload_bandwidth = + connection->stats.upload_bandwidth + .1f * (bytes_per_sec - connection->stats.upload_bandwidth); +} - NBN_LogTrace("Updated oldest unacked message id on channel %d: %d", - channel->id, reliable_ordered_channel->oldest_unacked_message_id); - } +static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *connection, double time) { + double t = time - connection->last_read_packets_time; + + if (t == 0) + return; - Connection_ReleaseMessage(endpoint, channel, message); + float bytes_per_sec = connection->downloaded_bytes / t; - return 0; + /* exponential smoothing with a factor of 0.1 */ + connection->stats.download_bandwidth = + connection->stats.download_bandwidth + .1f * (bytes_per_sec - connection->stats.download_bandwidth); + + connection->downloaded_bytes = 0; } -#pragma endregion /* NBN_MessageChannel */ +#pragma endregion /* NBN_Connection */ #pragma region NBN_EventQueue -void NBN_EventQueue_Init(NBN_EventQueue *event_queue) -{ +void NBN_EventQueue_Init(NBN_EventQueue *event_queue) { event_queue->head = 0; event_queue->tail = 0; event_queue->count = 0; } -bool NBN_EventQueue_Enqueue(NBN_EventQueue *event_queue, NBN_Event ev) -{ +bool NBN_EventQueue_Enqueue(NBN_EventQueue *event_queue, NBN_Event ev) { if (event_queue->count >= NBN_EVENT_QUEUE_CAPACITY) return false; @@ -3006,8 +2210,7 @@ bool NBN_EventQueue_Enqueue(NBN_EventQueue *event_queue, NBN_Event ev) return true; } -bool NBN_EventQueue_Dequeue(NBN_EventQueue *event_queue, NBN_Event *ev) -{ +bool NBN_EventQueue_Dequeue(NBN_EventQueue *event_queue, NBN_Event *ev) { if (NBN_EventQueue_IsEmpty(event_queue)) return false; @@ -3018,81 +2221,35 @@ bool NBN_EventQueue_Dequeue(NBN_EventQueue *event_queue, NBN_Event *ev) return true; } -bool NBN_EventQueue_IsEmpty(NBN_EventQueue *event_queue) -{ - return event_queue->count == 0; -} +bool NBN_EventQueue_IsEmpty(NBN_EventQueue *event_queue) { return event_queue->count == 0; } #pragma endregion /* NBN_EventQueue */ #pragma region NBN_Endpoint -static void Endpoint_Init(NBN_Endpoint *, NBN_UserMessageDef[NBN_USER_MESSAGE_TYPE_RANGE + 1], uint32_t, bool); +static void Endpoint_Init(NBN_Endpoint *, NBN_Endpoint_Config, uint32_t, bool); static void Endpoint_Deinit(NBN_Endpoint *); static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, uint32_t, int, void *); static uint32_t Endpoint_BuildProtocolId(const char *); -static void Endpoint_RegisterChannel(NBN_Endpoint *, uint8_t, NBN_ChannelBuilder, NBN_ChannelDestructor); static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *, NBN_Packet *, NBN_Connection *); static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN_Message *); -// static int Endpoint_SplitMessageIntoChunks(NBN_Message *, NBN_OutgoingMessage *, NBN_Channel *, NBN_MessageSerializer, unsigned int, NBN_MessageChunk **); static void Endpoint_UpdateTime(NBN_Endpoint *); -static void Endpoint_Init(NBN_Endpoint *endpoint, - NBN_UserMessageDef user_message_defs[NBN_USER_MESSAGE_TYPE_RANGE + 1], - uint32_t protocol_id, bool is_server) -{ - MemoryManager_Init(); - +static void Endpoint_Init(NBN_Endpoint *endpoint, NBN_Endpoint_Config config, uint32_t protocol_id, bool is_server) { endpoint->is_server = is_server; endpoint->protocol_id = protocol_id; endpoint->write_message = NULL; - endpoint->write_message_buffer_len = 0; - - for (int i = 0; i < NBN_MAX_CHANNELS; i++) - { - endpoint->channel_builders[i] = NULL; - endpoint->channel_destructors[i] = NULL; - } + endpoint->write_message_buffer_len = sizeof(endpoint->scratch_write_buffer); + endpoint->last_message_need_free = false; + endpoint->custom_reliable_channels = config.custom_reliable_channels; + endpoint->active_out_msg_count = 0; + endpoint->active_inc_msg_buffer_count = 0; + endpoint->active_out_msg_buffer_count = 0; + endpoint->msg_allocator = config.msg_allocator; + endpoint->msg_deallocator = config.msg_deallocator; NBN_EventQueue_Init(&endpoint->event_queue); - /* Register library reserved reliable channel */ - Endpoint_RegisterChannel(endpoint, NBN_CHANNEL_RESERVED_UNRELIABLE, (NBN_ChannelBuilder)NBN_UnreliableOrderedChannel_Create, (NBN_ChannelDestructor)NBN_Channel_Destroy); - Endpoint_RegisterChannel(endpoint, NBN_CHANNEL_RESERVED_RELIABLE, (NBN_ChannelBuilder)NBN_ReliableOrderedChannel_Create, (NBN_ChannelDestructor)NBN_Channel_Destroy); - - // create message pools for the library messages - endpoint->msg_buffer_pools[NBN_CLIENT_CLOSED_MESSAGE_TYPE] = NBN_Allocator(sizeof(NBN_MemPool)); - endpoint->msg_buffer_pools[NBN_CLIENT_ACCEPTED_MESSAGE_TYPE] = NBN_Allocator(sizeof(NBN_MemPool)); - endpoint->msg_buffer_pools[NBN_CONNECTION_REQUEST_MESSAGE_TYPE] = NBN_Allocator(sizeof(NBN_MemPool)); - - // TODO: use macro for pool block sizes - MemPool_Init(endpoint->msg_buffer_pools[NBN_CLIENT_CLOSED_MESSAGE_TYPE], 4, - NBN_LIBRARY_MESSAGE_POOL_INITIAL_SIZE); - MemPool_Init(endpoint->msg_buffer_pools[NBN_CLIENT_ACCEPTED_MESSAGE_TYPE], - NBN_SERVER_DATA_MAX_SIZE + 4, NBN_LIBRARY_MESSAGE_POOL_INITIAL_SIZE); - MemPool_Init(endpoint->msg_buffer_pools[NBN_CONNECTION_REQUEST_MESSAGE_TYPE], - NBN_CONNECTION_DATA_MAX_SIZE + 4, NBN_LIBRARY_MESSAGE_POOL_INITIAL_SIZE); - - // create message pools for the user-defined messages - for (int i = 0; i < NBN_USER_MESSAGE_TYPE_RANGE + 1; i++) - { - NBN_UserMessageDef def = user_message_defs[i]; - - if (def.type >= 0) - { - // TODO: define initial count from config? - endpoint->msg_buffer_pools[i] = NBN_Allocator(sizeof(NBN_MemPool)); - MemPool_Init(endpoint->msg_buffer_pools[i], def.max_length, 32); - } - else - { - endpoint->msg_buffer_pools[i] = NULL; - } - } - - endpoint->outgoing_msg_pool = NBN_Allocator(sizeof(NBN_MemPool)); - MemPool_Init(endpoint->outgoing_msg_pool, sizeof(NBN_Message), 32); // TODO: macro for initial pool size - #ifdef NBN_DEBUG endpoint->OnMessageAddedToRecvQueue = NULL; #endif @@ -3105,71 +2262,44 @@ static void Endpoint_Init(NBN_Endpoint *endpoint, Endpoint_UpdateTime(endpoint); } -static void Endpoint_Deinit(NBN_Endpoint *endpoint) -{ +static void Endpoint_Deinit(NBN_Endpoint *endpoint) { (void)endpoint; #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) NBN_PacketSimulator_Stop(&endpoint->packet_simulator); #endif - - for (int i = 0; i < NBN_USER_MESSAGE_TYPE_RANGE + 1; i++) - { - if (endpoint->msg_buffer_pools[i]) - { - NBN_MemPool *pool = endpoint->msg_buffer_pools[i]; - - MemPool_Deinit(pool); - NBN_Deallocator(pool); - } - } - - MemPool_Deinit(endpoint->outgoing_msg_pool); - NBN_Deallocator(endpoint->outgoing_msg_pool); - - MemoryManager_Deinit(); } -static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_t id, int driver_id, void *driver_data) -{ +static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_t id, int driver_id, + void *driver_data) { NBN_Driver *driver = &nbn_drivers[driver_id]; NBN_Assert(driver->id >= 0); NBN_Connection *connection = NBN_Connection_Create(id, endpoint, driver, driver_data); - // TODO: don't, remove this builder thing, we only have unreliable and reliable channels - for (unsigned int chan_id = 0; chan_id < NBN_MAX_CHANNELS; chan_id++) - { - NBN_ChannelBuilder builder = endpoint->channel_builders[chan_id]; - - if (!builder) continue; - - NBN_ChannelDestructor destructor = endpoint->channel_destructors[chan_id]; - - NBN_Assert(destructor); - - NBN_Channel *channel = builder(); + connection->channel_count = 2 + endpoint->custom_reliable_channels; + connection->channels = NBN_Allocator(sizeof(NBN_Channel *) * connection->channel_count); - NBN_Assert(channel); + // create library channels + connection->channels[NBN_CHANNEL_RESERVED_UNRELIABLE] = + Channel_Create(NBN_CHANNEL_RESERVED_UNRELIABLE, NBN_CHANNEL_UNRELIABLE); + connection->channels[NBN_CHANNEL_RESERVED_RELIABLE] = + Channel_Create(NBN_CHANNEL_RESERVED_RELIABLE, NBN_CHANNEL_RELIABLE); - channel->id = chan_id; - channel->destructor = destructor; + // create custom user-defined reliable channels - connection->channels[chan_id] = channel; - - NBN_Connection_InitChannel(connection, channel); - } + for (uint8_t chan_id = 2; chan_id < connection->channel_count; chan_id++) { + connection->channels[chan_id] = Channel_Create(chan_id, NBN_CHANNEL_RELIABLE); + } return connection; } -static uint32_t Endpoint_BuildProtocolId(const char *protocol_name) -{ +static uint32_t Endpoint_BuildProtocolId(const char *protocol_name) { uint32_t protocol_id = 2166136261; - for (unsigned int i = 0; i < strlen(protocol_name); i++) - { + for (unsigned int i = 0; i < strlen(protocol_name); i++) { protocol_id *= 16777619; protocol_id ^= protocol_name[i]; } @@ -3177,20 +2307,7 @@ static uint32_t Endpoint_BuildProtocolId(const char *protocol_name) return protocol_id; } -static void Endpoint_RegisterChannel(NBN_Endpoint *endpoint, uint8_t id, NBN_ChannelBuilder builder, NBN_ChannelDestructor destructor) -{ - if (endpoint->channel_builders[id] || endpoint->channel_destructors[id]) - { - NBN_LogError("A channel with id %d already exists", id); - NBN_Abort(); - } - - endpoint->channel_builders[id] = builder; - endpoint->channel_destructors[id] = destructor; -} - -static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *packet, NBN_Connection *connection) -{ +static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *packet, NBN_Connection *connection) { (void)endpoint; NBN_LogTrace("Received packet %d (conn id: %d, ack: %d, messages count: %d)", packet->header.seq_number, @@ -3205,164 +2322,71 @@ static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *pa return 0; } -static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, uint8_t type, uint8_t channel_id) -{ - NBN_Message *message = (NBN_Message *)MemPool_Alloc(endpoint->outgoing_msg_pool); +static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, uint8_t type, uint8_t channel_id) { + NBN_Message *message = (NBN_Message *)NBN_Allocator(sizeof(NBN_Message)); NBN_Assert(message != NULL); - NBN_Assert(message->ref_count == 0); - - message->header = (NBN_MessageHeader){-1, 0, type, channel_id}; - message->sender = NULL; - message->type = NBN_OUTGOING_MESSAGE; - message->data = NULL; - message->ref_count = 0; - - endpoint->write_message_buffer_len = 0; - endpoint->message_writer.position = 0; - - NBN_MemPool *msg_buffer_pool = endpoint->msg_buffer_pools[type]; - - // message with no data have no buffer pool - if (msg_buffer_pool) - { - message->data = MemPool_Alloc(msg_buffer_pool); - endpoint->write_message_buffer_len = msg_buffer_pool->block_size; - } - - endpoint->write_message = message; -} - -static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Message *message) -{ - NBN_Assert(!connection->is_closed || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); - NBN_Assert(!connection->is_stale); - - NBN_Channel *channel = connection->channels[message->header.channel_id]; - - NBN_Assert(channel); - - if (message->header.length > NBN_PACKET_MAX_DATA_SIZE) - { - /*NBN_MessageChunk *chunks[NBN_CHANNEL_CHUNKS_BUFFER_SIZE]; - int chunk_count = Endpoint_SplitMessageIntoChunks(&message, outgoing_msg, channel, msg_serializer, message_size, chunks); - - NBN_Assert(chunk_count <= NBN_CHANNEL_CHUNKS_BUFFER_SIZE); - - if (chunk_count < 0) - { - NBN_LogError("Failed to split message into chunks"); - - return NBN_ERROR; - } - - NBN_Assert(outgoing_msg->ref_count == 0); - outgoing_msg->ref_count = chunk_count; - - NBN_Channel *channel = connection->channels[channel_id]; - - for (int i = 0; i < chunk_count; i++) - { - NBN_OutgoingMessage *chunk_outgoing_msg = Endpoint_CreateOutgoingMessage(endpoint, channel, NBN_MESSAGE_CHUNK_TYPE, chunks[i]); - - if (chunk_outgoing_msg == NULL) - return NBN_ERROR; - - if (Endpoint_EnqueueOutgoingMessage(endpoint, connection, chunk_outgoing_msg, channel_id) < 0) - { - NBN_LogError("Failed to enqueue message chunk"); - - return NBN_ERROR; - } - }*/ - - // TODO: fix chunks - NBN_Assert(false); - } - else - { - message->ref_count++; - - NBN_LogTrace("Enqueue message of type %d on channel %d", message->header.type, channel->id); - - if (!channel->AddOutgoingMessage(channel, message)) - { - NBN_LogError("Failed to enqueue outgoing message of type %d on channel %d", message->header.type, message->header.channel_id); - - return NBN_ERROR; - } - } - - return 0; -} + NBN_Assert(message->ref_count == 0); -// TODO -/*static int Endpoint_SplitMessageIntoChunks( - NBN_Message *message, - NBN_OutgoingMessage *outgoing_msg, - NBN_Channel *channel, - NBN_MessageSerializer msg_serializer, - unsigned int message_size, - NBN_MessageChunk **chunks) -{ + endpoint->active_out_msg_count++; - unsigned int chunk_count = ((message_size - 1) / NBN_MESSAGE_CHUNK_SIZE) + 1; + message->header = (NBN_MessageHeader){-1, 0, type, channel_id}; + message->sender = NULL; + message->type = NBN_OUTGOING_MESSAGE; + message->data = NULL; + message->ref_count = 0; - NBN_LogTrace("Split message into %d chunks (message size: %d)", chunk_count, message_size); + endpoint->message_writer.position = 0; + message->data = endpoint->scratch_write_buffer; - if (chunk_count > NBN_CHANNEL_CHUNKS_BUFFER_SIZE) - { - NBN_LogError("The maximum number of chunks is 255"); + NBN_Assert(message->data != NULL); - return NBN_ERROR; - } + endpoint->active_out_msg_buffer_count++; + endpoint->write_message = message; +} - if (message_size > channel->write_chunk_buffer_size) - NBN_Channel_ResizeWriteChunkBuffer(channel, message_size); +static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Message *message) { + NBN_Assert(!connection->is_closed || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); + NBN_Assert(!connection->is_stale); - NBN_WriteStream w_stream; + uint8_t *scratch_buffer = message->data; - NBN_WriteStream_Init(&w_stream, channel->write_chunk_buffer, message_size); + message->header.length = endpoint->message_writer.position; - if (NBN_Message_SerializeHeader(&message->header, (NBN_Stream *)&w_stream) < 0) - return NBN_ERROR; + NBN_Assert(message->header.length <= NBN_PACKET_MAX_DATA_SIZE); - if (NBN_Message_SerializeData(message, (NBN_Stream *)&w_stream, msg_serializer) < 0) - return NBN_ERROR; + if (message->header.length == 0) { + message->data = NULL; + } else { + message->data = NBN_Allocator(message->header.length); + } - for (unsigned int i = 0; i < chunk_count; i++) - { - NBN_MessageChunk *chunk = NBN_MessageChunk_Create(); + memcpy(message->data, scratch_buffer, message->header.length); - chunk->id = i; - chunk->total = chunk_count; - chunk->outgoing_msg = outgoing_msg; + NBN_Channel *channel = connection->channels[message->header.channel_id]; - unsigned int offset = i * NBN_MESSAGE_CHUNK_SIZE; - unsigned int chunk_size = MIN(NBN_MESSAGE_CHUNK_SIZE, message_size - offset); + NBN_Assert(channel); - NBN_Assert(chunk_size <= NBN_MESSAGE_CHUNK_SIZE); + message->ref_count++; - memcpy(chunk->data, channel->write_chunk_buffer + offset, chunk_size); + NBN_LogTrace("Enqueue message of type %d on channel %d", message->header.type, channel->id); - NBN_LogTrace("Enqueue chunk %d (size: %d, total: %d) for message %d of type %d", - chunk->id, chunk_size, chunk->total, message->header.id, message->header.type); + if (!Channel_AddOutgoingMessage(channel, message)) { + NBN_LogError("Failed to enqueue outgoing message of type %d on channel %d", message->header.type, + message->header.channel_id); - chunks[i] = chunk; + return NBN_ERROR; } - return chunk_count; return 0; -}*/ +} -static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) -{ +static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) { #ifdef NBNET_WINDOWS endpoint->time = GetTickCount64() / 1000.0; #else static struct timespec tp; - if (clock_gettime(CLOCK_MONOTONIC_RAW, &tp) < 0) - { + if (clock_gettime(CLOCK_MONOTONIC_RAW, &tp) < 0) { NBN_LogError("gettimeofday() failed"); NBN_Abort(); } @@ -3371,6 +2395,26 @@ static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) #endif // NBNET_WINDOWS } +static uint8_t *Endpoint_AllocateMessage(NBN_Endpoint *endpoint, uint8_t type, uint16_t *length) { + if (type >= NBN_MAX_MESSAGE_TYPES - (NBN_RESERVED_MESSAGE_TYPES - 1)) { + *length = NBN_RESERVED_MESSAGE_BUFFER_LEN; + + return NBN_Allocator(NBN_RESERVED_MESSAGE_BUFFER_LEN); + } + + uint8_t *data = endpoint->msg_allocator(type, length); + + return data; +} + +static void Endpoint_DeallocateMessage(NBN_Endpoint *endpoint, uint8_t type, uint8_t *data) { + if (type >= NBN_MAX_MESSAGE_TYPES - (NBN_RESERVED_MESSAGE_TYPES - 1)) { + NBN_Deallocator(data); + } + + endpoint->msg_deallocator(type, data); +} + #pragma endregion /* NBN_Endpoint */ #pragma region Network driver @@ -3379,8 +2423,7 @@ static void ClientDriver_OnPacketReceived(NBN_Packet *packet); static int ServerDriver_OnClientConnected(NBN_Connection *); static int ServerDriver_OnClientPacketReceived(NBN_Packet *); -void NBN_Driver_Register(int id, const char *name, NBN_DriverImplementation implementation) -{ +void NBN_Driver_Register(int id, const char *name, NBN_DriverImplementation implementation) { // driver id must be valid NBN_Assert(id >= 0 && id < NBN_MAX_DRIVERS); @@ -3398,10 +2441,8 @@ void NBN_Driver_Register(int id, const char *name, NBN_DriverImplementation impl nbn_driver_count++; } -int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data) -{ - switch (ev) - { +int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data) { + switch (ev) { case NBN_DRIVER_CLI_PACKET_RECEIVED: ClientDriver_OnPacketReceived((NBN_Packet *)data); break; @@ -3426,64 +2467,56 @@ static int GameClient_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); static int GameClient_HandleEvent(void); static int GameClient_HandleMessageReceivedEvent(void); -NBN_GameClient_Config NBN_GameClient_CreateConfig(const char *protocol_name, const char *host, uint16_t port) -{ - NBN_GameClient_Config config = {protocol_name, host, port}; - - for (int i = 0; i < NBN_USER_MESSAGE_TYPE_RANGE + 1; i++) - { - config.user_message_defs[i].type = -1; - } +NBN_GameClient_Config NBN_GameClient_CreateConfig(const char *protocol_name, const char *host, uint16_t port, + NBN_MessageAllocator msg_allocator, + NBN_MessageDeallocator msg_deallocator) { + NBN_GameClient_Config config = {.host = host, + .endpoint = {.protocol_name = protocol_name, + .port = port, + .custom_reliable_channels = 0, + .msg_allocator = msg_allocator, + .msg_deallocator = msg_deallocator}}; return config; } -void NBN_GameClient_RegisterMessageType(NBN_GameClient_Config *config, uint8_t type, unsigned int max_length) -{ - NBN_Assert(type >= 0 && type <= NBN_USER_MESSAGE_TYPE_RANGE); - // make sure we don't override an already defined message - NBN_Assert(config->user_message_defs[type].type < 0); +void NBN_GameClient_EnableCustomChannels(NBN_GameClient_Config *config, unsigned int count) { + NBN_Assert(count <= NBN_MAX_CUSTOM_CHANNELS); - config->user_message_defs[type] = (NBN_UserMessageDef){type, max_length}; + config->endpoint.custom_reliable_channels = count; } -NBN_Writer *NBN_GameClient_GetConnectionDataWriter(void) -{ - NBN_Writer_Init( - &nbn_game_client.client_data_writer, - nbn_game_client.client_data_buffer, - sizeof(nbn_game_client.server_data_buffer)); +NBN_Writer *NBN_GameClient_GetConnectionDataWriter(void) { + NBN_Writer_Init(&nbn_game_client.client_data_writer, nbn_game_client.client_data_buffer, + sizeof(nbn_game_client.server_data_buffer)); return &nbn_game_client.client_data_writer; } -int NBN_GameClient_Start(NBN_GameClient_Config config) -{ - if (nbn_driver_count < 1) - { +int NBN_GameClient_Start(NBN_GameClient_Config config) { + if (nbn_driver_count < 1) { NBN_LogError("At least one network driver has to be registered"); NBN_Abort(); } - const char *protocol_name = config.protocol_name; + const char *protocol_name = config.endpoint.protocol_name; const char *host = config.host; - uint16_t port = config.port; + uint16_t port = config.endpoint.port; uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - Endpoint_Init(&nbn_game_client.endpoint, config.user_message_defs, protocol_id, false); + Endpoint_Init(&nbn_game_client.endpoint, config.endpoint, protocol_id, false); nbn_game_client.server_connection = NULL; nbn_game_client.is_connected = false; nbn_game_client.closed_code = -1; - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) - { + for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) { NBN_Driver *driver = &nbn_drivers[i]; - if (driver->id < 0) continue; + if (driver->id < 0) + continue; - if (driver->impl.cli_start(protocol_id, host, port) < 0) - { + if (driver->impl.cli_start(protocol_id, host, port) < 0) { NBN_LogError("Failed to start driver %s", driver->name); return NBN_ERROR; } @@ -3494,15 +2527,12 @@ int NBN_GameClient_Start(NBN_GameClient_Config config) NBN_GameClient_CreateReliableMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE); NBN_Writer *writer = NBN_GameClient_GetMessageWriter(); - if (connection_data_len > 0) - { + if (connection_data_len > 0) { NBN_Assert(connection_data_len <= sizeof(nbn_game_client.client_data_buffer)); NBN_Writer_WriteUInt32(writer, connection_data_len); NBN_Writer_WriteBytes(writer, nbn_game_client.client_data_buffer, connection_data_len); - } - else - { + } else { NBN_Writer_WriteUInt32(writer, 0); } @@ -3514,26 +2544,22 @@ int NBN_GameClient_Start(NBN_GameClient_Config config) return 0; } -void NBN_GameClient_Stop(void) -{ +void NBN_GameClient_Stop(void) { // Poll remaining events to clear the event queue - while (NBN_GameClient_Poll() != NBN_NO_EVENT) {} + while (NBN_GameClient_Poll() != NBN_NO_EVENT) { + } - if (nbn_game_client.server_connection) - { - if (!nbn_game_client.server_connection->is_closed && !nbn_game_client.server_connection->is_stale) - { + if (nbn_game_client.server_connection) { + if (!nbn_game_client.server_connection->is_closed && !nbn_game_client.server_connection->is_stale) { NBN_LogInfo("Disconnecting..."); NBN_GameClient_CreateReliableMessage(NBN_DISCONNECTION_MESSAGE_TYPE); - if (NBN_GameClient_SendMessage() < 0) - { + if (NBN_GameClient_SendMessage() < 0) { NBN_LogError("Failed to send disconnection message"); } - if (NBN_GameClient_SendPackets() < 0) - { + if (NBN_GameClient_SendPackets() < 0) { NBN_LogError("Failed to send packets"); } @@ -3544,15 +2570,15 @@ void NBN_GameClient_Stop(void) NBN_Connection_Destroy(&nbn_game_client.endpoint, nbn_game_client.server_connection); nbn_game_client.server_connection = NULL; - } + } NBN_LogInfo("Stopping all drivers..."); - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) - { + for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) { NBN_Driver *driver = &nbn_drivers[i]; - if (driver->id < 0) continue; + if (driver->id < 0) + continue; driver->impl.cli_stop(); } @@ -3566,26 +2592,28 @@ void NBN_GameClient_Stop(void) NBN_LogInfo("Stopped"); } -NBN_Reader *NBN_GameClient_GetServerDataReader(void) -{ - NBN_Reader_Init(&nbn_game_client.server_data_reader, - nbn_game_client.server_data_buffer, - nbn_game_client.server_data_len); +NBN_Reader *NBN_GameClient_GetServerDataReader(void) { + NBN_Reader_Init(&nbn_game_client.server_data_reader, nbn_game_client.server_data_buffer, + nbn_game_client.server_data_len); return &nbn_game_client.server_data_reader; } -int NBN_GameClient_Poll(void) -{ +int NBN_GameClient_Poll(void) { Endpoint_UpdateTime(&nbn_game_client.endpoint); + NBN_Endpoint *endpoint = &nbn_game_client.endpoint; + + if (endpoint->last_message_need_free) { + Endpoint_FreeMessage(endpoint, &endpoint->last_received_message); + endpoint->last_message_need_free = false; + } + if (nbn_game_client.server_connection->is_stale) return NBN_NO_EVENT; - if (NBN_EventQueue_IsEmpty(&nbn_game_client.endpoint.event_queue)) - { - if (NBN_Connection_CheckIfStale(nbn_game_client.server_connection, nbn_game_client.endpoint.time)) - { + if (NBN_EventQueue_IsEmpty(&endpoint->event_queue)) { + if (NBN_Connection_CheckIfStale(nbn_game_client.server_connection, nbn_game_client.endpoint.time)) { nbn_game_client.server_connection->is_stale = true; nbn_game_client.is_connected = false; @@ -3596,95 +2624,79 @@ int NBN_GameClient_Poll(void) e.type = NBN_DISCONNECTED; e.data.connection = (NBN_Connection *)NULL; - if (!NBN_EventQueue_Enqueue(&nbn_game_client.endpoint.event_queue, e)) + if (!NBN_EventQueue_Enqueue(&endpoint->event_queue, e)) return NBN_ERROR; - } - else - { - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) - { + } else { + for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) { NBN_Driver *driver = &nbn_drivers[i]; - if (driver->id < 0) continue; + if (driver->id < 0) + continue; - if (driver->impl.cli_recv_packets() < 0) - { + if (driver->impl.cli_recv_packets() < 0) { NBN_LogError("Failed to read packets from driver %s", driver->name); return NBN_ERROR; } } - for (unsigned int i = 0; i < NBN_MAX_CHANNELS; i++) - { - NBN_Channel *channel = nbn_game_client.server_connection->channels[i]; + NBN_Connection *server_conn = nbn_game_client.server_connection; - if (channel) - { - NBN_Message *msg; + for (unsigned int i = 0; i < server_conn->channel_count; i++) { + NBN_Channel *channel = server_conn->channels[i]; - while ((msg = channel->GetNextRecvedMessage(channel)) != NULL) - { - NBN_LogTrace("Got message %d of type %d from channel %d", msg->header.id, msg->header.type, channel->id); + NBN_Message *msg; - if (GameClient_ProcessReceivedMessage(msg, nbn_game_client.server_connection) < 0) - { - NBN_LogError("Failed to process received message"); + while ((msg = Channel_GetNextRecvedMessage(channel)) != NULL) { + NBN_LogTrace("Got message %d of type %d from channel %d", msg->header.id, msg->header.type, + channel->id); - return NBN_ERROR; - } + if (GameClient_ProcessReceivedMessage(msg, server_conn) < 0) { + NBN_LogError("Failed to process received message"); + + return NBN_ERROR; } } } - Connection_UpdateAverageDownloadBandwidth(nbn_game_client.server_connection, nbn_game_client.endpoint.time); + Connection_UpdateAverageDownloadBandwidth(server_conn, nbn_game_client.endpoint.time); - nbn_game_client.server_connection->last_read_packets_time = nbn_game_client.endpoint.time; + server_conn->last_read_packets_time = nbn_game_client.endpoint.time; } } - bool ret = NBN_EventQueue_Dequeue(&nbn_game_client.endpoint.event_queue, &nbn_game_client.last_event); + bool ret = NBN_EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_client.last_event); return ret ? GameClient_HandleEvent() : NBN_NO_EVENT; } -int NBN_GameClient_SendPackets(void) -{ - return NBN_Connection_FlushChannels( - &nbn_game_client.endpoint, - nbn_game_client.server_connection, - nbn_game_client.endpoint.protocol_id, - nbn_game_client.endpoint.time); +int NBN_GameClient_SendPackets(void) { + return NBN_Connection_FlushChannels(&nbn_game_client.endpoint, nbn_game_client.server_connection, + nbn_game_client.endpoint.protocol_id, nbn_game_client.endpoint.time); } -void NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id) -{ +void NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; NBN_Assert(endpoint->write_message == NULL); Endpoint_CreateOutgoingMessage(endpoint, type, channel_id); } -void NBN_GameClient_CreateUnreliableMessage(uint8_t type) -{ +void NBN_GameClient_CreateUnreliableMessage(uint8_t type) { NBN_GameClient_CreateMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE); } -void NBN_GameClient_CreateReliableMessage(uint8_t type) -{ +void NBN_GameClient_CreateReliableMessage(uint8_t type) { NBN_GameClient_CreateMessage(type, NBN_CHANNEL_RESERVED_RELIABLE); } -int NBN_GameClient_SendMessage(void) -{ +int NBN_GameClient_SendMessage(void) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; NBN_Message *message = endpoint->write_message; + NBN_Assert(message != NULL); NBN_Assert(message->ref_count == 0); - message->header.length = endpoint->message_writer.position; - - if (Endpoint_EnqueueOutgoingMessage(endpoint, nbn_game_client.server_connection, message) < 0) - { + if (Endpoint_EnqueueOutgoingMessage(endpoint, nbn_game_client.server_connection, message) < 0) { NBN_LogError("Failed to create outgoing message"); return NBN_ERROR; @@ -3695,8 +2707,7 @@ int NBN_GameClient_SendMessage(void) return 0; } -NBN_Writer *NBN_GameClient_GetMessageWriter(void) -{ +NBN_Writer *NBN_GameClient_GetMessageWriter(void) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; NBN_Assert(endpoint->write_message != NULL); @@ -3704,14 +2715,12 @@ NBN_Writer *NBN_GameClient_GetMessageWriter(void) NBN_Writer *writer = &endpoint->message_writer; - NBN_Writer_Init(writer, endpoint->write_message->data, - endpoint->write_message_buffer_len); + NBN_Writer_Init(writer, endpoint->write_message->data, endpoint->write_message_buffer_len); return writer; } -NBN_Reader *NBN_GameClient_GetMessageReader(void) -{ +NBN_Reader *NBN_GameClient_GetMessageReader(void) { NBN_Assert(nbn_game_client.last_event.type == NBN_MESSAGE_RECEIVED); NBN_MessageInfo msg_info = nbn_game_client.last_event.data.message_info; @@ -3724,8 +2733,17 @@ NBN_Reader *NBN_GameClient_GetMessageReader(void) return reader; } -NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *driver_data) -{ +int NBN_GameClient_GetActiveOutgoingMessageCount(void) { return nbn_game_client.endpoint.active_out_msg_count; } + +int NBN_GameClient_GetActiveOutgoingMessageBufferCount(void) { + return nbn_game_client.endpoint.active_out_msg_buffer_count; +} + +int NBN_GameClient_GetActiveIncomingMessageBufferCount(void) { + return nbn_game_client.endpoint.active_inc_msg_buffer_count; +} + +NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *driver_data) { NBN_Connection *server_connection = Endpoint_CreateConnection(&nbn_game_client.endpoint, 0, driver_id, driver_data); #ifdef NBN_DEBUG @@ -3737,34 +2755,22 @@ NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *drive return server_connection; } -NBN_MessageInfo NBN_GameClient_GetMessageInfo(void) -{ +NBN_MessageInfo NBN_GameClient_GetMessageInfo(void) { NBN_Assert(nbn_game_client.last_event.type == NBN_MESSAGE_RECEIVED); return nbn_game_client.last_event.data.message_info; } -NBN_ConnectionStats NBN_GameClient_GetStats(void) -{ - return nbn_game_client.server_connection->stats; -} +NBN_ConnectionStats NBN_GameClient_GetStats(void) { return nbn_game_client.server_connection->stats; } -int NBN_GameClient_GetServerCloseCode(void) -{ - return nbn_game_client.closed_code; -} +int NBN_GameClient_GetServerCloseCode(void) { return nbn_game_client.closed_code; } -bool NBN_GameClient_IsConnected(void) -{ - return nbn_game_client.is_connected; -} +bool NBN_GameClient_IsConnected(void) { return nbn_game_client.is_connected; } #ifdef NBN_DEBUG -void NBN_GameClient_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, void *cb) -{ - switch (cb_type) - { +void NBN_GameClient_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, void *cb) { + switch (cb_type) { case NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE: nbn_game_client.endpoint.OnMessageAddedToRecvQueue = (void (*)(NBN_Connection *, NBN_Message *))cb; break; @@ -3773,41 +2779,17 @@ void NBN_GameClient_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, #endif /* NBN_DEBUG */ -static int GameClient_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *server_connection) -{ +static int GameClient_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *server_connection) { NBN_Assert(nbn_game_client.server_connection == server_connection); NBN_Event ev; ev.type = NBN_MESSAGE_RECEIVED; - // if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) - if (false) // TODO: - { - /*NBN_Channel *channel = server_connection->channels[message->header.channel_id];*/ - /**/ - /*if (!NBN_Channel_AddChunk(channel, message))*/ - /* return 0;*/ - /**/ - /*NBN_Message complete_message;*/ - /**/ - /*if (NBN_Channel_ReconstructMessageFromChunks(channel, server_connection, &complete_message) < 0)*/ - /*{*/ - /* NBN_LogError("Failed to reconstruct message from chunks");*/ - /**/ - /* return NBN_ERROR;*/ - /*}*/ - /**/ - /*NBN_MessageInfo msg_info = {complete_message.header.type, complete_message.header.channel_id, complete_message.data, 0};*/ - /**/ - /*ev.data.message_info = msg_info;*/ - } - else - { - NBN_MessageInfo msg_info = {message->header.type, message->header.channel_id, message->data, message->header.length, 0}; + NBN_MessageInfo msg_info = {message->header.type, message->header.channel_id, message->data, message->header.length, + 0}; - ev.data.message_info = msg_info; - } + ev.data.message_info = msg_info; if (!NBN_EventQueue_Enqueue(&nbn_game_client.endpoint.event_queue, ev)) return NBN_ERROR; @@ -3815,10 +2797,8 @@ static int GameClient_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio return 0; } -static int GameClient_HandleEvent(void) -{ - switch (nbn_game_client.last_event.type) - { +static int GameClient_HandleEvent(void) { + switch (nbn_game_client.last_event.type) { case NBN_MESSAGE_RECEIVED: return GameClient_HandleMessageReceivedEvent(); @@ -3827,30 +2807,32 @@ static int GameClient_HandleEvent(void) } } -static int GameClient_HandleMessageReceivedEvent(void) -{ +static int GameClient_HandleMessageReceivedEvent(void) { NBN_MessageInfo message_info = nbn_game_client.last_event.data.message_info; + NBN_Endpoint *endpoint = &nbn_game_client.endpoint; + NBN_Message *last_received_message = &endpoint->last_received_message; + + last_received_message->data = message_info.data; + last_received_message->type = NBN_INCOMING_MESSAGE; + last_received_message->header.length = message_info.length; + + endpoint->last_message_need_free = true; int ret = NBN_NO_EVENT; - if (message_info.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE) - { + if (message_info.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE) { nbn_game_client.is_connected = false; NBN_Reader *reader = NBN_GameClient_GetMessageReader(); - if (NBN_Reader_ReadInt32(reader, &nbn_game_client.closed_code) < 0) - { + if (NBN_Reader_ReadInt32(reader, &nbn_game_client.closed_code) < 0) { NBN_LogError("Failed to read code from client closed message"); return NBN_ERROR; } ret = NBN_DISCONNECTED; - } - else if (message_info.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE) - { - if (message_info.length < 4) - { + } else if (message_info.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE) { + if (message_info.length < 4) { NBN_LogError("Accept message invalid length"); return NBN_ERROR; @@ -3859,24 +2841,20 @@ static int GameClient_HandleMessageReceivedEvent(void) NBN_Reader *reader = NBN_GameClient_GetMessageReader(); unsigned int data_length; - if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) - { + if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) { NBN_LogError("Failed to read client data length"); return NBN_ERROR; } - if (data_length > 0) - { - if (data_length > sizeof(nbn_game_client.server_data_buffer)) - { + if (data_length > 0) { + if (data_length > sizeof(nbn_game_client.server_data_buffer)) { NBN_LogError("Received an invalid client accepted message"); return NBN_ERROR; } - if (NBN_Reader_ReadBytes(reader, nbn_game_client.server_data_buffer, data_length) < 0) - { + if (NBN_Reader_ReadBytes(reader, nbn_game_client.server_data_buffer, data_length) < 0) { NBN_LogError("Failed to read server data"); return NBN_ERROR; @@ -3886,9 +2864,7 @@ static int GameClient_HandleMessageReceivedEvent(void) nbn_game_client.server_data_len = data_length; nbn_game_client.is_connected = true; ret = NBN_CONNECTED; - } - else - { + } else { ret = NBN_MESSAGE_RECEIVED; } @@ -3899,11 +2875,9 @@ static int GameClient_HandleMessageReceivedEvent(void) #pragma region Game client driver -static void ClientDriver_OnPacketReceived(NBN_Packet *packet) -{ +static void ClientDriver_OnPacketReceived(NBN_Packet *packet) { // packets from server should always be valid - if (Endpoint_ProcessReceivedPacket(&nbn_game_client.endpoint, packet, nbn_game_client.server_connection) < 0) - { + if (Endpoint_ProcessReceivedPacket(&nbn_game_client.endpoint, packet, nbn_game_client.server_connection) < 0) { NBN_LogError("Received invalid packet from server"); NBN_Abort(); } @@ -3926,58 +2900,50 @@ static void GameServer_RemoveClosedClientConnections(void); static int GameServer_HandleEvent(void); static int GameServer_HandleMessageReceivedEvent(void); -NBN_GameServer_Config NBN_GameServer_CreateConfig(const char *protocol_name, uint16_t port) -{ - NBN_GameServer_Config config = {protocol_name, port}; - - for (int i = 0; i < NBN_USER_MESSAGE_TYPE_RANGE + 1; i++) - { - config.user_message_defs[i].type = -1; - } +NBN_GameServer_Config NBN_GameServer_CreateConfig(const char *protocol_name, uint16_t port, + NBN_MessageAllocator msg_allocator, + NBN_MessageDeallocator msg_deallocator) { + NBN_GameServer_Config config = {.endpoint = {.protocol_name = protocol_name, + .port = port, + .custom_reliable_channels = 0, + .msg_allocator = msg_allocator, + .msg_deallocator = msg_deallocator}}; return config; } -void NBN_GameServer_RegisterMessageType(NBN_GameServer_Config *config, uint8_t type, unsigned int max_length) -{ - // TODO: maybe find a way to remove code duplication with NBN_GameClient_RegisterMessageType - NBN_Assert(type >= 0 && type <= NBN_USER_MESSAGE_TYPE_RANGE); - // make sure we don't override an already defined message - NBN_Assert(config->user_message_defs[type].type < 0); +void NBN_GameServer_EnableCustomChannels(NBN_GameServer_Config *config, unsigned int count) { + NBN_Assert(count <= NBN_MAX_CUSTOM_CHANNELS); - config->user_message_defs[type] = (NBN_UserMessageDef){type, max_length}; + config->endpoint.custom_reliable_channels = count; } -int NBN_GameServer_Start(NBN_GameServer_Config config) -{ - if (nbn_driver_count < 1) - { +int NBN_GameServer_Start(NBN_GameServer_Config config) { + if (nbn_driver_count < 1) { NBN_LogError("At least one network driver has to be registered"); NBN_Abort(); } - const char *protocol_name = config.protocol_name; - uint16_t port = config.port; + const char *protocol_name = config.endpoint.protocol_name; + uint16_t port = config.endpoint.port; uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - Endpoint_Init(&nbn_game_server.endpoint, config.user_message_defs, protocol_id, true); + Endpoint_Init(&nbn_game_server.endpoint, config.endpoint, protocol_id, true); - if ((nbn_game_server.clients = NBN_ConnectionVector_Create()) == NULL) - { + if ((nbn_game_server.clients = NBN_ConnectionVector_Create()) == NULL) { NBN_LogError("Failed to create connections vector"); NBN_Abort(); } nbn_game_server.closed_clients_head = NULL; - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) - { + for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) { NBN_Driver *driver = &nbn_drivers[i]; - if (driver->id < 0) continue; + if (driver->id < 0) + continue; - if (driver->impl.serv_start(protocol_id, port) < 0) - { + if (driver->impl.serv_start(protocol_id, port) < 0) { NBN_LogError("Failed to start driver %s", driver->name); return NBN_ERROR; } @@ -3988,23 +2954,22 @@ int NBN_GameServer_Start(NBN_GameServer_Config config) return 0; } -void NBN_GameServer_Stop(void) -{ +void NBN_GameServer_Stop(void) { // Poll remaning events to clear the event queue - while (NBN_GameServer_Poll() != NBN_NO_EVENT) {} + while (NBN_GameServer_Poll() != NBN_NO_EVENT) { + } - for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) - { + for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { NBN_Connection_Destroy(&nbn_game_server.endpoint, nbn_game_server.clients->connections[i]); } NBN_ConnectionVector_Destroy(nbn_game_server.clients); - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) - { + for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) { NBN_Driver *driver = &nbn_drivers[i]; - if (driver->id < 0) continue; + if (driver->id < 0) + continue; driver->impl.serv_stop(); } @@ -4012,8 +2977,7 @@ void NBN_GameServer_Stop(void) // Free closed clients list NBN_ConnectionListNode *current = nbn_game_server.closed_clients_head; - while (current) - { + while (current) { NBN_ConnectionListNode *next = current->next; NBN_Deallocator(current); @@ -4027,23 +2991,27 @@ void NBN_GameServer_Stop(void) NBN_LogInfo("Stopped"); } -int NBN_GameServer_Poll(void) -{ +int NBN_GameServer_Poll(void) { Endpoint_UpdateTime(&nbn_game_server.endpoint); - if (NBN_EventQueue_IsEmpty(&nbn_game_server.endpoint.event_queue)) - { + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + + if (endpoint->last_message_need_free) { + Endpoint_FreeMessage(endpoint, &endpoint->last_received_message); + endpoint->last_message_need_free = false; + } + + if (NBN_EventQueue_IsEmpty(&endpoint->event_queue)) { if (GameServer_CloseStaleClientConnections() < 0) return NBN_ERROR; - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) - { + for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) { NBN_Driver *driver = &nbn_drivers[i]; - if (driver->id < 0) continue; + if (driver->id < 0) + continue; - if (driver->impl.serv_recv_packets() < 0) - { + if (driver->impl.serv_recv_packets() < 0) { NBN_LogError("Failed to read packets from driver %s", driver->name); return NBN_ERROR; } @@ -4051,22 +3019,17 @@ int NBN_GameServer_Poll(void) nbn_game_server.stats.download_bandwidth = 0; - for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) - { + for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { NBN_Connection *client = nbn_game_server.clients->connections[i]; - for (unsigned int i = 0; i < NBN_MAX_CHANNELS; i++) - { + for (unsigned int i = 0; i < client->channel_count; i++) { NBN_Channel *channel = client->channels[i]; - if (channel) - { + if (channel) { NBN_Message *msg; - while ((msg = channel->GetNextRecvedMessage(channel)) != NULL) - { - if (GameServer_ProcessReceivedMessage(msg, client) < 0) - { + while ((msg = Channel_GetNextRecvedMessage(channel)) != NULL) { + if (GameServer_ProcessReceivedMessage(msg, client) < 0) { NBN_LogError("Failed to process received message"); return NBN_ERROR; @@ -4076,45 +3039,38 @@ int NBN_GameServer_Poll(void) } if (!client->is_closed) - Connection_UpdateAverageDownloadBandwidth(client, nbn_game_server.endpoint.time); + Connection_UpdateAverageDownloadBandwidth(client, endpoint->time); nbn_game_server.stats.download_bandwidth += client->stats.download_bandwidth; - client->last_read_packets_time = nbn_game_server.endpoint.time; + client->last_read_packets_time = endpoint->time; } GameServer_RemoveClosedClientConnections(); } - while (NBN_EventQueue_Dequeue(&nbn_game_server.endpoint.event_queue, &nbn_game_server.last_event)) - { + while (NBN_EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_server.last_event)) { int ev = GameServer_HandleEvent(); - if (ev != NBN_SKIP_EVENT) return ev; + if (ev != NBN_SKIP_EVENT) + return ev; } return NBN_NO_EVENT; } -int NBN_GameServer_SendPackets(void) -{ +int NBN_GameServer_SendPackets(void) { nbn_game_server.stats.upload_bandwidth = 0; GameServer_RemoveClosedClientConnections(); - for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) - { + for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { NBN_Connection *client = nbn_game_server.clients->connections[i]; NBN_Assert(!(client->is_closed && client->is_stale)); - if ( - !client->is_stale && - NBN_Connection_FlushChannels( - &nbn_game_server.endpoint, - client, - nbn_game_server.endpoint.protocol_id, - nbn_game_server.endpoint.time) < 0 - ) { + if (!client->is_stale && + NBN_Connection_FlushChannels(&nbn_game_server.endpoint, client, nbn_game_server.endpoint.protocol_id, + nbn_game_server.endpoint.time) < 0) { return NBN_ERROR; } @@ -4124,8 +3080,7 @@ int NBN_GameServer_SendPackets(void) return 0; } -NBN_Connection *NBN_GameServer_CreateClientConnection(int driver_id, void *driver_data, uint32_t conn_id) -{ +NBN_Connection *NBN_GameServer_CreateClientConnection(int driver_id, void *driver_data, uint32_t conn_id) { NBN_Assert(conn_id > 0); // Connection IDs start at 1 NBN_Connection *client = Endpoint_CreateConnection(&nbn_game_server.endpoint, conn_id, driver_id, driver_data); @@ -4137,60 +3092,46 @@ NBN_Connection *NBN_GameServer_CreateClientConnection(int driver_id, void *drive return client; } -int NBN_GameServer_CloseClientWithCode(NBN_Connection *conn, int code) -{ +int NBN_GameServer_CloseClientWithCode(NBN_Connection *conn, int code) { return GameServer_CloseClientWithCode(conn, code, false); } -int NBN_GameServer_CloseClient(NBN_Connection *conn) -{ - return GameServer_CloseClientWithCode(conn, -1, false); -} +int NBN_GameServer_CloseClient(NBN_Connection *conn) { return GameServer_CloseClientWithCode(conn, -1, false); } -void NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id) -{ +void NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; Endpoint_CreateOutgoingMessage(endpoint, type, channel_id); } -void NBN_GameServer_CreateUnreliableMessage(uint8_t type) -{ +void NBN_GameServer_CreateUnreliableMessage(uint8_t type) { NBN_GameServer_CreateMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE); } -void NBN_GameServer_CreateReliableMessage(uint8_t type) -{ +void NBN_GameServer_CreateReliableMessage(uint8_t type) { NBN_GameServer_CreateMessage(type, NBN_CHANNEL_RESERVED_RELIABLE); } -int NBN_GameServer_SendMessageTo(NBN_Connection *conn) -{ +int NBN_GameServer_SendMessageTo(NBN_Connection *conn) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; NBN_Message *message = endpoint->write_message; NBN_Assert(message != NULL); - message->header.length = endpoint->message_writer.position; - return GameServer_SendMessageTo(conn, message); } -int NBN_GameServer_BroadcastMessage(void) -{ +int NBN_GameServer_BroadcastMessage(void) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; NBN_Message *message = endpoint->write_message; NBN_Assert(message != NULL); - message->header.length = endpoint->message_writer.position; - - for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) - { + for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { NBN_Connection *conn = nbn_game_server.clients->connections[i]; - if (!conn->is_accepted || conn->is_closed) continue; + if (!conn->is_accepted || conn->is_closed) + continue; - if (GameServer_SendMessageTo(conn, message) < 0) - { + if (GameServer_SendMessageTo(conn, message) < 0) { NBN_LogError("Failed to send message to client %d when broadcasting", conn->id); return -1; } @@ -4199,8 +3140,7 @@ int NBN_GameServer_BroadcastMessage(void) return 0; } -NBN_Writer *NBN_GameServer_GetMessageWriter(void) -{ +NBN_Writer *NBN_GameServer_GetMessageWriter(void) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; NBN_Assert(endpoint->write_message != NULL); @@ -4208,14 +3148,12 @@ NBN_Writer *NBN_GameServer_GetMessageWriter(void) NBN_Writer *writer = &endpoint->message_writer; - NBN_Writer_Init(writer, endpoint->write_message->data, - endpoint->write_message_buffer_len); + NBN_Writer_Init(writer, endpoint->write_message->data, endpoint->write_message_buffer_len); return writer; } -NBN_Reader *NBN_GameServer_GetMessageReader(void) -{ +NBN_Reader *NBN_GameServer_GetMessageReader(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); NBN_MessageInfo msg_info = nbn_game_server.last_event.data.message_info; @@ -4228,21 +3166,17 @@ NBN_Reader *NBN_GameServer_GetMessageReader(void) return reader; } -NBN_Writer *NBN_GameServer_GetConnectionDataWriter(void) -{ +NBN_Writer *NBN_GameServer_GetConnectionDataWriter(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); - NBN_Writer_Init( - &nbn_game_server.server_data_writer, - nbn_game_server.server_data_buffer, - sizeof(nbn_game_server.server_data_buffer)); + NBN_Writer_Init(&nbn_game_server.server_data_writer, nbn_game_server.server_data_buffer, + sizeof(nbn_game_server.server_data_buffer)); return &nbn_game_server.server_data_writer; } -int NBN_GameServer_AcceptIncomingConnection(void) -{ +int NBN_GameServer_AcceptIncomingConnection(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); @@ -4252,15 +3186,12 @@ int NBN_GameServer_AcceptIncomingConnection(void) NBN_GameServer_CreateReliableMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); NBN_Writer *writer = NBN_GameServer_GetMessageWriter(); - if (data_length > 0) - { + if (data_length > 0) { NBN_Assert(data_length <= NBN_SERVER_DATA_MAX_SIZE); NBN_Writer_WriteUInt32(writer, data_length); NBN_Writer_WriteBytes(writer, nbn_game_server.server_data_buffer, data_length); - } - else - { + } else { NBN_Writer_WriteUInt32(writer, 0); } @@ -4274,8 +3205,7 @@ int NBN_GameServer_AcceptIncomingConnection(void) return 0; } -int NBN_GameServer_RejectIncomingConnectionWithCode(int code) -{ +int NBN_GameServer_RejectIncomingConnectionWithCode(int code) { NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); @@ -4285,55 +3215,52 @@ int NBN_GameServer_RejectIncomingConnectionWithCode(int code) return GameServer_CloseClientWithCode(conn, code, false); } -int NBN_GameServer_RejectIncomingConnection(void) -{ - return NBN_GameServer_RejectIncomingConnectionWithCode(-1); -} +int NBN_GameServer_RejectIncomingConnection(void) { return NBN_GameServer_RejectIncomingConnectionWithCode(-1); } -NBN_Connection *NBN_GameServer_GetIncomingConnection(void) -{ +NBN_Connection *NBN_GameServer_GetIncomingConnection(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); return nbn_game_server.last_event.data.connection; } -NBN_Reader *NBN_GameServer_GetConnectionDataReader(void) -{ +NBN_Reader *NBN_GameServer_GetConnectionDataReader(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); - NBN_Reader_Init(&nbn_game_server.client_data_reader, - nbn_game_server.client_data_buffer, - nbn_game_server.client_data_len); + NBN_Reader_Init(&nbn_game_server.client_data_reader, nbn_game_server.client_data_buffer, + nbn_game_server.client_data_len); return &nbn_game_server.client_data_reader; } -NBN_Connection *NBN_GameServer_GetDisconnectedClient(void) -{ +NBN_DisconnectionInfo NBN_GameServer_GetDisconnectionInfo(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_DISCONNECTED); - return nbn_game_server.last_event.data.connection; + return nbn_game_server.last_event.data.disconnection; } -NBN_MessageInfo NBN_GameServer_GetMessageInfo(void) -{ +NBN_MessageInfo NBN_GameServer_GetMessageInfo(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); return nbn_game_server.last_event.data.message_info; } -NBN_GameServerStats NBN_GameServer_GetStats(void) -{ - return nbn_game_server.stats; +NBN_GameServerStats NBN_GameServer_GetStats(void) { return nbn_game_server.stats; } + +int NBN_GameServer_GetActiveOutgoingMessageCount(void) { return nbn_game_server.endpoint.active_out_msg_count; } + +int NBN_GameServer_GetActiveOutgoingMessageBufferCount(void) { + return nbn_game_server.endpoint.active_out_msg_buffer_count; +} + +int NBN_GameServer_GetActiveIncomingMessageBufferCount(void) { + return nbn_game_server.endpoint.active_inc_msg_buffer_count; } #ifdef NBN_DEBUG -void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, void *cb) -{ - switch (cb_type) - { +void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, void *cb) { + switch (cb_type) { case NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE: nbn_game_server.endpoint.OnMessageAddedToRecvQueue = (void (*)(NBN_Connection *, NBN_Message *))cb; break; @@ -4342,22 +3269,19 @@ void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, #endif /* NBN_DEBUG */ -static int GameServer_SendMessageTo(NBN_Connection *client, NBN_Message *message) -{ +static int GameServer_SendMessageTo(NBN_Connection *client, NBN_Message *message) { NBN_Assert(message != NULL); - message->header.length = nbn_game_server.endpoint.message_writer.position; + /* Only NBN_CLIENT_ACCEPTED_MESSAGE_TYPE and NBN_CLIENT_CLOSED_MESSAGE_TYPE messages can be sent to an + * unaccapted client */ + NBN_Assert(client->is_accepted || message->header.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE || + message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); - /* Only NBN_CLIENT_ACCEPTED_MESSAGE_TYPE and NBN_CLIENT_CLOSED_MESSAGE_TYPE messages can be sent to an unaccapted client */ - NBN_Assert(client->is_accepted || message->header.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); - - if (Endpoint_EnqueueOutgoingMessage(&nbn_game_server.endpoint, client, message) < 0) - { + if (Endpoint_EnqueueOutgoingMessage(&nbn_game_server.endpoint, client, message) < 0) { NBN_LogError("Failed to create outgoing message for client %d", client->id); /* Do not close the client if we failed to send the close client message to avoid infinite loops */ - if (message->header.type != NBN_CLIENT_CLOSED_MESSAGE_TYPE) - { + if (message->header.type != NBN_CLIENT_CLOSED_MESSAGE_TYPE) { GameServer_CloseClientWithCode(client, -1, false); return NBN_ERROR; @@ -4367,10 +3291,8 @@ static int GameServer_SendMessageTo(NBN_Connection *client, NBN_Message *message return 0; } -static int GameServer_AddClient(NBN_Connection *client) -{ - if (nbn_game_server.clients->count >= NBN_MAX_CLIENTS) - { +static int GameServer_AddClient(NBN_Connection *client) { + if (nbn_game_server.clients->count >= NBN_MAX_CLIENTS) { NBN_LogError("Cannot accept new client: too many clients"); return NBN_ERROR; @@ -4382,24 +3304,20 @@ static int GameServer_AddClient(NBN_Connection *client) return 0; } -static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection) -{ - if (!client->is_closed && client->is_accepted) - { - if (!disconnection) - { +static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection) { + if (!client->is_closed && client->is_accepted) { + if (!disconnection) { NBN_Event e; e.type = NBN_CLIENT_DISCONNECTED; - e.data.connection = client; + e.data.disconnection = (NBN_DisconnectionInfo){client->id, client->user_data}; if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, e)) return NBN_ERROR; } } - if (client->is_stale) - { + if (client->is_stale) { NBN_LogDebug("Closing stale connection %d", client->id); GameServer_AddClientToClosedList(client); @@ -4413,8 +3331,7 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool GameServer_AddClientToClosedList(client); client->is_closed = true; - if (!disconnection) - { + if (!disconnection) { NBN_LogDebug("Send close message for client %d (code: %d)", client->id, code); NBN_GameServer_CreateReliableMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE); @@ -4427,78 +3344,44 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool return 0; } -static void GameServer_AddClientToClosedList(NBN_Connection *client) -{ - if (client->is_closed) return; +static void GameServer_AddClientToClosedList(NBN_Connection *client) { + if (client->is_closed) + return; NBN_ConnectionListNode *node = (NBN_ConnectionListNode *)NBN_Allocator(sizeof(NBN_ConnectionListNode)); node->conn = client; node->next = NULL; - if (nbn_game_server.closed_clients_head == NULL) - { + if (nbn_game_server.closed_clients_head == NULL) { // list is empty nbn_game_server.closed_clients_head = node; node->prev = NULL; - } - else - { + } else { // list is not empty, add node at the end NBN_ConnectionListNode *tail = nbn_game_server.closed_clients_head; - while (tail->next != NULL) tail = tail->next; + while (tail->next != NULL) + tail = tail->next; node->prev = tail; tail->next = node; } } -static unsigned int GameServer_GetClientCount(void) -{ - return nbn_game_server.clients->count; -} +static unsigned int GameServer_GetClientCount(void) { return nbn_game_server.clients->count; } -static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *client) -{ +static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *client) { NBN_Event ev; ev.type = NBN_CLIENT_MESSAGE_RECEIVED; - if (false) // TODO: - // if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) - { - /*NBN_Channel *channel = client->channels[message->header.channel_id];*/ - /**/ - /*if (!NBN_Channel_AddChunk(channel, message))*/ - /* return 0;*/ - /**/ - /*NBN_Message complete_message;*/ - /**/ - /*if (NBN_Channel_ReconstructMessageFromChunks(channel, client, &complete_message) < 0)*/ - /*{*/ - /* NBN_LogError("Failed to reconstruct message from chunks");*/ - /**/ - /* return NBN_ERROR;*/ - /*}*/ - /**/ - /*NBN_MessageInfo msg_info = {complete_message.header.type, complete_message.header.channel_id, complete_message.data, client->id};*/ - /**/ - /*ev.data.message_info = msg_info;*/ - } - else - { - NBN_MessageInfo msg_info = { - message->header.type, - message->header.channel_id, - message->data, - message->header.length, - client - }; - - NBN_LogDebug("Received message (type: %d, id: %d) from client %d", message->header.type, message->header.id, client->id); - ev.data.message_info = msg_info; - } + NBN_MessageInfo msg_info = {message->header.type, message->header.channel_id, message->data, message->header.length, + client}; + + NBN_LogDebug("Received message (type: %d, id: %d) from client %d", message->header.type, message->header.id, + client->id); + ev.data.message_info = msg_info; if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, ev)) return NBN_ERROR; @@ -4506,14 +3389,11 @@ static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio return 0; } -static int GameServer_CloseStaleClientConnections(void) -{ - for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) - { +static int GameServer_CloseStaleClientConnections(void) { + for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { NBN_Connection *client = nbn_game_server.clients->connections[i]; - if (!client->is_stale && NBN_Connection_CheckIfStale(client, nbn_game_server.endpoint.time)) - { + if (!client->is_stale && NBN_Connection_CheckIfStale(client, nbn_game_server.endpoint.time)) { NBN_LogInfo("Client %d connection is stale, closing it.", client->id); client->is_stale = true; @@ -4526,20 +3406,17 @@ static int GameServer_CloseStaleClientConnections(void) return 0; } -static void GameServer_RemoveClosedClientConnections(void) -{ +static void GameServer_RemoveClosedClientConnections(void) { NBN_ConnectionListNode *current = nbn_game_server.closed_clients_head; - while (current) - { + while (current) { NBN_ConnectionListNode *prev = current->prev; NBN_ConnectionListNode *next = current->next; NBN_Connection *client = current->conn; NBN_Assert(client->id > 0); - if (client->is_stale) - { + if (client->is_stale) { NBN_LogDebug("Remove closed client connection (ID: %d)", client->id); client->driver->impl.serv_remove_connection(client); // Notify the driver to remove the connection @@ -4548,8 +3425,7 @@ static void GameServer_RemoveClosedClientConnections(void) uint32_t rm_conn_id = NBN_ConnectionVector_RemoveAt(nbn_game_server.clients, client->vector_pos); - if (rm_conn_id != client->id) - { + if (rm_conn_id != client->id) { NBN_LogError("Failed to remove client connection from connections vector"); NBN_Abort(); } @@ -4562,24 +3438,21 @@ static void GameServer_RemoveClosedClientConnections(void) NBN_Deallocator(current); - if (current == nbn_game_server.closed_clients_head) - { + if (current == nbn_game_server.closed_clients_head) { // delete the head of the list NBN_ConnectionListNode *new_head = next; - if (new_head) - { + if (new_head) { new_head->prev = NULL; } nbn_game_server.closed_clients_head = new_head; - } - else - { + } else { // delete a node in the middle of the list prev->next = next; - if (next) next->prev = prev; + if (next) + next->prev = prev; } } @@ -4587,23 +3460,29 @@ static void GameServer_RemoveClosedClientConnections(void) } } -static int GameServer_HandleEvent(void) -{ - return nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED ? - GameServer_HandleMessageReceivedEvent() : - nbn_game_server.last_event.type; +static int GameServer_HandleEvent(void) { + return nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED ? GameServer_HandleMessageReceivedEvent() + : nbn_game_server.last_event.type; } // TODO: big ass function -static int GameServer_HandleMessageReceivedEvent(void) -{ - NBN_MessageInfo message_info = nbn_game_server.last_event.data.message_info; +static int GameServer_HandleMessageReceivedEvent(void) { + NBN_Event *last_event = &nbn_game_server.last_event; + NBN_MessageInfo message_info = last_event->data.message_info; NBN_Connection *sender = message_info.sender; + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + NBN_Message *last_received_message = &endpoint->last_received_message; - if (sender->is_closed || sender->is_stale) return NBN_SKIP_EVENT; + last_received_message->data = message_info.data; + last_received_message->type = NBN_INCOMING_MESSAGE; + last_received_message->header.length = message_info.length; - if (message_info.type == NBN_DISCONNECTION_MESSAGE_TYPE) - { + endpoint->last_message_need_free = true; + + if (sender->is_closed || sender->is_stale) + return NBN_SKIP_EVENT; + + if (message_info.type == NBN_DISCONNECTION_MESSAGE_TYPE) { NBN_LogInfo("Received disconnection message from client %d", sender->id); if (GameServer_CloseClientWithCode(sender, -1, true) < 0) @@ -4611,24 +3490,28 @@ static int GameServer_HandleMessageReceivedEvent(void) sender->is_stale = true; - nbn_game_server.last_event.type = NBN_CLIENT_DISCONNECTED; - nbn_game_server.last_event.data.connection = sender; + last_event->type = NBN_CLIENT_DISCONNECTED; + last_event->data.disconnection = (NBN_DisconnectionInfo){sender->id, sender->user_data}; GameServer_RemoveClosedClientConnections(); return NBN_CLIENT_DISCONNECTED; } - if (message_info.type != NBN_CONNECTION_REQUEST_MESSAGE_TYPE) - { + if (message_info.type != NBN_CONNECTION_REQUEST_MESSAGE_TYPE) { + NBN_Message *last_received_message = &endpoint->last_received_message; + + last_received_message->data = message_info.data; + last_received_message->type = NBN_INCOMING_MESSAGE; + last_received_message->header.length = message_info.length; + return NBN_CLIENT_MESSAGE_RECEIVED; } // at this point we know it's a connection request NBN_Assert(message_info.type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE); - if (message_info.length < 4) - { + if (message_info.length < 4) { NBN_LogError("Connection request invalid length"); return NBN_ERROR; @@ -4637,24 +3520,20 @@ static int GameServer_HandleMessageReceivedEvent(void) NBN_Reader *reader = NBN_GameServer_GetMessageReader(); unsigned int data_length; - if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) - { + if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) { NBN_LogError("Failed to read client data length"); return NBN_ERROR; } - if (data_length > 0) - { - if (data_length > sizeof(nbn_game_server.client_data_buffer)) - { + if (data_length > 0) { + if (data_length > sizeof(nbn_game_server.client_data_buffer)) { NBN_LogError("Invalid client data length"); return NBN_ERROR; } - if (NBN_Reader_ReadBytes(reader, nbn_game_server.client_data_buffer, data_length) < 0) - { + if (NBN_Reader_ReadBytes(reader, nbn_game_server.client_data_buffer, data_length) < 0) { NBN_LogError("Failed to read client data"); return NBN_ERROR; @@ -4668,7 +3547,7 @@ static int GameServer_HandleMessageReceivedEvent(void) e.type = NBN_NEW_CONNECTION; e.data.connection = sender; - if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, e)) + if (!NBN_EventQueue_Enqueue(&endpoint->event_queue, e)) return NBN_ERROR; return NBN_NO_EVENT; @@ -4678,10 +3557,8 @@ static int GameServer_HandleMessageReceivedEvent(void) #pragma region Game server driver -static int ServerDriver_OnClientConnected(NBN_Connection *client) -{ - if (GameServer_AddClient(client) < 0) - { +static int ServerDriver_OnClientConnected(NBN_Connection *client) { + if (GameServer_AddClient(client) < 0) { NBN_LogError("Failed to add client"); return NBN_ERROR; @@ -4690,10 +3567,8 @@ static int ServerDriver_OnClientConnected(NBN_Connection *client) return 0; } -static int ServerDriver_OnClientPacketReceived(NBN_Packet *packet) -{ - if (Endpoint_ProcessReceivedPacket(&nbn_game_server.endpoint, packet, packet->sender) < 0) - { +static int ServerDriver_OnClientPacketReceived(NBN_Packet *packet) { + if (Endpoint_ProcessReceivedPacket(&nbn_game_server.endpoint, packet, packet->sender) < 0) { NBN_LogError("An error occured while processing packet from client %d, closing the client", packet->sender->id); return GameServer_CloseClientWithCode(packet->sender, -1, false); @@ -4720,8 +3595,7 @@ static void *PacketSimulator_Routine(void *); static int PacketSimulator_SendPacket(NBN_PacketSimulator *, NBN_Packet *, NBN_Connection *receiver); static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimulator *); -void NBN_PacketSimulator_Init(NBN_PacketSimulator *packet_simulator, NBN_Endpoint *endpoint) -{ +void NBN_PacketSimulator_Init(NBN_PacketSimulator *packet_simulator, NBN_Endpoint *endpoint) { packet_simulator->endpoint = endpoint; packet_simulator->running = false; packet_simulator->ping = 0; @@ -4740,8 +3614,8 @@ void NBN_PacketSimulator_Init(NBN_PacketSimulator *packet_simulator, NBN_Endpoin #endif } -int NBN_PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN_Packet *packet, NBN_Connection *receiver) -{ +int NBN_PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN_Packet *packet, + NBN_Connection *receiver) { #ifdef NBNET_WINDOWS WaitForSingleObject(packet_simulator->queue_mutex, INFINITE); #else @@ -4756,7 +3630,7 @@ int NBN_PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN jitter = (jitter > 0) ? (rand() % (jitter * 2)) - jitter : 0; - NBN_PacketSimulatorEntry *entry = (NBN_PacketSimulatorEntry *)MemoryManager_Alloc(NBN_MEM_PACKET_SIMULATOR_ENTRY); + NBN_PacketSimulatorEntry *entry = (NBN_PacketSimulatorEntry *)NBN_Allocator(sizeof(NBN_PacketSimulatorEntry)); entry->delay = packet_simulator->ping + (double)jitter / 1000; /* and converted back to seconds */ entry->receiver = receiver; @@ -4764,15 +3638,13 @@ int NBN_PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN memcpy(&entry->packet, packet, sizeof(NBN_Packet)); - if (packet_simulator->packet_count > 0) - { + if (packet_simulator->packet_count > 0) { entry->prev = packet_simulator->tail_packet; entry->next = NULL; packet_simulator->tail_packet->next = entry; packet_simulator->tail_packet = entry; - } - else // the list is empty + } else // the list is empty { entry->prev = NULL; entry->next = NULL; @@ -4792,8 +3664,7 @@ int NBN_PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN return 0; } -void NBN_PacketSimulator_Start(NBN_PacketSimulator *packet_simulator) -{ +void NBN_PacketSimulator_Start(NBN_PacketSimulator *packet_simulator) { #ifdef NBNET_WINDOWS packet_simulator->thread = CreateThread(NULL, 0, PacketSimulator_Routine, packet_simulator, 0, NULL); #else @@ -4803,8 +3674,7 @@ void NBN_PacketSimulator_Start(NBN_PacketSimulator *packet_simulator) packet_simulator->running = true; } -void NBN_PacketSimulator_Stop(NBN_PacketSimulator *packet_simulator) -{ +void NBN_PacketSimulator_Stop(NBN_PacketSimulator *packet_simulator) { packet_simulator->running = false; #ifdef NBNET_WINDOWS @@ -4822,8 +3692,7 @@ static void *PacketSimulator_Routine(void *arg) { NBN_PacketSimulator *packet_simulator = (NBN_PacketSimulator *)arg; - while (packet_simulator->running) - { + while (packet_simulator->running) { #ifdef NBNET_WINDOWS WaitForSingleObject(packet_simulator->queue_mutex, INFINITE); #else @@ -4832,12 +3701,10 @@ static void *PacketSimulator_Routine(void *arg) NBN_PacketSimulatorEntry *entry = packet_simulator->head_packet; - while (entry) - { + while (entry) { NBN_PacketSimulatorEntry *next = entry->next; - if (packet_simulator->endpoint->time - entry->enqueued_at < entry->delay) - { + if (packet_simulator->endpoint->time - entry->enqueued_at < entry->delay) { entry = next; continue; @@ -4845,8 +3712,7 @@ static void *PacketSimulator_Routine(void *arg) PacketSimulator_SendPacket(packet_simulator, &entry->packet, entry->receiver); - for (unsigned int i = 0; i < PacketSimulator_GetRandomDuplicatePacketCount(packet_simulator); i++) - { + for (unsigned int i = 0; i < PacketSimulator_GetRandomDuplicatePacketCount(packet_simulator); i++) { NBN_LogDebug("Duplicate packet %d (count: %d)", entry->packet.header.seq_number, i + 1); PacketSimulator_SendPacket(packet_simulator, &entry->packet, entry->receiver); @@ -4863,15 +3729,13 @@ static void *PacketSimulator_Routine(void *arg) packet_simulator->tail_packet = NULL; packet_simulator->head_packet = new_head; - } - else if (entry == packet_simulator->tail_packet) // it's the tail of the list + } else if (entry == packet_simulator->tail_packet) // it's the tail of the list { NBN_PacketSimulatorEntry *new_tail = entry->prev; new_tail->next = NULL; packet_simulator->tail_packet = new_tail; - } - else // it's in the middle of the list + } else // it's in the middle of the list { entry->prev->next = entry->next; entry->next->prev = entry->prev; @@ -4880,7 +3744,7 @@ static void *PacketSimulator_Routine(void *arg) packet_simulator->packet_count--; // release the memory allocated for the entry - MemoryManager_Dealloc(entry, NBN_MEM_PACKET_SIMULATOR_ENTRY); + NBN_Deallocator(entry); entry = next; } @@ -4899,34 +3763,29 @@ static void *PacketSimulator_Routine(void *arg) #endif } -static int PacketSimulator_SendPacket( - NBN_PacketSimulator *packet_simulator, NBN_Packet *packet, NBN_Connection *receiver) -{ - if (RAND_RATIO < packet_simulator->packet_loss_ratio) - { +static int PacketSimulator_SendPacket(NBN_PacketSimulator *packet_simulator, NBN_Packet *packet, + NBN_Connection *receiver) { + if (RAND_RATIO < packet_simulator->packet_loss_ratio) { packet_simulator->total_dropped_packets++; - NBN_LogDebug("Drop packet %d (Total dropped packets: %d)", packet->header.seq_number, packet_simulator->total_dropped_packets); + NBN_LogDebug("Drop packet %d (Total dropped packets: %d)", packet->header.seq_number, + packet_simulator->total_dropped_packets); return 0; } NBN_Driver *driver = receiver->driver; - if (receiver->endpoint->is_server) - { + if (receiver->endpoint->is_server) { if (receiver->is_stale) return 0; return driver->impl.serv_send_packet_to(packet, receiver); - } - else - { + } else { return driver->impl.cli_send_packet(packet); } } -static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimulator *packet_simulator) -{ +static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimulator *packet_simulator) { if (RAND_RATIO < packet_simulator->packet_duplication_ratio) return rand() % 10 + 1; diff --git a/net_drivers/udp.h b/net_drivers/udp.h index 39761bd..0dac985 100644 --- a/net_drivers/udp.h +++ b/net_drivers/udp.h @@ -28,16 +28,18 @@ freely, subject to the following restrictions: How to use: 1. Include this header *once* after the nbnet header in the same file where you defined the NBNET_IMPL macro - 2. Call NBN_UDP_Register in both your client and server code before calling NBN_GameClient_Start or NBN_GameServer_Start + 2. Call NBN_UDP_Register in both your client and server code before calling NBN_GameClient_Start or + NBN_GameServer_Start */ void NBN_UDP_Register(void); #ifdef NBNET_IMPL -#include -#include #include +#include +#include +#include #define NBN_UDP_DRIVER_ID 0 #define NBN_UDP_DRIVER_NAME "UDP" @@ -45,13 +47,13 @@ void NBN_UDP_Register(void); #pragma region Platform detection #if defined(_WIN32) || defined(_WIN64) - #ifndef PLATFORM_WINDOWS - #define PLATFORM_WINDOWS - #endif +#ifndef PLATFORM_WINDOWS +#define PLATFORM_WINDOWS +#endif #elif (defined(__APPLE__) && defined(__MACH__)) - #define PLATFORM_MAC +#define PLATFORM_MAC #else - #define PLATFORM_UNIX +#define PLATFORM_UNIX #endif #pragma endregion /* Platform detection */ @@ -64,12 +66,12 @@ typedef int socklen_t; #elif defined(PLATFORM_UNIX) || defined(PLATFORM_MAC) -#include -#include -#include #include -#include #include +#include +#include +#include +#include #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 @@ -82,14 +84,12 @@ typedef struct in_addr IN_ADDR; #endif -typedef struct -{ +typedef struct { uint32_t host; uint16_t port; } NBN_IPAddress; -typedef struct -{ +typedef struct { uint32_t id; NBN_IPAddress address; NBN_Connection *conn; // nbnet connection associated to this UDP connection @@ -104,15 +104,13 @@ static bool CompareIPAddresses(NBN_IPAddress ip_addr1, NBN_IPAddress ip_addr2); #define HTABLE_DEFAULT_INITIAL_CAPACITY 32 #define HTABLE_LOAD_FACTOR_THRESHOLD 0.75 -typedef struct -{ +typedef struct { NBN_IPAddress ip_addr; NBN_UDP_Connection *conn; unsigned int slot; } NBN_UDP_HTableEntry; -typedef struct -{ +typedef struct { NBN_UDP_HTableEntry **internal_array; unsigned int capacity; unsigned int count; @@ -132,16 +130,14 @@ static NBN_UDP_HTableEntry *NBN_UDP_HTable_FindEntry(NBN_UDP_HTable *, NBN_IPAdd static void NBN_UDP_HTable_Grow(NBN_UDP_HTable *); static unsigned long NBN_UDP_HTable_HashSDBM(NBN_IPAddress); -static NBN_UDP_HTable *NBN_UDP_HTable_Create(void) -{ +static NBN_UDP_HTable *NBN_UDP_HTable_Create(void) { return NBN_UDP_HTable_CreateWithCapacity(HTABLE_DEFAULT_INITIAL_CAPACITY); } -static NBN_UDP_HTable *NBN_UDP_HTable_CreateWithCapacity(unsigned int capacity) -{ - NBN_UDP_HTable *htable = (NBN_UDP_HTable *) NBN_Allocator(sizeof(NBN_UDP_HTable)); +static NBN_UDP_HTable *NBN_UDP_HTable_CreateWithCapacity(unsigned int capacity) { + NBN_UDP_HTable *htable = (NBN_UDP_HTable *)NBN_Allocator(sizeof(NBN_UDP_HTable)); - htable->internal_array = (NBN_UDP_HTableEntry **) NBN_Allocator(sizeof(NBN_UDP_HTableEntry *) * capacity); + htable->internal_array = (NBN_UDP_HTableEntry **)NBN_Allocator(sizeof(NBN_UDP_HTableEntry *) * capacity); htable->capacity = capacity; htable->count = 0; htable->load_factor = 0; @@ -152,10 +148,8 @@ static NBN_UDP_HTable *NBN_UDP_HTable_CreateWithCapacity(unsigned int capacity) return htable; } -static void NBN_UDP_HTable_Destroy(NBN_UDP_HTable *htable) -{ - for (unsigned int i = 0; i < htable->capacity; i++) - { +static void NBN_UDP_HTable_Destroy(NBN_UDP_HTable *htable) { + for (unsigned int i = 0; i < htable->capacity; i++) { NBN_UDP_HTableEntry *entry = htable->internal_array[i]; if (entry) @@ -166,9 +160,8 @@ static void NBN_UDP_HTable_Destroy(NBN_UDP_HTable *htable) NBN_Deallocator(htable); } -static void NBN_UDP_HTable_Add(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr, NBN_UDP_Connection *conn) -{ - NBN_UDP_HTableEntry *entry = (NBN_UDP_HTableEntry*) NBN_Allocator(sizeof(NBN_UDP_HTableEntry)); +static void NBN_UDP_HTable_Add(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr, NBN_UDP_Connection *conn) { + NBN_UDP_HTableEntry *entry = (NBN_UDP_HTableEntry *)NBN_Allocator(sizeof(NBN_UDP_HTableEntry)); entry->ip_addr = ip_addr; entry->conn = conn; @@ -179,19 +172,16 @@ static void NBN_UDP_HTable_Add(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr, NB NBN_UDP_HTable_Grow(htable); } -static NBN_UDP_Connection *NBN_UDP_HTable_Get(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr) -{ +static NBN_UDP_Connection *NBN_UDP_HTable_Get(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr) { NBN_UDP_HTableEntry *entry = NBN_UDP_HTable_FindEntry(htable, ip_addr); return entry ? entry->conn : NULL; } -static NBN_UDP_Connection *NBN_UDP_HTable_Remove(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr) -{ +static NBN_UDP_Connection *NBN_UDP_HTable_Remove(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr) { NBN_UDP_HTableEntry *entry = NBN_UDP_HTable_FindEntry(htable, ip_addr); - if (entry) - { + if (entry) { NBN_UDP_Connection *conn = entry->conn; NBN_UDP_HTable_RemoveEntry(htable, entry); @@ -201,23 +191,20 @@ static NBN_UDP_Connection *NBN_UDP_HTable_Remove(NBN_UDP_HTable *htable, NBN_IPA return NULL; } -static void NBN_UDP_HTable_InsertEntry(NBN_UDP_HTable *htable, NBN_UDP_HTableEntry *entry) -{ +static void NBN_UDP_HTable_InsertEntry(NBN_UDP_HTable *htable, NBN_UDP_HTableEntry *entry) { bool use_existing_slot = false; unsigned int slot = NBN_UDP_HTable_FindFreeSlot(htable, entry, &use_existing_slot); entry->slot = slot; htable->internal_array[slot] = entry; - if (!use_existing_slot) - { + if (!use_existing_slot) { htable->count++; htable->load_factor = (float)htable->count / htable->capacity; } } -static void NBN_UDP_HTable_RemoveEntry(NBN_UDP_HTable *htable, NBN_UDP_HTableEntry *entry) -{ +static void NBN_UDP_HTable_RemoveEntry(NBN_UDP_HTable *htable, NBN_UDP_HTableEntry *entry) { htable->internal_array[entry->slot] = NULL; NBN_Deallocator(entry); @@ -226,8 +213,8 @@ static void NBN_UDP_HTable_RemoveEntry(NBN_UDP_HTable *htable, NBN_UDP_HTableEnt htable->load_factor = (float)htable->count / htable->capacity; } -static unsigned int NBN_UDP_HTable_FindFreeSlot(NBN_UDP_HTable *htable, NBN_UDP_HTableEntry *entry, bool *use_existing_slot) -{ +static unsigned int NBN_UDP_HTable_FindFreeSlot(NBN_UDP_HTable *htable, NBN_UDP_HTableEntry *entry, + bool *use_existing_slot) { unsigned long hash = NBN_UDP_HTable_HashSDBM(entry->ip_addr); unsigned int slot; @@ -236,8 +223,7 @@ static unsigned int NBN_UDP_HTable_FindFreeSlot(NBN_UDP_HTable *htable, NBN_UDP_ NBN_UDP_HTableEntry *current_entry; unsigned int i = 0; - do - { + do { slot = (hash + (int)pow(i, 2)) % htable->capacity; current_entry = htable->internal_array[slot]; @@ -250,45 +236,41 @@ static unsigned int NBN_UDP_HTable_FindFreeSlot(NBN_UDP_HTable *htable, NBN_UDP_ NBN_Deallocator(current_entry); } - + return slot; } -static NBN_UDP_HTableEntry *NBN_UDP_HTable_FindEntry(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr) -{ +static NBN_UDP_HTableEntry *NBN_UDP_HTable_FindEntry(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr) { unsigned long hash = NBN_UDP_HTable_HashSDBM(ip_addr); unsigned int slot; - //quadratic probing + // quadratic probing NBN_UDP_HTableEntry *current_entry; unsigned int i = 0; - do - { + do { slot = (hash + (int)pow(i, 2)) % htable->capacity; current_entry = htable->internal_array[slot]; - if (current_entry != NULL && CompareIPAddresses(current_entry->ip_addr, ip_addr)) - { + if (current_entry != NULL && CompareIPAddresses(current_entry->ip_addr, ip_addr)) { return current_entry; } i++; } while (i < htable->capacity); - + return NULL; } -static void NBN_UDP_HTable_Grow(NBN_UDP_HTable *htable) -{ +static void NBN_UDP_HTable_Grow(NBN_UDP_HTable *htable) { unsigned int old_capacity = htable->capacity; unsigned int new_capacity = old_capacity * 2; - NBN_UDP_HTableEntry** old_internal_array = htable->internal_array; - NBN_UDP_HTableEntry** new_internal_array = (NBN_UDP_HTableEntry**) NBN_Allocator(sizeof(NBN_UDP_HTableEntry*) * new_capacity); + NBN_UDP_HTableEntry **old_internal_array = htable->internal_array; + NBN_UDP_HTableEntry **new_internal_array = + (NBN_UDP_HTableEntry **)NBN_Allocator(sizeof(NBN_UDP_HTableEntry *) * new_capacity); - for (unsigned int i = 0; i < new_capacity; i++) - { + for (unsigned int i = 0; i < new_capacity; i++) { new_internal_array[i] = NULL; } @@ -299,8 +281,7 @@ static void NBN_UDP_HTable_Grow(NBN_UDP_HTable *htable) // rehash - for (unsigned int i = 0; i < old_capacity; i++) - { + for (unsigned int i = 0; i < old_capacity; i++) { if (old_internal_array[i]) NBN_UDP_HTable_InsertEntry(htable, old_internal_array[i]); } @@ -308,10 +289,7 @@ static void NBN_UDP_HTable_Grow(NBN_UDP_HTable *htable) NBN_Deallocator(old_internal_array); } -static unsigned long NBN_UDP_HTable_HashSDBM(NBN_IPAddress ip_addr) -{ - return ip_addr.host ^ ip_addr.port; -} +static unsigned long NBN_UDP_HTable_HashSDBM(NBN_IPAddress ip_addr) { return ip_addr.host ^ ip_addr.port; } #pragma endregion // Hashtable @@ -328,13 +306,11 @@ static void DeinitSocket(void); static int BindSocket(uint16_t); static char *GetLastErrorMessage(void); -static int InitSocket(void) -{ +static int InitSocket(void) { #ifdef PLATFORM_WINDOWS WSADATA wsa; int err = WSAStartup(MAKEWORD(2, 2), &wsa); - if (err < 0) - { + if (err < 0) { NBN_LogError("WSAStartup() failed"); return NBN_ERROR; @@ -347,8 +323,7 @@ static int InitSocket(void) #if defined(PLATFORM_WINDOWS) DWORD non_blocking = 1; - if (ioctlsocket(nbn_udp_sock, FIONBIO, &non_blocking) != 0) - { + if (ioctlsocket(nbn_udp_sock, FIONBIO, &non_blocking) != 0) { NBN_LogError("ioctlsocket() failed: %s", GetLastErrorMessage()); return NBN_ERROR; @@ -356,8 +331,7 @@ static int InitSocket(void) #elif defined(PLATFORM_MAC) || defined(PLATFORM_UNIX) int non_blocking = 1; - if (fcntl(nbn_udp_sock, F_SETFL, O_NONBLOCK, non_blocking) < 0) - { + if (fcntl(nbn_udp_sock, F_SETFL, O_NONBLOCK, non_blocking) < 0) { NBN_LogError("fcntl() failed: %s", GetLastErrorMessage()); return NBN_ERROR; @@ -367,8 +341,7 @@ static int InitSocket(void) return 0; } -static void DeinitSocket(void) -{ +static void DeinitSocket(void) { closesocket(nbn_udp_sock); #ifdef PLATFORM_WINDOWS @@ -376,33 +349,29 @@ static void DeinitSocket(void) #endif } -static int BindSocket(uint16_t port) -{ +static int BindSocket(uint16_t port) { SOCKADDR_IN sin; sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_family = AF_INET; sin.sin_port = htons(port); - if (bind(nbn_udp_sock, (SOCKADDR *)&sin, sizeof(sin)) < 0) - { + if (bind(nbn_udp_sock, (SOCKADDR *)&sin, sizeof(sin)) < 0) { NBN_LogError("bind() failed: %s", GetLastErrorMessage()); return NBN_ERROR; } - + return 0; } -static int ResolveIpAddress(const char *host, uint16_t port, NBN_IPAddress *address) -{ +static int ResolveIpAddress(const char *host, uint16_t port, NBN_IPAddress *address) { size_t host_len = strlen(host); - char *dup_host = (char*)NBN_Allocator(host_len + 1); + char *dup_host = (char *)NBN_Allocator(host_len + 1); memcpy(dup_host, host, host_len + 1); uint8_t arr[4]; - for (int i = 0; i < 4; i++) - { + for (int i = 0; i < 4; i++) { char *s; // TODO: replace strtok with strsep @@ -426,8 +395,7 @@ static int ResolveIpAddress(const char *host, uint16_t port, NBN_IPAddress *addr return 0; } -static char *GetLastErrorMessage(void) -{ +static char *GetLastErrorMessage(void) { #ifdef PLATFORM_WINDOWS snprintf(err_msg, sizeof(err_msg), "%d", WSAGetLastError()); @@ -441,8 +409,7 @@ static char *GetLastErrorMessage(void) #pragma region Game server -typedef struct NBN_UDP_Server -{ +typedef struct NBN_UDP_Server { NBN_UDP_HTable *connections; uint32_t next_conn_id; // nbnet connection ids, starts at 1 uint32_t protocol_id; @@ -452,8 +419,7 @@ static NBN_UDP_Server nbn_udp_serv = {NULL, 1, 0}; static NBN_Connection *FindOrCreateClientConnectionByAddress(NBN_IPAddress); -static int NBN_UDP_ServStart(uint32_t protocol_id, uint16_t port) -{ +static int NBN_UDP_ServStart(uint32_t protocol_id, uint16_t port) { nbn_udp_serv.protocol_id = protocol_id; nbn_udp_serv.connections = NBN_UDP_HTable_Create(); @@ -466,22 +432,20 @@ static int NBN_UDP_ServStart(uint32_t protocol_id, uint16_t port) return 0; } -static void NBN_UDP_ServStop(void) -{ +static void NBN_UDP_ServStop(void) { NBN_UDP_HTable_Destroy(nbn_udp_serv.connections); DeinitSocket(); } -static int NBN_UDP_ServRecvPackets(void) -{ +static int NBN_UDP_ServRecvPackets(void) { NBN_Packet packet = {0}; SOCKADDR_IN src_addr; socklen_t src_addr_len = sizeof(src_addr); NBN_IPAddress ip_address; - while (true) - { - int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, &src_addr_len); + while (true) { + int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, + &src_addr_len); if (bytes <= 0) break; @@ -489,8 +453,7 @@ static int NBN_UDP_ServRecvPackets(void) if (bytes <= NBN_PACKET_HEADER_SIZE) continue; - if (NBN_Packet_InitRead(&packet, nbn_udp_serv.protocol_id, bytes) < 0) - { + if (NBN_Packet_InitRead(&packet, nbn_udp_serv.protocol_id, bytes) < 0) { NBN_LogDebug("Discarded invalid packet"); continue; } @@ -504,9 +467,8 @@ static int NBN_UDP_ServRecvPackets(void) continue; packet.sender = conn; - - if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet) < 0) - { + + if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet) < 0) { NBN_LogError("Failed to raise game server event"); return NBN_ERROR; @@ -516,22 +478,20 @@ static int NBN_UDP_ServRecvPackets(void) return 0; } -static void NBN_UDP_ServRemoveClientConnection(NBN_Connection *connection) -{ +static void NBN_UDP_ServRemoveClientConnection(NBN_Connection *connection) { assert(connection != NULL); - NBN_UDP_Connection *udp_conn = NBN_UDP_HTable_Remove(nbn_udp_serv.connections, ((NBN_UDP_Connection *)connection->driver_data)->address); + NBN_UDP_Connection *udp_conn = + NBN_UDP_HTable_Remove(nbn_udp_serv.connections, ((NBN_UDP_Connection *)connection->driver_data)->address); - if (udp_conn) - { + if (udp_conn) { NBN_LogDebug("Destroyed UDP connection %d", connection->id); NBN_Deallocator(udp_conn); } } -static int NBN_UDP_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *connection) -{ +static int NBN_UDP_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *connection) { NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)connection->driver_data; SOCKADDR_IN dest_addr; @@ -540,8 +500,8 @@ static int NBN_UDP_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *connecti dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(udp_conn->address.port); - if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, sizeof(dest_addr)) == SOCKET_ERROR) - { + if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, + sizeof(dest_addr)) == SOCKET_ERROR) { NBN_LogError("sendto() failed: %s", GetLastErrorMessage()); return NBN_ERROR; @@ -550,12 +510,10 @@ static int NBN_UDP_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *connecti return 0; } -static NBN_Connection *FindOrCreateClientConnectionByAddress(NBN_IPAddress address) -{ +static NBN_Connection *FindOrCreateClientConnectionByAddress(NBN_IPAddress address) { NBN_UDP_Connection *udp_conn = NBN_UDP_HTable_Get(nbn_udp_serv.connections, address); - if (udp_conn == NULL) - { + if (udp_conn == NULL) { /* this is a new connection */ if (GameServer_GetClientCount() >= NBN_MAX_CLIENTS) @@ -571,8 +529,7 @@ static NBN_Connection *FindOrCreateClientConnectionByAddress(NBN_IPAddress addre NBN_LogDebug("New UDP connection (id: %d)", udp_conn->id); - if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, udp_conn->conn) < 0) - { + if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, udp_conn->conn) < 0) { NBN_LogError("Failed to raise game server event"); return NULL; @@ -582,8 +539,7 @@ static NBN_Connection *FindOrCreateClientConnectionByAddress(NBN_IPAddress addre return udp_conn->conn; } -static bool CompareIPAddresses(NBN_IPAddress ip_addr1, NBN_IPAddress ip_addr2) -{ +static bool CompareIPAddresses(NBN_IPAddress ip_addr1, NBN_IPAddress ip_addr2) { return ip_addr1.host == ip_addr2.host && ip_addr1.port == ip_addr2.port; } @@ -591,8 +547,7 @@ static bool CompareIPAddresses(NBN_IPAddress ip_addr1, NBN_IPAddress ip_addr2) #pragma region Game client -typedef struct NBN_UDP_Client -{ +typedef struct NBN_UDP_Client { NBN_Connection *server_conn; uint32_t protocol_id; } NBN_UDP_Client; @@ -601,14 +556,12 @@ static NBN_UDP_Client nbn_udp_cli = {NULL, 0}; static int ResolveIpAddress(const char *, uint16_t, NBN_IPAddress *); -static int NBN_UDP_CliStart(uint32_t protocol_id, const char *host, uint16_t port) -{ +static int NBN_UDP_CliStart(uint32_t protocol_id, const char *host, uint16_t port) { NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)NBN_Allocator(sizeof(NBN_Connection)); nbn_udp_cli.protocol_id = protocol_id; - if (ResolveIpAddress(host, port, &udp_conn->address) < 0) - { + if (ResolveIpAddress(host, port, &udp_conn->address) < 0) { NBN_LogError("Failed to resolve IP address from %s", host); return NBN_ERROR; @@ -625,24 +578,22 @@ static int NBN_UDP_CliStart(uint32_t protocol_id, const char *host, uint16_t por return 0; } -static void NBN_UDP_CliStop(void) -{ +static void NBN_UDP_CliStop(void) { NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)nbn_udp_cli.server_conn->driver_data; NBN_Deallocator(udp_conn); DeinitSocket(); } -static int NBN_UDP_CliRecvPackets(void) -{ +static int NBN_UDP_CliRecvPackets(void) { NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)nbn_udp_cli.server_conn->driver_data; NBN_Packet packet = {0}; SOCKADDR_IN src_addr; socklen_t src_addr_len = sizeof(src_addr); - while (true) - { - int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, &src_addr_len); + while (true) { + int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, + &src_addr_len); if (bytes <= 0) break; @@ -658,8 +609,7 @@ static int NBN_UDP_CliRecvPackets(void) if (ip_address.host != udp_conn->address.host || ip_address.port != udp_conn->address.port) continue; - if (NBN_Packet_InitRead(&packet, nbn_udp_cli.protocol_id, bytes) < 0) - { + if (NBN_Packet_InitRead(&packet, nbn_udp_cli.protocol_id, bytes) < 0) { NBN_LogDebug("Discarded invalid packet"); continue; } @@ -672,8 +622,7 @@ static int NBN_UDP_CliRecvPackets(void) return 0; } -static int NBN_UDP_CliSendPacket(NBN_Packet *packet) -{ +static int NBN_UDP_CliSendPacket(NBN_Packet *packet) { NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)nbn_udp_cli.server_conn->driver_data; SOCKADDR_IN dest_addr; @@ -681,8 +630,8 @@ static int NBN_UDP_CliSendPacket(NBN_Packet *packet) dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(udp_conn->address.port); - if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, sizeof(dest_addr)) == SOCKET_ERROR) - { + if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, + sizeof(dest_addr)) == SOCKET_ERROR) { NBN_LogError("sendto() failed: %s", GetLastErrorMessage()); return NBN_ERROR; @@ -695,28 +644,16 @@ static int NBN_UDP_CliSendPacket(NBN_Packet *packet) #pragma region Driver registering -void NBN_UDP_Register(void) -{ - NBN_DriverImplementation driver_impl = { - // Client implementation - NBN_UDP_CliStart, - NBN_UDP_CliStop, - NBN_UDP_CliRecvPackets, - NBN_UDP_CliSendPacket, - - // Server implementation - NBN_UDP_ServStart, - NBN_UDP_ServStop, - NBN_UDP_ServRecvPackets, - NBN_UDP_ServSendPacketTo, - NBN_UDP_ServRemoveClientConnection - }; - - NBN_Driver_Register( - NBN_UDP_DRIVER_ID, - NBN_UDP_DRIVER_NAME, - driver_impl - ); +void NBN_UDP_Register(void) { + NBN_DriverImplementation driver_impl = {// Client implementation + NBN_UDP_CliStart, NBN_UDP_CliStop, NBN_UDP_CliRecvPackets, + NBN_UDP_CliSendPacket, + + // Server implementation + NBN_UDP_ServStart, NBN_UDP_ServStop, NBN_UDP_ServRecvPackets, + NBN_UDP_ServSendPacketTo, NBN_UDP_ServRemoveClientConnection}; + + NBN_Driver_Register(NBN_UDP_DRIVER_ID, NBN_UDP_DRIVER_NAME, driver_impl); } #pragma endregion /* Driver registering */ diff --git a/soak/client.c b/soak/client.c index 94224af..ead7523 100644 --- a/soak/client.c +++ b/soak/client.c @@ -39,16 +39,15 @@ #endif // __EMSCRIPTEN__ -typedef struct -{ - uint8_t data[SOAK_MESSAGE_MAX_DATA_LENGTH]; +typedef struct { + uint8_t data[SOAK_MESSAGE_BIG_MAX_LENGTH]; uint8_t channel_id; unsigned int length; bool free; } Soak_MessageEntry; -typedef struct -{ +typedef struct { + uint8_t id; unsigned int message_count; unsigned int sent_message_count; unsigned int next_msg_id; @@ -60,18 +59,15 @@ typedef struct static bool connected = false; static unsigned int done_channel_count = 0; -static void GenerateRandomBytes(uint8_t *data, unsigned int length) -{ +static void GenerateRandomBytes(uint8_t *data, unsigned int length) { for (int i = 0; i < length; i++) data[i] = rand() % 255 + 1; } -static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) -{ +static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { unsigned int msg_count = channel->message_count; - if (channel->sent_message_count < msg_count) - { + if (channel->sent_message_count < msg_count) { // number of messages yet to be sent unsigned int remaining_message_count = msg_count - channel->sent_message_count; @@ -79,114 +75,122 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) unsigned int pending_message_count = channel->last_sent_message_id - channel->last_recved_message_id; Soak_LogInfo("Compute number of soak messages to send (sent: %d, pending: %d, remaining: %d)", - channel->sent_message_count, pending_message_count, remaining_message_count); + channel->sent_message_count, pending_message_count, remaining_message_count); // don't send anything on this tick if we have reached the max number of unacked messages - if (pending_message_count >= SOAK_CLIENT_MAX_PENDING_MESSAGES) - { + if (pending_message_count >= SOAK_CLIENT_MAX_PENDING_MESSAGES) { Soak_LogInfo("Max number of pending messages has been reached, not sending anything this tick"); return 0; } // number of messages to send on this tick - unsigned int send_message_count = MIN( - SOAK_CLIENT_MAX_PENDING_MESSAGES - pending_message_count, remaining_message_count); + unsigned int send_message_count = + MIN(SOAK_CLIENT_MAX_PENDING_MESSAGES - pending_message_count, remaining_message_count); Soak_LogInfo("Will send %d soak messages this tick", send_message_count); - for (int i = 0; i < send_message_count; i++) - { - SoakMessage *msg = SoakMessage_CreateOutgoing(); - - if (msg == NULL) - { - Soak_LogError("Failed to create soak message"); - - return -1; - } - + for (int i = 0; i < send_message_count; i++) { int percent = rand() % 100 + 1; - - if (percent <= SOAK_BIG_MESSAGE_PERCENTAGE) - { - // chunked - msg->data_length = rand() % (SOAK_MESSAGE_MAX_DATA_LENGTH - 1024) + 1024; + unsigned int min_len; + unsigned int max_len; + + if (percent <= SOAK_BIG_MESSAGE_PERCENTAGE) { + min_len = SOAK_MESSAGE_BIG_MIN_DATA_LENGTH; + max_len = SOAK_MESSAGE_BIG_MAX_DATA_LENGTH; + } else { + min_len = SOAK_MESSAGE_SMALL_MIN_DATA_LENGTH; + max_len = SOAK_MESSAGE_SMALL_MAX_DATA_LENGTH; } - else - { - // not chuncked - msg->data_length = rand() % (200 - SOAK_MESSAGE_MIN_DATA_LENGTH) + SOAK_MESSAGE_MIN_DATA_LENGTH; - } - - msg->id = channel->next_msg_id++; - - GenerateRandomBytes(msg->data, msg->data_length); - Soak_MessageEntry *entry = &channel->messages[(msg->id - 1) % SOAK_CLIENT_MAX_PENDING_MESSAGES]; + unsigned int data_length = rand() % (max_len - min_len) + min_len; + unsigned int msg_id = channel->next_msg_id++; + Soak_MessageEntry *entry = &channel->messages[(msg_id - 1) % SOAK_CLIENT_MAX_PENDING_MESSAGES]; assert(entry->free); - entry->length = msg->data_length; + GenerateRandomBytes(entry->data, data_length); + + // entry->msg_id = msg_id; + entry->length = data_length; entry->free = false; entry->channel_id = channel_id; - memcpy(entry->data, msg->data, msg->data_length); - Soak_LogInfo("Send soak message (id: %d, data length: %d)", msg->id, msg->data_length); + Soak_LogInfo("Send soak message (id: %d, data length: %d)", msg_id, data_length); + + // TODO: support big messages + NBN_GameClient_CreateMessage(SOAK_MESSAGE_SMALL, channel->id); + NBN_Writer *writer = NBN_GameClient_GetMessageWriter(); - if (NBN_GameClient_SendMessage(SOAK_MESSAGE, channel_id, msg) < 0) + SoakMessage_Write(writer, msg_id, entry->data, entry->length); + + if (NBN_GameClient_SendMessage() < 0) return -1; channel->sent_message_count++; - channel->last_sent_message_id = msg->id; + channel->last_sent_message_id = msg_id; } } return 0; } -static int HandleReceivedSoakMessage(SoakMessage *msg, uint8_t channel_id, SoakChannel *channels) -{ - SoakChannel *channel = &channels[channel_id]; +static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) { + SoakChannel *channel = &channels[channel_id - 2]; + unsigned int msg_id; + unsigned int data_length; + static uint8_t recv_buffer[SOAK_MESSAGE_BIG_MAX_DATA_LENGTH]; + + NBN_Reader *reader = NBN_GameClient_GetMessageReader(); + + if (SoakMessage_Read(reader, &msg_id, recv_buffer, &data_length) < 0) { + Soak_LogError("Failed to read soak message"); + + return -1; + } - if (msg->id != channel->last_recved_message_id + 1) - { - Soak_LogError("Expected to receive message %d but received message %d (channel_id: %d)", channel->last_recved_message_id + 1, msg->id, channel_id); + if (msg_id != channel->last_recved_message_id + 1) { + Soak_LogError("Expected to receive message %d but received message %d (channel_id: %d)", + channel->last_recved_message_id + 1, msg_id, channel_id); return -1; } - Soak_MessageEntry *entry = &channel->messages[(msg->id - 1) % SOAK_CLIENT_MAX_PENDING_MESSAGES]; + Soak_MessageEntry *entry = &channel->messages[(msg_id - 1) % SOAK_CLIENT_MAX_PENDING_MESSAGES]; assert(!entry->free); assert(entry->channel_id == channel_id); - if (memcmp(msg->data, entry->data, msg->data_length) != 0) - { - Soak_LogError("Received invalid data for message %d (data length: %d, channel_id: %d)", msg->id, msg->data_length, channel_id); + if (data_length != entry->length) { + Soak_LogError("Expected message %d to have length %d but was %d (channel_id: %d)", msg_id, entry->length, + data_length); + + return -1; + } + + if (memcmp(recv_buffer, entry->data, data_length) != 0) { + Soak_LogError("Received invalid data for message %d (data length: %d, channel_id: %d)", msg_id, data_length, + channel_id); return -1; } entry->free = true; - channel->last_recved_message_id = msg->id; + channel->last_recved_message_id = msg_id; SoakOptions options = Soak_GetOptions(); unsigned int channel_count = options.channel_count; - Soak_LogInfo("Received soak message (length: %d, %d/%d) on channel %d", msg->data_length, msg->id, channel->message_count, channel_id); + Soak_LogInfo("Received soak message (length: %d, %d/%d) on channel %d", data_length, msg_id, channel->message_count, + channel_id); - SoakMessage_Destroy(msg); - - if (channel->last_recved_message_id == channel->message_count) - { + if (channel->last_recved_message_id == channel->message_count) { Soak_LogInfo("Received all soak message echoes on channel %d", channel_id); done_channel_count++; } - if (done_channel_count >= channel_count) - { + if (done_channel_count >= channel_count) { Soak_LogInfo("Received all soak message echoes on all channels"); Soak_Stop(); @@ -196,74 +200,65 @@ static int HandleReceivedSoakMessage(SoakMessage *msg, uint8_t channel_id, SoakC return 0; } -static int HandleReceivedMessage(SoakChannel *channels) -{ +static int HandleReceivedMessage(SoakChannel *channels) { NBN_MessageInfo msg = NBN_GameClient_GetMessageInfo(); - switch (msg.type) - { - case SOAK_MESSAGE: - return HandleReceivedSoakMessage((SoakMessage *)msg.data, msg.channel_id, channels); + int ret; - default: - Soak_LogError("Received unexpected message (type: %d, channel_id: %d)", msg.type, msg.channel_id); + if (msg.type == SOAK_MESSAGE_SMALL) { + ret = HandleReceivedSoakMessage(msg.channel_id, channels); + } else { + Soak_LogError("Received unexpected message (type: %d, channel_id: %d)", msg.type, msg.channel_id); - return -1; + ret = -1; } - return 0; + return ret; } -static int Tick(void *data) -{ +static int Tick(void *data) { SoakChannel *channels = (SoakChannel *)data; int ev; - while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) - { + while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) { if (ev < 0) return -1; - switch (ev) - { - case NBN_DISCONNECTED: - connected = false; + switch (ev) { + case NBN_DISCONNECTED: + connected = false; - Soak_LogInfo("Disconnected from server (code: %d)", NBN_GameClient_GetServerCloseCode()); - Soak_Stop(); - return 0; + Soak_LogInfo("Disconnected from server (code: %d)", NBN_GameClient_GetServerCloseCode()); + Soak_Stop(); + return 0; - case NBN_CONNECTED: - Soak_LogInfo("Connected to server"); - connected = true; - break; + case NBN_CONNECTED: + Soak_LogInfo("Connected to server"); + connected = true; + break; - case NBN_MESSAGE_RECEIVED: - if (HandleReceivedMessage(channels) < 0) - return -1; - break; + case NBN_MESSAGE_RECEIVED: + if (HandleReceivedMessage(channels) < 0) + return -1; + break; } } - if (connected) - { + if (connected) { unsigned int channel_count = Soak_GetOptions().channel_count; - for (unsigned int c = 0; c < channel_count; c++) - { + for (unsigned int c = 0; c < channel_count; c++) { SoakChannel *channel = &channels[c]; - if (SendSoakMessages(channel, c) < 0) - { + if (SendSoakMessages(channel, channel->id) < 0) { Soak_LogError("An error occured while sending messages on channel %d", c); return -1; } } } - if (NBN_GameClient_SendPackets() < 0) - { + if (NBN_GameClient_SendPackets() < 0) { Soak_LogError("Failed to flush game client send queue. Exit"); return -1; @@ -272,8 +267,7 @@ static int Tick(void *data) return 0; } -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { Soak_SetLogLevel(LOG_TRACE); if (Soak_ReadCommandLine(argc, argv) < 0) @@ -287,23 +281,19 @@ int main(int argc, char *argv[]) #ifdef WEBRTC_NATIVE - if (options.webrtc) - { + if (options.webrtc) { // Register native WebRTC driver - const char *ice_servers[] = { "stun:stun01.sipphone.com" }; - NBN_WebRTC_C_Config cfg = { - .ice_servers = ice_servers, - .ice_servers_count = 1, - .enable_tls = false, - .cert_path = NULL, - .key_path = NULL, - .passphrase = NULL, - .log_level = RTC_LOG_VERBOSE}; + const char *ice_servers[] = {"stun:stun01.sipphone.com"}; + NBN_WebRTC_C_Config cfg = {.ice_servers = ice_servers, + .ice_servers_count = 1, + .enable_tls = false, + .cert_path = NULL, + .key_path = NULL, + .passphrase = NULL, + .log_level = RTC_LOG_VERBOSE}; NBN_WebRTC_C_Register(cfg); - } - else - { + } else { NBN_UDP_Register(); } @@ -313,10 +303,14 @@ int main(int argc, char *argv[]) #endif // WEBRTC_NATIVE -#endif // __EMSCRIPTEN__ +#endif // __EMSCRIPTEN__ + + NBN_GameClient_Config config = + NBN_GameClient_CreateConfig(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT, AllocateMessage, DeallocateMessage); - if (NBN_GameClient_Start(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT) < 0) - { + NBN_GameClient_EnableCustomChannels(&config, options.channel_count); + + if (NBN_GameClient_Start(config) < 0) { Soak_LogError("Failed to start game client. Exit"); #ifdef __EMSCRIPTEN__ @@ -326,8 +320,7 @@ int main(int argc, char *argv[]) #endif } - if (Soak_Init(argc, argv) < 0) - { + if (Soak_Init(argc, argv) < 0) { Soak_LogError("Failed to initialize soak test"); return 1; } @@ -338,59 +331,60 @@ int main(int argc, char *argv[]) unsigned int leftover_message_count = message_count % channel_count; SoakChannel *channels = (SoakChannel *)malloc(sizeof(SoakChannel) * channel_count); - for (int c = 0; c < channel_count; c++) - { + for (int c = 0; c < channel_count; c++) { SoakChannel *channel = &channels[c]; - + + channel->id = 2 + c; // channels 0 and 1 are reserved by the library channel->next_msg_id = 1; channel->sent_message_count = 0; channel->last_recved_message_id = 0; channel->last_sent_message_id = 0; channel->message_count = message_per_channel; - for (int i = 0; i < SOAK_CLIENT_MAX_PENDING_MESSAGES; i++) - { + for (int i = 0; i < SOAK_CLIENT_MAX_PENDING_MESSAGES; i++) { channel->messages[i].free = true; } } channels[channel_count - 1].message_count += leftover_message_count; - NBN_GameClient_Debug_RegisterCallback(NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE, (void *)Soak_Debug_PrintAddedToRecvQueue); + NBN_GameClient_Debug_RegisterCallback(NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE, + (void *)Soak_Debug_PrintAddedToRecvQueue); int ret = Soak_MainLoop(Tick, channels); - NBN_GameClient_Stop(); - free(channels); + int active_out_msg = NBN_GameClient_GetActiveOutgoingMessageCount(); + bool leak = false; - unsigned int created_outgoing_message_count = Soak_GetCreatedOutgoingSoakMessageCount(); - unsigned int destroyed_outgoing_message_count = Soak_GetDestroyedOutgoingSoakMessageCount(); - unsigned int created_incoming_message_count = Soak_GetCreatedIncomingSoakMessageCount(); - unsigned int destroyed_incoming_message_count = Soak_GetDestroyedIncomingSoakMessageCount(); + if (active_out_msg > 0) { + Soak_LogError("Outgoing message leak detected: %d", active_out_msg); + leak = true; + } - Soak_LogInfo("Outgoing soak messages created: %d", created_outgoing_message_count); - Soak_LogInfo("Outgoing soak messages destroyed: %d", destroyed_outgoing_message_count); - Soak_LogInfo("Incoming soak messages created: %d", created_incoming_message_count); - Soak_LogInfo("Incoming soak messages destroyed: %d", destroyed_incoming_message_count); + int active_inc_buffer_count = NBN_GameClient_GetActiveIncomingMessageBufferCount(); - if (created_outgoing_message_count != destroyed_outgoing_message_count) - { - Soak_LogError("created_outgoing_message_count != destroyed_outgoing_message_count (potential memory leak !)"); - return 1; + if (active_inc_buffer_count > 0) { + Soak_LogError("Incoming message buffer leak detected: %d", active_inc_buffer_count); + leak = true; } - if (created_incoming_message_count != destroyed_incoming_message_count) - { - Soak_LogError("created_incoming_message_count != destroyed_incoming_message_count (potential memory leak !)"); - return 1; + int active_out_buffer_count = NBN_GameClient_GetActiveOutgoingMessageCount(); + + if (active_out_buffer_count > 0) { + Soak_LogError("Outgoing message buffer leak detected: %d", active_out_buffer_count); + leak = true; } - Soak_LogInfo("No memory leak detected! Cool... cool cool cool"); + NBN_GameClient_Stop(); + free(channels); + + if (!leak) { + Soak_LogInfo("No memory leak detected! Cool... cool cool cool"); + } #ifdef WEBRTC_NATIVE - if (options.webrtc) - { + if (options.webrtc) { NBN_WebRTC_C_Unregister(); } diff --git a/soak/server.c b/soak/server.c index 7c6a831..8ea7be6 100644 --- a/soak/server.c +++ b/soak/server.c @@ -40,30 +40,29 @@ #endif // __EMSCRIPTEN__ -typedef struct -{ +typedef struct { uint8_t channel_id; - SoakMessage *msg; + unsigned int msg_id; + uint8_t data[SOAK_MESSAGE_BIG_MAX_LENGTH]; + unsigned int length; } Soak_MessageEntry; -typedef struct -{ +typedef struct { unsigned int head; unsigned int tail; unsigned int count; Soak_MessageEntry messages[SOAK_CLIENT_MAX_PENDING_MESSAGES]; } EchoMessageQueue; -typedef struct -{ +typedef struct { + uint8_t id; unsigned int recved_messages_count; unsigned int last_recved_message_id; EchoMessageQueue echo_queue; } SoakChannel; -typedef struct -{ - NBN_ConnectionHandle connection_handle; +typedef struct { + NBN_Connection *conn; bool error; bool is_closed; SoakChannel *channels; @@ -72,10 +71,8 @@ typedef struct static SoakClient *clients[SOAK_MAX_CLIENTS] = {NULL}; static unsigned int client_count = 0; -static void HandleNewConnection(void) -{ - if (client_count == SOAK_MAX_CLIENTS) - { +static void HandleNewConnection(void) { + if (client_count == SOAK_MAX_CLIENTS) { NBN_LogInfo("Connection rejected"); NBN_GameServer_RejectIncomingConnectionWithCode(SOAK_SERVER_FULL_CODE); @@ -83,24 +80,24 @@ static void HandleNewConnection(void) return; } - NBN_ConnectionHandle connection_handle = NBN_GameServer_GetIncomingConnection(); + NBN_Connection *conn = NBN_GameServer_GetIncomingConnection(); - assert(clients[connection_handle - 1] == NULL); + assert(clients[conn->id - 1] == NULL); NBN_GameServer_AcceptIncomingConnection(); SoakClient *soak_client = (SoakClient *)malloc(sizeof(SoakClient)); unsigned int channel_count = Soak_GetOptions().channel_count; - soak_client->connection_handle = connection_handle; + soak_client->conn = conn; soak_client->error = false; soak_client->is_closed = false; soak_client->channels = (SoakChannel *)malloc(sizeof(SoakChannel) * channel_count); - for (unsigned int i = 0; i < channel_count; i++) - { - SoakChannel *channel = &soak_client->channels[i]; + for (unsigned int c = 0; c < channel_count; c++) { + SoakChannel *channel = &soak_client->channels[c]; + channel->id = 2 + c; channel->recved_messages_count = 0; channel->last_recved_message_id = 0; @@ -112,58 +109,54 @@ static void HandleNewConnection(void) memset(channel->echo_queue.messages, 0, sizeof(channel->echo_queue.messages)); } - clients[connection_handle - 1] = soak_client; - client_count++; + clients[conn->id - 1] = soak_client; + client_count++; - Soak_LogInfo("Client has connected (ID: %d)", soak_client->connection_handle); + Soak_LogInfo("Client has connected (ID: %d)", soak_client->conn->id); } -static void HandleClientDisconnection(NBN_ConnectionHandle connection_handle) -{ - SoakClient *soak_client = clients[connection_handle - 1]; +static void HandleClientDisconnection(NBN_DisconnectionInfo info) { + SoakClient *soak_client = clients[info.conn_id - 1]; assert(soak_client != NULL); - Soak_LogInfo("Client has disconnected (ID: %d)", connection_handle); + Soak_LogInfo("Client has disconnected (ID: %d)", info.conn_id); free(soak_client->channels); free(soak_client); - clients[connection_handle - 1] = NULL; + clients[info.conn_id - 1] = NULL; client_count--; } -static void EchoReceivedSoakMessages(void) -{ +static void EchoReceivedSoakMessages(void) { unsigned int channel_count = Soak_GetOptions().channel_count; - for (unsigned int i = 0; i < SOAK_MAX_CLIENTS; i++) - { + for (unsigned int i = 0; i < SOAK_MAX_CLIENTS; i++) { SoakClient *soak_client = clients[i]; if (soak_client == NULL || soak_client->is_closed) continue; - for (unsigned int c = 0; c < channel_count; c++) - { + for (unsigned int c = 0; c < channel_count; c++) { SoakChannel *channel = &soak_client->channels[c]; - while (channel->echo_queue.count > 0) - { + while (channel->echo_queue.count > 0) { Soak_MessageEntry *msg_entry = &channel->echo_queue.messages[channel->echo_queue.head]; - assert(msg_entry->msg); - assert(!msg_entry->msg->outgoing); + NBN_GameServer_CreateMessage(SOAK_MESSAGE_SMALL, channel->id); // TODO: support big + NBN_Writer *writer = NBN_GameServer_GetMessageWriter(); - SoakMessage *echo_msg = SoakMessage_CreateOutgoing(); + SoakMessage_Write(writer, msg_entry->msg_id, msg_entry->data, msg_entry->length); - if (echo_msg == NULL) - { - Soak_LogError("Failed to create soak message"); + Soak_LogInfo("Send soak message %d's echo (length: %d) to client %d", msg_entry->msg_id, + msg_entry->length, soak_client->conn->id); - if (NBN_GameServer_CloseClient(soak_client->connection_handle) < 0) - { - Soak_LogError("Failed to close client %d", soak_client->connection_handle); + if (NBN_GameServer_SendMessageTo(soak_client->conn) < 0) { + Soak_LogError("Failed to send soak message to client %d, closing client", soak_client->conn->id); + + if (NBN_GameServer_CloseClient(soak_client->conn) < 0) { + Soak_LogError("Failed to close client %d", soak_client->conn->id); abort(); } @@ -171,19 +164,8 @@ static void EchoReceivedSoakMessages(void) return; } - echo_msg->id = msg_entry->msg->id; - echo_msg->data_length = msg_entry->msg->data_length; - - memcpy(echo_msg->data, msg_entry->msg->data, msg_entry->msg->data_length); - - Soak_LogInfo("Send soak message %d's echo to client %d", echo_msg->id, soak_client->connection_handle); - - if (NBN_GameServer_SendMessageTo(soak_client->connection_handle, SOAK_MESSAGE, msg_entry->channel_id, echo_msg) < 0) - break; - - SoakMessage_Destroy(msg_entry->msg); + msg_entry->length = 0; - msg_entry->msg = NULL; channel->echo_queue.head = (channel->echo_queue.head + 1) % SOAK_CLIENT_MAX_PENDING_MESSAGES; channel->echo_queue.count--; } @@ -191,110 +173,117 @@ static void EchoReceivedSoakMessages(void) } } -static int HandleReceivedSoakMessage(SoakMessage *msg, NBN_ConnectionHandle sender, uint8_t channel_id) -{ - SoakClient *soak_client = clients[sender - 1]; +static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_Connection *sender, uint8_t channel_id) { + SoakClient *soak_client = clients[sender->id - 1]; if (!soak_client || soak_client->error) return 0; - SoakChannel *channel = &soak_client->channels[channel_id]; + SoakChannel *channel = &soak_client->channels[channel_id - 2]; + + unsigned int msg_id; + unsigned int data_length; + static uint8_t recv_buffer[SOAK_MESSAGE_BIG_MAX_DATA_LENGTH]; + + if (SoakMessage_Read(reader, &msg_id, recv_buffer, &data_length) < 0) { + Soak_LogError("Failed to read soak message"); + + return -1; + } - if (msg->id != channel->last_recved_message_id + 1) - { + if (msg_id != channel->last_recved_message_id + 1) { Soak_LogError("Expected to receive message %d but received message %d (from client: %d)", - channel->last_recved_message_id + 1, msg->id, sender); + channel->last_recved_message_id + 1, msg_id, sender); soak_client->error = true; return -1; } - Soak_LogInfo("Received soak message %d from client %d on channel %d", msg->id, sender, channel_id); + Soak_LogInfo("Received soak message %d (length: %d) from client %d on channel %d", msg_id, data_length, sender->id, + channel_id); channel->recved_messages_count++; - channel->last_recved_message_id = msg->id; + channel->last_recved_message_id = msg_id; Soak_MessageEntry *msg_entry = &channel->echo_queue.messages[channel->echo_queue.tail]; assert(channel->echo_queue.count < SOAK_CLIENT_MAX_PENDING_MESSAGES); - assert(!msg_entry->msg); + assert(msg_entry->length == 0); - Soak_LogInfo("Enqueue soak message %d's echo for client %d on channel %d", msg->id, soak_client->connection_handle, channel_id); + Soak_LogInfo("Enqueue soak message %d's echo for client %d on channel %d", msg_id, soak_client->conn->id, + channel_id); - msg_entry->msg = msg; + memcpy(msg_entry->data, recv_buffer, data_length); + msg_entry->msg_id = msg_id; + msg_entry->length = data_length; msg_entry->channel_id = channel_id; + channel->echo_queue.tail = (channel->echo_queue.tail + 1) % SOAK_CLIENT_MAX_PENDING_MESSAGES; channel->echo_queue.count++; return 0; } -static void HandleReceivedMessage(void) -{ - NBN_MessageInfo msg = NBN_GameServer_GetMessageInfo(); - SoakClient *soak_client = clients[msg.sender - 1]; - - switch (msg.type) - { - case SOAK_MESSAGE: - if (HandleReceivedSoakMessage((SoakMessage *)msg.data, msg.sender, msg.channel_id) < 0) - { - if (NBN_GameServer_CloseClient(msg.sender) < 0) - { - Soak_LogError("Failed to close client %d", msg.sender); - abort(); - } +static void HandleReceivedMessage(void) { + NBN_MessageInfo msg_info = NBN_GameServer_GetMessageInfo(); + NBN_Reader *reader = NBN_GameServer_GetMessageReader(); + SoakClient *soak_client = clients[msg_info.sender->id - 1]; - soak_client->is_closed = true; - } - break; - - default: - Soak_LogError("Received unexpected message (type: %d, channel_id: %d)", msg.type, msg.channel_id); - - if (NBN_GameServer_CloseClient(msg.sender) < 0) - { - Soak_LogError("Failed to close client %d", msg.sender); + switch (msg_info.type) { + case SOAK_MESSAGE_SMALL: + if (HandleReceivedSoakMessage(reader, msg_info.sender, msg_info.channel_id) < 0) { + if (NBN_GameServer_CloseClient(msg_info.sender) < 0) { + Soak_LogError("Failed to close client %d", msg_info.sender->id); abort(); } soak_client->is_closed = true; - break; + } + break; + + // TODO: support big messages + + default: + Soak_LogError("Received unexpected message (type: %d, channel_id: %d)", msg_info.type, msg_info.channel_id); + + if (NBN_GameServer_CloseClient(msg_info.sender) < 0) { + Soak_LogError("Failed to close client %d", msg_info.sender->id); + abort(); + } + + soak_client->is_closed = true; + break; } } -static int Tick(void *data) -{ +static int Tick(void *data) { (void)data; int ev; - while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) - { + while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) { if (ev < 0) return -1; - switch (ev) - { - case NBN_NEW_CONNECTION: - HandleNewConnection(); - break; + switch (ev) { + case NBN_NEW_CONNECTION: + HandleNewConnection(); + break; - case NBN_CLIENT_DISCONNECTED: - HandleClientDisconnection(NBN_GameServer_GetDisconnectedClient()); - break; + case NBN_CLIENT_DISCONNECTED: + HandleClientDisconnection(NBN_GameServer_GetDisconnectionInfo()); + break; - case NBN_CLIENT_MESSAGE_RECEIVED: - HandleReceivedMessage(); - break; + case NBN_CLIENT_MESSAGE_RECEIVED: + HandleReceivedMessage(); + break; } } EchoReceivedSoakMessages(); - if (NBN_GameServer_SendPackets() < 0) - { + if (NBN_GameServer_SendPackets() < 0) { Soak_LogError("Failed to flush game server send queue. Exit"); return -1; @@ -303,39 +292,40 @@ static int Tick(void *data) return 0; } -static void SigintHandler(int dummy) -{ - unsigned int created_outgoing_message_count = Soak_GetCreatedOutgoingSoakMessageCount(); - unsigned int destroyed_outgoing_message_count = Soak_GetDestroyedOutgoingSoakMessageCount(); - unsigned int created_incoming_message_count = Soak_GetCreatedIncomingSoakMessageCount(); - unsigned int destroyed_incoming_message_count = Soak_GetDestroyedIncomingSoakMessageCount(); - - Soak_LogInfo("Outgoing soak messages created: %d", created_outgoing_message_count); - Soak_LogInfo("Outgoing soak messages destroyed: %d", destroyed_outgoing_message_count); - Soak_LogInfo("Incoming soak messages created: %d", created_incoming_message_count); - Soak_LogInfo("Incoming soak messages destroyed: %d", destroyed_incoming_message_count); - - if (created_outgoing_message_count != destroyed_outgoing_message_count) - { - Soak_LogError("created_outgoing_message_count != destroyed_outgoing_message_count (potential memory leak !)"); +static void SigintHandler(int dummy) { + int active_out_msg = NBN_GameServer_GetActiveOutgoingMessageCount(); + bool leak = false; + + if (active_out_msg > 0) { + Soak_LogError("Outgoing message leak detected: %d", active_out_msg); + leak = true; } - else if (created_incoming_message_count != destroyed_incoming_message_count) - { - Soak_LogError("created_incoming_message_count != destroyed_incoming_message_count (potential memory leak !)"); + + int active_inc_buffer_count = NBN_GameServer_GetActiveIncomingMessageBufferCount(); + + if (active_inc_buffer_count > 0) { + Soak_LogError("Incoming message buffer leak detected: %d", active_inc_buffer_count); + leak = true; + } + + int active_out_buffer_count = NBN_GameServer_GetActiveOutgoingMessageCount(); + + if (active_out_buffer_count > 0) { + Soak_LogError("Outgoing message buffer leak detected: %d", active_out_buffer_count); + leak = true; } - else - { + + if (!leak) { Soak_LogInfo("No memory leak detected! Cool... cool cool cool"); } Soak_Stop(); } -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { signal(SIGINT, SigintHandler); - Soak_SetLogLevel(LOG_TRACE); + Soak_SetLogLevel(LOG_DEBUG); if (Soak_ReadCommandLine(argc, argv) < 0) return -1; @@ -348,34 +338,38 @@ int main(int argc, char *argv[]) #ifdef WEBRTC_NATIVE // Register native WebRTC driver - const char *ice_servers[] = { "stun:stun01.sipphone.com" }; - NBN_WebRTC_C_Config cfg = { - .ice_servers = ice_servers, - .ice_servers_count = 1, - .enable_tls = false, - .cert_path = NULL, - .key_path = NULL, - .passphrase = NULL, - .log_level = RTC_LOG_VERBOSE}; + const char *ice_servers[] = {"stun:stun01.sipphone.com"}; + NBN_WebRTC_C_Config cfg = {.ice_servers = ice_servers, + .ice_servers_count = 1, + .enable_tls = false, + .cert_path = NULL, + .key_path = NULL, + .passphrase = NULL, + .log_level = RTC_LOG_VERBOSE}; NBN_WebRTC_C_Register(cfg); #endif // WEBRTC_NATIVE - if (NBN_GameServer_Start(SOAK_PROTOCOL_NAME, SOAK_PORT)) - { + SoakOptions options = Soak_GetOptions(); + NBN_GameServer_Config config = + NBN_GameServer_CreateConfig(SOAK_PROTOCOL_NAME, SOAK_PORT, AllocateMessage, DeallocateMessage); + + NBN_GameServer_EnableCustomChannels(&config, options.channel_count); + + if (NBN_GameServer_Start(config)) { Soak_LogError("Failed to start game server"); return 1; } - if (Soak_Init(argc, argv) < 0) - { + if (Soak_Init(argc, argv) < 0) { Soak_LogError("Failed to initialize soak test"); return 1; } - NBN_GameServer_Debug_RegisterCallback(NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE, (void *)Soak_Debug_PrintAddedToRecvQueue); + NBN_GameServer_Debug_RegisterCallback(NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE, + (void *)Soak_Debug_PrintAddedToRecvQueue); int ret = Soak_MainLoop(Tick, NULL); diff --git a/soak/soak.c b/soak/soak.c index e190962..d579684 100644 --- a/soak/soak.c +++ b/soak/soak.c @@ -22,8 +22,8 @@ */ -#include #include +#include #ifdef __EMSCRIPTEN__ #include @@ -32,8 +32,8 @@ #include #endif -#include "soak.h" #include "cargs.h" +#include "soak.h" static bool running = true; static SoakOptions soak_options = {0}; @@ -42,8 +42,7 @@ static unsigned int created_incoming_soak_message_count = 0; static unsigned int destroyed_outgoing_soak_message_count = 0; static unsigned int destroyed_incoming_soak_message_count = 0; -static void Usage(void) -{ +static void Usage(void) { #ifdef SOAK_CLIENT #ifdef WEBRTC_NATIVE @@ -57,65 +56,44 @@ static void Usage(void) #endif // SOAK_CLIENT #ifdef SOAK_SERVER - printf("Usage: server --channel_count= [--packet_loss=] \ + printf("Usage: server --channel_count= [--packet_loss=] \ [--packet_duplication=] [--ping=] [--jitter=]\n"); #endif } -int Soak_Init(int argc, char *argv[]) -{ +int Soak_Init(int argc, char *argv[]) { srand(SOAK_SEED); SoakOptions options = Soak_GetOptions(); Soak_LogInfo("Soak test initialized (Packet loss: %f, Packet duplication: %f, Ping: %f, Jitter: %f)", - options.packet_loss, options.packet_duplication, options.ping, options.jitter); - -#ifdef SOAK_CLIENT - - NBN_GameClient_RegisterMessage(SOAK_MESSAGE, - (NBN_MessageBuilder)SoakMessage_CreateIncoming, - (NBN_MessageDestructor)SoakMessage_Destroy, - (NBN_MessageSerializer)SoakMessage_Serialize); - -#endif - -#ifdef SOAK_SERVER - - NBN_GameServer_RegisterMessage(SOAK_MESSAGE, - (NBN_MessageBuilder)SoakMessage_CreateIncoming, - (NBN_MessageDestructor)SoakMessage_Destroy, - (NBN_MessageSerializer)SoakMessage_Serialize); - -#endif + options.packet_loss, options.packet_duplication, options.ping, options.jitter); /* Packet simulator configuration */ #ifdef SOAK_CLIENT NBN_GameClient_SetPing(soak_options.ping); NBN_GameClient_SetJitter(soak_options.jitter); NBN_GameClient_SetPacketLoss(soak_options.packet_loss); - NBN_GameClient_SetPacketDuplication(soak_options.packet_duplication); + NBN_GameClient_SetPacketDuplication(soak_options.packet_duplication); #endif #ifdef SOAK_SERVER NBN_GameServer_SetPing(soak_options.ping); NBN_GameServer_SetJitter(soak_options.jitter); NBN_GameServer_SetPacketLoss(soak_options.packet_loss); - NBN_GameServer_SetPacketDuplication(soak_options.packet_duplication); + NBN_GameServer_SetPacketDuplication(soak_options.packet_duplication); #endif return 0; } -void Soak_Deinit(void) -{ +void Soak_Deinit(void) { Soak_LogInfo("Done."); Soak_LogInfo("Memory report:\n"); // TODO } -int Soak_ReadCommandLine(int argc, char *argv[]) -{ +int Soak_ReadCommandLine(int argc, char *argv[]) { struct cag_option options[] = { #ifdef SOAK_CLIENT @@ -129,98 +107,72 @@ int Soak_ReadCommandLine(int argc, char *argv[]) #endif // SOAK_CLIENT - {'c', NULL, "channel_count", "VALUE", "Number of channels (1-NBN_MAX_CHANNELS)"}, + {'c', NULL, "channel_count", "VALUE", "Number of channels (1 - 8)"}, {'l', NULL, "packet_loss", "VALUE", "Packet loss frenquency (0-1)"}, {'d', NULL, "packet_duplication", "VALUE", "Packet duplication frequency (0-1)"}, {'p', NULL, "ping", "VALUE", "Ping in seconds"}, - {'j', NULL, "jitter", "VALUE", "Jitter in seconds"} - }; + {'j', NULL, "jitter", "VALUE", "Jitter in seconds"}}; cag_option_context context; cag_option_prepare(&context, options, CAG_ARRAY_SIZE(options), argc, argv); - while (cag_option_fetch(&context)) - { + while (cag_option_fetch(&context)) { char option = cag_option_get(&context); #ifdef SOAK_CLIENT - if (option == 'm') - { + if (option == 'm') { const char *val = cag_option_get_value(&context); - if (val) - { + if (val) { soak_options.message_count = atoi(val); } - } - else if (option == 'w') - { + } else if (option == 'w') { soak_options.webrtc = true; } #else - if (false) {} + if (false) { + } #endif - else if (option == 'c') - { + else if (option == 'c') { const char *val = cag_option_get_value(&context); - if (val) - { + if (val) { soak_options.channel_count = atoi(val); } - } - else if (option == 'l') - { + } else if (option == 'l') { soak_options.packet_loss = atof(cag_option_get_value(&context)); - } - else if (option == 'd') - { + } else if (option == 'd') { soak_options.packet_duplication = atof(cag_option_get_value(&context)); - } - else if (option == 'p') - { + } else if (option == 'p') { soak_options.ping = atof(cag_option_get_value(&context)); - } - else if (option == 'j') - { + } else if (option == 'j') { soak_options.jitter = atof(cag_option_get_value(&context)); } } - if (soak_options.channel_count <= 0) - { + if (soak_options.channel_count <= 0) { Usage(); return -1; } - if (soak_options.channel_count > NBN_MAX_CHANNELS - NBN_LIBRARY_RESERVED_CHANNELS) - { - Soak_LogError("Channel count cannot exceed %d", NBN_MAX_CHANNELS - NBN_LIBRARY_RESERVED_CHANNELS); + if (soak_options.channel_count > NBN_MAX_CUSTOM_CHANNELS) { + Soak_LogError("Channel count cannot exceed %d", NBN_MAX_CUSTOM_CHANNELS); return -1; } #ifdef SOAK_CLIENT - if (soak_options.message_count <= 0) - { + if (soak_options.message_count <= 0) { Usage(); return -1; } #endif - if (soak_options.channel_count > SOAK_MAX_CHANNELS) - { - Soak_LogError("Too many channels (max: %d)", SOAK_MAX_CHANNELS); - return -1; - } - return 0; } -int Soak_MainLoop(int (*Tick)(void *), void *data) -{ - while (running) - { +int Soak_MainLoop(int (*Tick)(void *), void *data) { + while (running) { int ret = Tick(data); if (ret < 0) // Error @@ -235,7 +187,7 @@ int Soak_MainLoop(int (*Tick)(void *), void *data) Sleep(SOAK_TICK_DT * 1000); #else long nanos = SOAK_TICK_DT * 1e9; - struct timespec t = { .tv_sec = nanos / 999999999, .tv_nsec = nanos % 999999999 }; + struct timespec t = {.tv_sec = nanos / 999999999, .tv_nsec = nanos % 999999999}; nanosleep(&t, &t); #endif @@ -244,21 +196,16 @@ int Soak_MainLoop(int (*Tick)(void *), void *data) return 0; } -void Soak_Stop(void) -{ +void Soak_Stop(void) { running = false; Soak_LogInfo("Soak test stopped"); } -SoakOptions Soak_GetOptions(void) -{ - return soak_options; -} +SoakOptions Soak_GetOptions(void) { return soak_options; } -void Soak_Debug_PrintAddedToRecvQueue(NBN_Connection *conn, NBN_Message *msg) -{ - // FIXME +void Soak_Debug_PrintAddedToRecvQueue(NBN_Connection *conn, NBN_Message *msg) { + // FIXME: /*if (msg->header.type == NBN_MESSAGE_CHUNK_TYPE) { NBN_MessageChunk *chunk = (NBN_MessageChunk *)msg->data; @@ -275,69 +222,38 @@ void Soak_Debug_PrintAddedToRecvQueue(NBN_Connection *conn, NBN_Message *msg) }*/ } -unsigned int Soak_GetCreatedOutgoingSoakMessageCount(void) -{ - return created_outgoing_soak_message_count; -} - -unsigned int Soak_GetDestroyedOutgoingSoakMessageCount(void) -{ - return destroyed_outgoing_soak_message_count; -} - -unsigned int Soak_GetCreatedIncomingSoakMessageCount(void) -{ - return created_incoming_soak_message_count; -} +unsigned int Soak_GetCreatedOutgoingSoakMessageCount(void) { return created_outgoing_soak_message_count; } -unsigned int Soak_GetDestroyedIncomingSoakMessageCount(void) -{ - return destroyed_incoming_soak_message_count; -} - -SoakMessage *SoakMessage_CreateIncoming(void) -{ - SoakMessage *msg = (SoakMessage *)malloc(sizeof(SoakMessage)); +unsigned int Soak_GetDestroyedOutgoingSoakMessageCount(void) { return destroyed_outgoing_soak_message_count; } - msg->outgoing = false; +unsigned int Soak_GetCreatedIncomingSoakMessageCount(void) { return created_incoming_soak_message_count; } - created_incoming_soak_message_count++; +unsigned int Soak_GetDestroyedIncomingSoakMessageCount(void) { return destroyed_incoming_soak_message_count; } - return msg; +void SoakMessage_Write(NBN_Writer *writer, unsigned int msg_id, uint8_t *data, unsigned int data_length) { + NBN_Writer_WriteUInt32(writer, msg_id); + NBN_Writer_WriteUInt32(writer, data_length); + NBN_Writer_WriteBytes(writer, data, data_length); } -SoakMessage *SoakMessage_CreateOutgoing(void) -{ - SoakMessage *msg = (SoakMessage *)malloc(sizeof(SoakMessage)); - - msg->outgoing = true; - - created_outgoing_soak_message_count++; +int SoakMessage_Read(NBN_Reader *reader, unsigned int *msg_id, uint8_t *data, unsigned int *data_length) { + if (NBN_Reader_ReadUInt32(reader, msg_id) < 0) + return -1; + if (NBN_Reader_ReadUInt32(reader, data_length) < 0) + return -1; + if (NBN_Reader_ReadBytes(reader, data, *data_length) < 0) + return -1; - return msg; + return 0; } -void SoakMessage_Destroy(SoakMessage *msg) -{ - if (msg->outgoing) - { - destroyed_outgoing_soak_message_count++; - Soak_LogDebug("Destroying outgoing soak message (destroyed count: %d, created count: %d)", - destroyed_outgoing_soak_message_count, created_outgoing_soak_message_count); - } - else - { - destroyed_incoming_soak_message_count++; +uint8_t *AllocateMessage(uint8_t type, uint16_t *length) { + if (type == SOAK_MESSAGE_SMALL) { + *length = SOAK_MESSAGE_SMALL_MAX_DATA_LENGTH + 32; + return (uint8_t *)NBN_Allocator(*length); } - free(msg); + NBN_Abort(); } -int SoakMessage_Serialize(SoakMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->id, 0, UINT32_MAX); - NBN_SerializeUInt(stream, msg->data_length, 1, SOAK_MESSAGE_MAX_DATA_LENGTH); - NBN_SerializeBytes(stream, msg->data, msg->data_length); - - return 0; -} +void DeallocateMessage(uint8_t type, uint8_t *data) { NBN_Deallocator(data); } diff --git a/soak/soak.h b/soak/soak.h index c37a947..18f660f 100644 --- a/soak/soak.h +++ b/soak/soak.h @@ -27,13 +27,13 @@ #if defined(_WIN32) || defined(_WIN64) -#include #include +#include #endif -#include #include +#include #include "logging.h" @@ -50,36 +50,32 @@ #define SOAK_PORT 42043 #define SOAK_TICK_RATE 60 #define SOAK_TICK_DT (1.0 / SOAK_TICK_RATE) -#define SOAK_MESSAGE_MIN_DATA_LENGTH 50 -#define SOAK_MESSAGE_MAX_DATA_LENGTH 4096 -#define SOAK_BIG_MESSAGE_PERCENTAGE 25 -#define SOAK_MESSAGE 42 +#define SOAK_MESSAGE_HEADER_LENGTH 8 // 4 bytes for ID, 4 bytes for data length +#define SOAK_MESSAGE_SMALL_MIN_DATA_LENGTH 50 +#define SOAK_MESSAGE_SMALL_MAX_DATA_LENGTH 200 +#define SOAK_MESSAGE_BIG_MIN_DATA_LENGTH 1024 +#define SOAK_MESSAGE_BIG_MAX_DATA_LENGTH 4096 +#define SOAK_MESSAGE_SMALL_MAX_LENGTH (SOAK_MESSAGE_HEADER_LENGTH + SOAK_MESSAGE_SMALL_MAX_DATA_LENGTH) +#define SOAK_MESSAGE_BIG_MAX_LENGTH (SOAK_MESSAGE_HEADER_LENGTH + SOAK_MESSAGE_BIG_MAX_DATA_LENGTH) +#define SOAK_BIG_MESSAGE_PERCENTAGE 0 // TODO: chunks are currently unsupported +#define SOAK_MESSAGE_SMALL 42 +#define SOAK_MESSAGE_BIG 43 // may get chunked #define SOAK_SEED time(NULL) #define SOAK_DONE 1 #define SOAK_MAX_CLIENTS 256 #define SOAK_CLIENT_MAX_PENDING_MESSAGES 50 // max number of unacked messages at a time -#define SOAK_SERVER_FULL_CODE 42 -#define SOAK_MAX_CHANNELS (NBN_MAX_CHANNELS - 3) +#define SOAK_SERVER_FULL_CODE 1234 -typedef struct -{ +typedef struct { unsigned int message_count; unsigned int channel_count; - float packet_loss; /* 0 - 1 */ + float packet_loss; /* 0 - 1 */ float packet_duplication; /* 0 - 1 */ - float ping; /* in seconds */ - float jitter; /* in seconds */ - bool webrtc; /* use native WebRTC driver */ + float ping; /* in seconds */ + float jitter; /* in seconds */ + bool webrtc; /* use native WebRTC driver */ } SoakOptions; -typedef struct -{ - uint32_t id; - unsigned int data_length; - bool outgoing; - uint8_t data[SOAK_MESSAGE_MAX_DATA_LENGTH]; -} SoakMessage; - int Soak_Init(int, char *[]); void Soak_Deinit(void); int Soak_ReadCommandLine(int, char *[]); @@ -91,9 +87,9 @@ unsigned int Soak_GetCreatedOutgoingSoakMessageCount(void); unsigned int Soak_GetDestroyedOutgoingSoakMessageCount(void); unsigned int Soak_GetCreatedIncomingSoakMessageCount(void); unsigned int Soak_GetDestroyedIncomingSoakMessageCount(void); -SoakMessage *SoakMessage_CreateOutgoing(void); -SoakMessage *SoakMessage_CreateIncoming(void); -void SoakMessage_Destroy(SoakMessage *); -int SoakMessage_Serialize(SoakMessage *, NBN_Stream *); +void SoakMessage_Write(NBN_Writer *, unsigned int, uint8_t *, unsigned int); +int SoakMessage_Read(NBN_Reader *reader, unsigned int *msg_id, uint8_t *data, unsigned int *data_length); +uint8_t *AllocateMessage(uint8_t type, uint16_t *); +void DeallocateMessage(uint8_t type, uint8_t *data); #endif // SOAK_H_INCLUDED From 7b09fa8623051a570752a8ed53802dc21f7fc964 Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sat, 24 Jan 2026 17:31:11 +0100 Subject: [PATCH 08/85] refine API, custom message allocation --- nbnet.h | 282 +++++++++++----------- net_drivers/udp.h | 35 +-- net_drivers/webrtc.h | 206 +++++++--------- net_drivers/webrtc_c.h | 516 ++++++++++++++++------------------------- soak/CMakeLists.txt | 2 +- soak/client.c | 10 +- soak/server.c | 7 +- soak/soak.c | 19 +- soak/soak.h | 4 +- 9 files changed, 474 insertions(+), 607 deletions(-) diff --git a/nbnet.h b/nbnet.h index a9ac42b..cabbc17 100644 --- a/nbnet.h +++ b/nbnet.h @@ -48,18 +48,6 @@ #endif -#ifndef NBN_Allocator -#define NBN_Allocator malloc -#endif - -#ifndef NBN_Reallocator -#define NBN_Reallocator realloc -#endif - -#ifndef NBN_Deallocator -#define NBN_Deallocator free -#endif - #pragma region Declarations #ifndef NBN_Abort @@ -123,9 +111,6 @@ typedef struct NBN_ConnectionVector { #pragma endregion // NBN_ConnectionVector -typedef uint8_t *(*NBN_MessageAllocator)(uint8_t type, uint16_t *length); -typedef void (*NBN_MessageDeallocator)(uint8_t type, uint8_t *data); - #pragma region Serialization #define B_MASK(n) (1u << (n)) @@ -188,6 +173,7 @@ typedef struct NBN_Message { NBN_MessageType type; NBN_Connection *sender; unsigned int ref_count; + bool custom_allocator; uint8_t *data; } NBN_Message; @@ -226,6 +212,9 @@ typedef struct NBN_MessageInfo { NBN_Connection *sender; } NBN_MessageInfo; +typedef bool (*NBN_MessageAllocator)(NBN_MessageHeader, NBN_MessageType, uint8_t **); +typedef void (*NBN_MessageDeallocator)(NBN_MessageHeader, NBN_MessageType, uint8_t *); + #pragma endregion /* NBN_Message */ #pragma region NBN_Packet @@ -238,7 +227,7 @@ typedef struct NBN_MessageInfo { * of a UDP packet should be no larger than 1500 - 20 - 8 = 1472 bytes to avoid fragmentation. */ #define NBN_PACKET_MAX_SIZE 1400 -#define NBN_MAX_MESSAGES_PER_PACKET 255 /* Maximum value of uint8_t, see packet header */ +#define NBN_MAX_MESSAGES_PER_PACKET UINT8_MAX #define NBN_PACKET_HEADER_SIZE 13 @@ -477,22 +466,38 @@ bool NBN_EventQueue_IsEmpty(NBN_EventQueue *); #endif /* NBNET_WINDOWS */ #define NBN_GameClient_SetPing(v) \ - { nbn_game_client.endpoint.packet_simulator.ping = v; } + { \ + nbn_game_client.endpoint.packet_simulator.ping = v; \ + } #define NBN_GameClient_SetJitter(v) \ - { nbn_game_client.endpoint.packet_simulator.jitter = v; } + { \ + nbn_game_client.endpoint.packet_simulator.jitter = v; \ + } #define NBN_GameClient_SetPacketLoss(v) \ - { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } + { \ + nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; \ + } #define NBN_GameClient_SetPacketDuplication(v) \ - { nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; } + { \ + nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; \ + } #define NBN_GameServer_SetPing(v) \ - { nbn_game_server.endpoint.packet_simulator.ping = v; } + { \ + nbn_game_server.endpoint.packet_simulator.ping = v; \ + } #define NBN_GameServer_SetJitter(v) \ - { nbn_game_server.endpoint.packet_simulator.jitter = v; } + { \ + nbn_game_server.endpoint.packet_simulator.jitter = v; \ + } #define NBN_GameServer_SetPacketLoss(v) \ - { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } + { \ + nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; \ + } #define NBN_GameServer_SetPacketDuplication(v) \ - { nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; } + { \ + nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; \ + } typedef struct NBN_PacketSimulatorEntry NBN_PacketSimulatorEntry; @@ -614,6 +619,7 @@ typedef struct NBN_GameClient_Config { typedef struct NBN_GameClient { NBN_Endpoint endpoint; + NBN_GameClient_Config config; NBN_Connection *server_connection; bool is_connected; uint8_t server_data_buffer[NBN_SERVER_DATA_MAX_SIZE]; @@ -627,35 +633,31 @@ typedef struct NBN_GameClient { extern NBN_GameClient nbn_game_client; -// TODO: add doc about msg allocators /** - * Create a minimal configuration to start a client. + * Initialize the game client with minimal configuration. * * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be able * to communicate * @param host Host to connect to * @param port Port to connect to - * - * @return 0 when successully started, -1 otherwise */ -NBN_GameClient_Config NBN_GameClient_CreateConfig(const char *protocol_name, const char *host, uint16_t port, - NBN_MessageAllocator msg_allocator, - NBN_MessageDeallocator msg_deallocator); +void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port); // TODO: doc -void NBN_GameClient_EnableCustomChannels(NBN_GameClient_Config *config, unsigned int count); +void NBN_GameClient_EnableCustomChannels(unsigned int count); + +// TODO: doc +void NBN_GameClient_EnableCustomMessageAllocation(NBN_MessageAllocator allocator, NBN_MessageDeallocator deallocator); // TODO: doc NBN_Writer *NBN_GameClient_GetConnectionDataWriter(void); /** - * Start the game client with the provided configuration. - * - * @param config the configuration to use to start the client + * Start the game client. * * @return 0 when successully started, -1 otherwise */ -int NBN_GameClient_Start(NBN_GameClient_Config config); +int NBN_GameClient_Start(void); /** * Disconnect from the server. The client can be restarted by calling NBN_GameClient_Start or @@ -798,6 +800,7 @@ typedef struct NBN_GameServer_Config { typedef struct NBN_GameServer { NBN_Endpoint endpoint; + NBN_GameServer_Config config; NBN_ConnectionVector *clients; /* Vector of clients connections */ NBN_ConnectionListNode *closed_clients_head; NBN_GameServerStats stats; @@ -813,29 +816,26 @@ extern NBN_GameServer nbn_game_server; // TODO: add doc about msg allocators /** - * Create a minimal configuration to start a server. + * Initialize the game server with minimal configuration. * - * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be able - * to communicate + * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be + * able to communicate * @param port The port clients will connect to - * - * @return 0 when successully started, -1 otherwise */ -NBN_GameServer_Config NBN_GameServer_CreateConfig(const char *protocol_name, uint16_t port, - NBN_MessageAllocator msg_allocator, - NBN_MessageDeallocator msg_deallocator); +void NBN_GameServer_Init(const char *protocol_name, uint16_t port); // TODO: doc -void NBN_GameServer_EnableCustomChannels(NBN_GameServer_Config *config, unsigned int count); +void NBN_GameServer_EnableCustomChannels(unsigned int count); + +// TODO: doc +void NBN_GameServer_EnableCustomMessageAllocation(NBN_MessageAllocator allocator, NBN_MessageDeallocator deallocator); /** * Start the game server with the provided configuration. * - * @param config the configuration to use to start the server - * * @return 0 when successfully started, -1 otherwise */ -int NBN_GameServer_Start(NBN_GameServer_Config config); +int NBN_GameServer_Start(void); /** * Stop the game server and clean everything up. @@ -1085,7 +1085,7 @@ int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data); static void NBN_ConnectionVector_Grow(NBN_ConnectionVector *vector, unsigned int new_capacity); static NBN_ConnectionVector *NBN_ConnectionVector_Create(void) { - NBN_ConnectionVector *vector = (NBN_ConnectionVector *)NBN_Allocator(sizeof(NBN_ConnectionVector)); + NBN_ConnectionVector *vector = (NBN_ConnectionVector *)malloc(sizeof(NBN_ConnectionVector)); vector->connections = NULL; vector->capacity = 0; @@ -1096,8 +1096,8 @@ static NBN_ConnectionVector *NBN_ConnectionVector_Create(void) { } static void NBN_ConnectionVector_Destroy(NBN_ConnectionVector *vector) { - NBN_Deallocator(vector->connections); - NBN_Deallocator(vector); + free(vector->connections); + free(vector); } static void NBN_ConnectionVector_Add(NBN_ConnectionVector *vector, NBN_Connection *conn) { @@ -1138,8 +1138,7 @@ static uint32_t NBN_ConnectionVector_RemoveAt(NBN_ConnectionVector *vector, unsi } static void NBN_ConnectionVector_Grow(NBN_ConnectionVector *vector, unsigned int new_capacity) { - vector->connections = - (NBN_Connection **)NBN_Reallocator(vector->connections, sizeof(NBN_Connection *) * new_capacity); + vector->connections = (NBN_Connection **)realloc(vector->connections, sizeof(NBN_Connection *) * new_capacity); if (vector->connections == NULL) { NBN_LogError("Failed to allocate memory to grow the connection vector"); @@ -1415,11 +1414,11 @@ void NBN_Channel_Destroy(NBN_Endpoint *endpoint, NBN_Channel *channel) { } } - NBN_Deallocator(channel); + free(channel); } static NBN_Channel *Channel_Create(uint8_t id, NBN_ChannelType type) { - NBN_Channel *channel = NBN_Allocator(sizeof(NBN_Channel)); + NBN_Channel *channel = malloc(sizeof(NBN_Channel)); channel->id = id; channel->type = type; @@ -1687,8 +1686,6 @@ static unsigned int Channel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2) { #pragma region NBN_Connection static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *, uint8_t, uint8_t); -static uint8_t *Endpoint_AllocateMessage(NBN_Endpoint *, uint8_t type, uint16_t *length); -static void Endpoint_DeallocateMessage(NBN_Endpoint *endpoint, uint8_t type, uint8_t *data); static uint32_t Connection_BuildPacketAckBits(NBN_Connection *); static int Connection_DecodePacketHeader(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); @@ -1707,7 +1704,7 @@ static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *, double); // TODO: move this code into Endpoint_CreateConnection NBN_Connection *NBN_Connection_Create(uint32_t id, NBN_Endpoint *endpoint, NBN_Driver *driver, void *driver_data) { - NBN_Connection *connection = (NBN_Connection *)NBN_Allocator(sizeof(NBN_Connection)); + NBN_Connection *connection = (NBN_Connection *)malloc(sizeof(NBN_Connection)); connection->id = id; connection->endpoint = endpoint; @@ -1745,7 +1742,7 @@ void NBN_Connection_Destroy(NBN_Endpoint *endpoint, NBN_Connection *connection) NBN_Channel_Destroy(endpoint, channel); } - NBN_Deallocator(connection); + free(connection); } int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, @@ -1776,6 +1773,7 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection NBN_Message message = {0}; message.type = NBN_INCOMING_MESSAGE; + message.custom_allocator = false; int msg_len = Connection_ReadNextMessageFromBuffer(endpoint, &msg_reader, &message); if (msg_len < 0) { @@ -1940,8 +1938,8 @@ static uint32_t Connection_BuildPacketAckBits(NBN_Connection *connection) { for (int i = 0; i < 32; i++) { /* - when last_received_packet_seq_number is lower than 32, the value of acked_packet_seq_number will eventually - wrap around, which means the packets from before the wrap around will naturally be acked + when last_received_packet_seq_number is lower than 32, the value of acked_packet_seq_number will + eventually wrap around, which means the packets from before the wrap around will naturally be acked */ uint16_t acked_packet_seq_number = connection->last_received_packet_seq_number - (i + 1); @@ -2097,7 +2095,17 @@ static int Connection_ReadNextMessageFromBuffer(NBN_Endpoint *endpoint, NBN_Read } if (message->header.length > 0) { - message->data = NBN_Allocator(message->header.length); + bool custom_allocator = + endpoint->msg_allocator != NULL && endpoint->msg_allocator(message->header, message->type, &message->data); + + message->custom_allocator = custom_allocator; + + if (!custom_allocator) { + message->data = malloc(message->header.length); + } + + NBN_Assert(message->data != NULL); + endpoint->active_inc_msg_buffer_count++; if (NBN_Reader_ReadBytes(reader, message->data, message->header.length) < 0) { @@ -2119,20 +2127,27 @@ static void Endpoint_FreeMessage(NBN_Endpoint *endpoint, NBN_Message *message) { message->ref_count--; - if (message->ref_count > 0) { - return; - } + if (message->ref_count == 0) { + if (message->header.length > 0) { + if (message->custom_allocator) { + endpoint->msg_deallocator(message->header, message->type, message->data); + } else { + free(message->data); + } - if (message->header.length > 0) { - NBN_Deallocator(message->data); - endpoint->active_out_msg_buffer_count--; - } + endpoint->active_out_msg_buffer_count--; + } - NBN_Deallocator(message); - endpoint->active_out_msg_count--; + free(message); + endpoint->active_out_msg_count--; + } } else if (message->type == NBN_INCOMING_MESSAGE) { if (message->header.length > 0) { - NBN_Deallocator(message->data); + if (message->custom_allocator) { + endpoint->msg_deallocator(message->header, message->type, message->data); + } else { + free(message->data); + } endpoint->active_inc_msg_buffer_count--; } } @@ -2232,6 +2247,7 @@ static void Endpoint_Deinit(NBN_Endpoint *); static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, uint32_t, int, void *); static uint32_t Endpoint_BuildProtocolId(const char *); static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *, NBN_Packet *, NBN_Connection *); +static void Endpoint_CreateMessageBuffer(NBN_Endpoint *, NBN_Message *); static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN_Message *); static void Endpoint_UpdateTime(NBN_Endpoint *); @@ -2279,7 +2295,7 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_ NBN_Connection *connection = NBN_Connection_Create(id, endpoint, driver, driver_data); connection->channel_count = 2 + endpoint->custom_reliable_channels; - connection->channels = NBN_Allocator(sizeof(NBN_Channel *) * connection->channel_count); + connection->channels = malloc(sizeof(NBN_Channel *) * connection->channel_count); // create library channels connection->channels[NBN_CHANNEL_RESERVED_UNRELIABLE] = @@ -2323,9 +2339,8 @@ static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *pa } static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, uint8_t type, uint8_t channel_id) { - NBN_Message *message = (NBN_Message *)NBN_Allocator(sizeof(NBN_Message)); + NBN_Message *message = (NBN_Message *)malloc(sizeof(NBN_Message)); NBN_Assert(message != NULL); - NBN_Assert(message->ref_count == 0); endpoint->active_out_msg_count++; @@ -2334,6 +2349,7 @@ static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, uint8_t type, message->type = NBN_OUTGOING_MESSAGE; message->data = NULL; message->ref_count = 0; + message->custom_allocator = false; endpoint->message_writer.position = 0; message->data = endpoint->scratch_write_buffer; @@ -2344,12 +2360,8 @@ static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, uint8_t type, endpoint->write_message = message; } -static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Message *message) { - NBN_Assert(!connection->is_closed || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); - NBN_Assert(!connection->is_stale); - +static void Endpoint_CreateMessageBuffer(NBN_Endpoint *endpoint, NBN_Message *message) { uint8_t *scratch_buffer = message->data; - message->header.length = endpoint->message_writer.position; NBN_Assert(message->header.length <= NBN_PACKET_MAX_DATA_SIZE); @@ -2357,10 +2369,24 @@ static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connectio if (message->header.length == 0) { message->data = NULL; } else { - message->data = NBN_Allocator(message->header.length); + bool custom_allocator = + endpoint->msg_allocator != NULL && endpoint->msg_allocator(message->header, message->type, &message->data); + + message->custom_allocator = custom_allocator; + + if (!custom_allocator) { + message->data = malloc(message->header.length); + } + + NBN_Assert(message->data != NULL); } memcpy(message->data, scratch_buffer, message->header.length); +} + +static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Message *message) { + NBN_Assert(!connection->is_closed || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); + NBN_Assert(!connection->is_stale); NBN_Channel *channel = connection->channels[message->header.channel_id]; @@ -2395,26 +2421,6 @@ static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) { #endif // NBNET_WINDOWS } -static uint8_t *Endpoint_AllocateMessage(NBN_Endpoint *endpoint, uint8_t type, uint16_t *length) { - if (type >= NBN_MAX_MESSAGE_TYPES - (NBN_RESERVED_MESSAGE_TYPES - 1)) { - *length = NBN_RESERVED_MESSAGE_BUFFER_LEN; - - return NBN_Allocator(NBN_RESERVED_MESSAGE_BUFFER_LEN); - } - - uint8_t *data = endpoint->msg_allocator(type, length); - - return data; -} - -static void Endpoint_DeallocateMessage(NBN_Endpoint *endpoint, uint8_t type, uint8_t *data) { - if (type >= NBN_MAX_MESSAGE_TYPES - (NBN_RESERVED_MESSAGE_TYPES - 1)) { - NBN_Deallocator(data); - } - - endpoint->msg_deallocator(type, data); -} - #pragma endregion /* NBN_Endpoint */ #pragma region Network driver @@ -2467,23 +2473,24 @@ static int GameClient_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); static int GameClient_HandleEvent(void); static int GameClient_HandleMessageReceivedEvent(void); -NBN_GameClient_Config NBN_GameClient_CreateConfig(const char *protocol_name, const char *host, uint16_t port, - NBN_MessageAllocator msg_allocator, - NBN_MessageDeallocator msg_deallocator) { - NBN_GameClient_Config config = {.host = host, - .endpoint = {.protocol_name = protocol_name, - .port = port, - .custom_reliable_channels = 0, - .msg_allocator = msg_allocator, - .msg_deallocator = msg_deallocator}}; - - return config; +void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port) { + nbn_game_client.config = (NBN_GameClient_Config){.host = host, + .endpoint = {.protocol_name = protocol_name, + .port = port, + .custom_reliable_channels = 0, + .msg_allocator = NULL, + .msg_deallocator = NULL}}; } -void NBN_GameClient_EnableCustomChannels(NBN_GameClient_Config *config, unsigned int count) { +void NBN_GameClient_EnableCustomChannels(unsigned int count) { NBN_Assert(count <= NBN_MAX_CUSTOM_CHANNELS); - config->endpoint.custom_reliable_channels = count; + nbn_game_client.config.endpoint.custom_reliable_channels = count; +} + +void NBN_GameClient_EnableCustomMessageAllocation(NBN_MessageAllocator allocator, NBN_MessageDeallocator deallocator) { + nbn_game_client.config.endpoint.msg_allocator = allocator; + nbn_game_client.config.endpoint.msg_deallocator = deallocator; } NBN_Writer *NBN_GameClient_GetConnectionDataWriter(void) { @@ -2493,12 +2500,13 @@ NBN_Writer *NBN_GameClient_GetConnectionDataWriter(void) { return &nbn_game_client.client_data_writer; } -int NBN_GameClient_Start(NBN_GameClient_Config config) { +int NBN_GameClient_Start(void) { if (nbn_driver_count < 1) { NBN_LogError("At least one network driver has to be registered"); NBN_Abort(); } + NBN_GameClient_Config config = nbn_game_client.config; const char *protocol_name = config.endpoint.protocol_name; const char *host = config.host; uint16_t port = config.endpoint.port; @@ -2696,6 +2704,8 @@ int NBN_GameClient_SendMessage(void) { NBN_Assert(message != NULL); NBN_Assert(message->ref_count == 0); + Endpoint_CreateMessageBuffer(endpoint, message); + if (Endpoint_EnqueueOutgoingMessage(endpoint, nbn_game_client.server_connection, message) < 0) { NBN_LogError("Failed to create outgoing message"); @@ -2900,30 +2910,32 @@ static void GameServer_RemoveClosedClientConnections(void); static int GameServer_HandleEvent(void); static int GameServer_HandleMessageReceivedEvent(void); -NBN_GameServer_Config NBN_GameServer_CreateConfig(const char *protocol_name, uint16_t port, - NBN_MessageAllocator msg_allocator, - NBN_MessageDeallocator msg_deallocator) { - NBN_GameServer_Config config = {.endpoint = {.protocol_name = protocol_name, - .port = port, - .custom_reliable_channels = 0, - .msg_allocator = msg_allocator, - .msg_deallocator = msg_deallocator}}; - - return config; +void NBN_GameServer_Init(const char *protocol_name, uint16_t port) { + nbn_game_server.config = (NBN_GameServer_Config){.endpoint = {.protocol_name = protocol_name, + .port = port, + .custom_reliable_channels = 0, + .msg_allocator = NULL, + .msg_deallocator = NULL}}; } -void NBN_GameServer_EnableCustomChannels(NBN_GameServer_Config *config, unsigned int count) { +void NBN_GameServer_EnableCustomChannels(unsigned int count) { NBN_Assert(count <= NBN_MAX_CUSTOM_CHANNELS); - config->endpoint.custom_reliable_channels = count; + nbn_game_server.config.endpoint.custom_reliable_channels = count; +} + +void NBN_GameServer_EnableCustomMessageAllocation(NBN_MessageAllocator allocator, NBN_MessageDeallocator deallocator) { + nbn_game_server.config.endpoint.msg_allocator = allocator; + nbn_game_server.config.endpoint.msg_deallocator = deallocator; } -int NBN_GameServer_Start(NBN_GameServer_Config config) { +int NBN_GameServer_Start(void) { if (nbn_driver_count < 1) { NBN_LogError("At least one network driver has to be registered"); NBN_Abort(); } + NBN_GameServer_Config config = nbn_game_server.config; const char *protocol_name = config.endpoint.protocol_name; uint16_t port = config.endpoint.port; uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); @@ -2980,7 +2992,7 @@ void NBN_GameServer_Stop(void) { while (current) { NBN_ConnectionListNode *next = current->next; - NBN_Deallocator(current); + free(current); current = next; } @@ -3117,6 +3129,8 @@ int NBN_GameServer_SendMessageTo(NBN_Connection *conn) { NBN_Message *message = endpoint->write_message; NBN_Assert(message != NULL); + Endpoint_CreateMessageBuffer(endpoint, message); + return GameServer_SendMessageTo(conn, message); } @@ -3125,6 +3139,8 @@ int NBN_GameServer_BroadcastMessage(void) { NBN_Message *message = endpoint->write_message; NBN_Assert(message != NULL); + Endpoint_CreateMessageBuffer(endpoint, message); + for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { NBN_Connection *conn = nbn_game_server.clients->connections[i]; @@ -3348,7 +3364,7 @@ static void GameServer_AddClientToClosedList(NBN_Connection *client) { if (client->is_closed) return; - NBN_ConnectionListNode *node = (NBN_ConnectionListNode *)NBN_Allocator(sizeof(NBN_ConnectionListNode)); + NBN_ConnectionListNode *node = (NBN_ConnectionListNode *)malloc(sizeof(NBN_ConnectionListNode)); node->conn = client; node->next = NULL; @@ -3436,7 +3452,7 @@ static void GameServer_RemoveClosedClientConnections(void) { // Remove the connection from the closed clients list - NBN_Deallocator(current); + free(current); if (current == nbn_game_server.closed_clients_head) { // delete the head of the list @@ -3630,7 +3646,7 @@ int NBN_PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN jitter = (jitter > 0) ? (rand() % (jitter * 2)) - jitter : 0; - NBN_PacketSimulatorEntry *entry = (NBN_PacketSimulatorEntry *)NBN_Allocator(sizeof(NBN_PacketSimulatorEntry)); + NBN_PacketSimulatorEntry *entry = (NBN_PacketSimulatorEntry *)malloc(sizeof(NBN_PacketSimulatorEntry)); entry->delay = packet_simulator->ping + (double)jitter / 1000; /* and converted back to seconds */ entry->receiver = receiver; @@ -3744,7 +3760,7 @@ static void *PacketSimulator_Routine(void *arg) packet_simulator->packet_count--; // release the memory allocated for the entry - NBN_Deallocator(entry); + free(entry); entry = next; } diff --git a/net_drivers/udp.h b/net_drivers/udp.h index 0dac985..a42fc15 100644 --- a/net_drivers/udp.h +++ b/net_drivers/udp.h @@ -39,7 +39,10 @@ void NBN_UDP_Register(void); #include #include #include +#include #include +#include +#include #define NBN_UDP_DRIVER_ID 0 #define NBN_UDP_DRIVER_NAME "UDP" @@ -135,9 +138,9 @@ static NBN_UDP_HTable *NBN_UDP_HTable_Create(void) { } static NBN_UDP_HTable *NBN_UDP_HTable_CreateWithCapacity(unsigned int capacity) { - NBN_UDP_HTable *htable = (NBN_UDP_HTable *)NBN_Allocator(sizeof(NBN_UDP_HTable)); + NBN_UDP_HTable *htable = (NBN_UDP_HTable *)malloc(sizeof(NBN_UDP_HTable)); - htable->internal_array = (NBN_UDP_HTableEntry **)NBN_Allocator(sizeof(NBN_UDP_HTableEntry *) * capacity); + htable->internal_array = (NBN_UDP_HTableEntry **)malloc(sizeof(NBN_UDP_HTableEntry *) * capacity); htable->capacity = capacity; htable->count = 0; htable->load_factor = 0; @@ -153,15 +156,15 @@ static void NBN_UDP_HTable_Destroy(NBN_UDP_HTable *htable) { NBN_UDP_HTableEntry *entry = htable->internal_array[i]; if (entry) - NBN_Deallocator(entry); + free(entry); } - NBN_Deallocator(htable->internal_array); - NBN_Deallocator(htable); + free(htable->internal_array); + free(htable); } static void NBN_UDP_HTable_Add(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr, NBN_UDP_Connection *conn) { - NBN_UDP_HTableEntry *entry = (NBN_UDP_HTableEntry *)NBN_Allocator(sizeof(NBN_UDP_HTableEntry)); + NBN_UDP_HTableEntry *entry = (NBN_UDP_HTableEntry *)malloc(sizeof(NBN_UDP_HTableEntry)); entry->ip_addr = ip_addr; entry->conn = conn; @@ -207,7 +210,7 @@ static void NBN_UDP_HTable_InsertEntry(NBN_UDP_HTable *htable, NBN_UDP_HTableEnt static void NBN_UDP_HTable_RemoveEntry(NBN_UDP_HTable *htable, NBN_UDP_HTableEntry *entry) { htable->internal_array[entry->slot] = NULL; - NBN_Deallocator(entry); + free(entry); htable->count--; htable->load_factor = (float)htable->count / htable->capacity; @@ -234,7 +237,7 @@ static unsigned int NBN_UDP_HTable_FindFreeSlot(NBN_UDP_HTable *htable, NBN_UDP_ { *use_existing_slot = true; - NBN_Deallocator(current_entry); + free(current_entry); } return slot; @@ -268,7 +271,7 @@ static void NBN_UDP_HTable_Grow(NBN_UDP_HTable *htable) { unsigned int new_capacity = old_capacity * 2; NBN_UDP_HTableEntry **old_internal_array = htable->internal_array; NBN_UDP_HTableEntry **new_internal_array = - (NBN_UDP_HTableEntry **)NBN_Allocator(sizeof(NBN_UDP_HTableEntry *) * new_capacity); + (NBN_UDP_HTableEntry **)malloc(sizeof(NBN_UDP_HTableEntry *) * new_capacity); for (unsigned int i = 0; i < new_capacity; i++) { new_internal_array[i] = NULL; @@ -286,7 +289,7 @@ static void NBN_UDP_HTable_Grow(NBN_UDP_HTable *htable) { NBN_UDP_HTable_InsertEntry(htable, old_internal_array[i]); } - NBN_Deallocator(old_internal_array); + free(old_internal_array); } static unsigned long NBN_UDP_HTable_HashSDBM(NBN_IPAddress ip_addr) { return ip_addr.host ^ ip_addr.port; } @@ -367,7 +370,7 @@ static int BindSocket(uint16_t port) { static int ResolveIpAddress(const char *host, uint16_t port, NBN_IPAddress *address) { size_t host_len = strlen(host); - char *dup_host = (char *)NBN_Allocator(host_len + 1); + char *dup_host = (char *)malloc(host_len + 1); memcpy(dup_host, host, host_len + 1); uint8_t arr[4]; @@ -390,7 +393,7 @@ static int ResolveIpAddress(const char *host, uint16_t port, NBN_IPAddress *addr address->host = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3]; address->port = port; - NBN_Deallocator(dup_host); + free(dup_host); return 0; } @@ -487,7 +490,7 @@ static void NBN_UDP_ServRemoveClientConnection(NBN_Connection *connection) { if (udp_conn) { NBN_LogDebug("Destroyed UDP connection %d", connection->id); - NBN_Deallocator(udp_conn); + free(udp_conn); } } @@ -519,7 +522,7 @@ static NBN_Connection *FindOrCreateClientConnectionByAddress(NBN_IPAddress addre if (GameServer_GetClientCount() >= NBN_MAX_CLIENTS) return NULL; - udp_conn = (NBN_UDP_Connection *)NBN_Allocator(sizeof(NBN_UDP_Connection)); + udp_conn = (NBN_UDP_Connection *)malloc(sizeof(NBN_UDP_Connection)); udp_conn->id = nbn_udp_serv.next_conn_id++; udp_conn->address = address; @@ -557,7 +560,7 @@ static NBN_UDP_Client nbn_udp_cli = {NULL, 0}; static int ResolveIpAddress(const char *, uint16_t, NBN_IPAddress *); static int NBN_UDP_CliStart(uint32_t protocol_id, const char *host, uint16_t port) { - NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)NBN_Allocator(sizeof(NBN_Connection)); + NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)malloc(sizeof(NBN_Connection)); nbn_udp_cli.protocol_id = protocol_id; @@ -581,7 +584,7 @@ static int NBN_UDP_CliStart(uint32_t protocol_id, const char *host, uint16_t por static void NBN_UDP_CliStop(void) { NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)nbn_udp_cli.server_conn->driver_data; - NBN_Deallocator(udp_conn); + free(udp_conn); DeinitSocket(); } diff --git a/net_drivers/webrtc.h b/net_drivers/webrtc.h index 144fae7..95204c5 100644 --- a/net_drivers/webrtc.h +++ b/net_drivers/webrtc.h @@ -28,11 +28,13 @@ freely, subject to the following restrictions: How to use: 1. Include this header *once* after the nbnet header in the same file where you defined the NBNET_IMPL macro - 2. Call NBN_WebRTC_Register in both your client and server code before calling NBN_GameClient_Start or NBN_GameServer_Start + 2. Call NBN_WebRTC_Register in both your client and server code before calling NBN_GameClient_Start or + NBN_GameServer_Start */ -typedef struct NBN_WebRTC_Config -{ +#include + +typedef struct NBN_WebRTC_Config { bool enable_tls; const char *cert_path; const char *key_path; @@ -43,11 +45,11 @@ void NBN_WebRTC_Register(NBN_WebRTC_Config config); #ifdef NBNET_IMPL #if !defined(EXTERN_C) - #if defined(__cplusplus) - #define NBN_EXTERN extern "C" - #else - #define NBN_EXTERN extern - #endif +#if defined(__cplusplus) +#define NBN_EXTERN extern "C" +#else +#define NBN_EXTERN extern +#endif #endif #include @@ -55,8 +57,7 @@ void NBN_WebRTC_Register(NBN_WebRTC_Config config); #define NBN_WEBRTC_DRIVER_ID 1 #define NBN_WEBRTC_DRIVER_NAME "WebRTC" -typedef struct -{ +typedef struct { uint32_t id; NBN_Connection *conn; } NBN_WebRTC_Peer; @@ -66,15 +67,13 @@ typedef struct #define HTABLE_DEFAULT_INITIAL_CAPACITY 32 #define HTABLE_LOAD_FACTOR_THRESHOLD 0.75 -typedef struct -{ +typedef struct { uint32_t peer_id; NBN_WebRTC_Peer *peer; unsigned int slot; } NBN_WebRTC_HTableEntry; -typedef struct -{ +typedef struct { NBN_WebRTC_HTableEntry **internal_array; unsigned int capacity; unsigned int count; @@ -93,16 +92,14 @@ static unsigned int NBN_WebRTC_HTable_FindFreeSlot(NBN_WebRTC_HTable *, NBN_WebR static NBN_WebRTC_HTableEntry *NBN_WebRTC_HTable_FindEntry(NBN_WebRTC_HTable *, uint32_t); static void NBN_WebRTC_HTable_Grow(NBN_WebRTC_HTable *); -static NBN_WebRTC_HTable *NBN_WebRTC_HTable_Create(void) -{ +static NBN_WebRTC_HTable *NBN_WebRTC_HTable_Create(void) { return NBN_WebRTC_HTable_CreateWithCapacity(HTABLE_DEFAULT_INITIAL_CAPACITY); } -static NBN_WebRTC_HTable *NBN_WebRTC_HTable_CreateWithCapacity(unsigned int capacity) -{ - NBN_WebRTC_HTable *htable = (NBN_WebRTC_HTable*)NBN_Allocator(sizeof(NBN_WebRTC_HTable)); +static NBN_WebRTC_HTable *NBN_WebRTC_HTable_CreateWithCapacity(unsigned int capacity) { + NBN_WebRTC_HTable *htable = (NBN_WebRTC_HTable *)malloc(sizeof(NBN_WebRTC_HTable)); - htable->internal_array = (NBN_WebRTC_HTableEntry**)NBN_Allocator(sizeof(NBN_WebRTC_HTableEntry *) * capacity); + htable->internal_array = (NBN_WebRTC_HTableEntry **)malloc(sizeof(NBN_WebRTC_HTableEntry *) * capacity); htable->capacity = capacity; htable->count = 0; htable->load_factor = 0; @@ -113,23 +110,20 @@ static NBN_WebRTC_HTable *NBN_WebRTC_HTable_CreateWithCapacity(unsigned int capa return htable; } -static void NBN_WebRTC_HTable_Destroy(NBN_WebRTC_HTable *htable) -{ - for (unsigned int i = 0; i < htable->capacity; i++) - { +static void NBN_WebRTC_HTable_Destroy(NBN_WebRTC_HTable *htable) { + for (unsigned int i = 0; i < htable->capacity; i++) { NBN_WebRTC_HTableEntry *entry = htable->internal_array[i]; if (entry) - NBN_Deallocator(entry); + free(entry); } - NBN_Deallocator(htable->internal_array); - NBN_Deallocator(htable); + free(htable->internal_array); + free(htable); } -static void NBN_WebRTC_HTable_Add(NBN_WebRTC_HTable *htable, uint32_t peer_id, NBN_WebRTC_Peer *peer) -{ - NBN_WebRTC_HTableEntry *entry = (NBN_WebRTC_HTableEntry*)NBN_Allocator(sizeof(NBN_WebRTC_HTableEntry)); +static void NBN_WebRTC_HTable_Add(NBN_WebRTC_HTable *htable, uint32_t peer_id, NBN_WebRTC_Peer *peer) { + NBN_WebRTC_HTableEntry *entry = (NBN_WebRTC_HTableEntry *)malloc(sizeof(NBN_WebRTC_HTableEntry)); entry->peer_id = peer_id; entry->peer = peer; @@ -140,19 +134,16 @@ static void NBN_WebRTC_HTable_Add(NBN_WebRTC_HTable *htable, uint32_t peer_id, N NBN_WebRTC_HTable_Grow(htable); } -static NBN_WebRTC_Peer *NBN_WebRTC_HTable_Get(NBN_WebRTC_HTable *htable, uint32_t peer_id) -{ +static NBN_WebRTC_Peer *NBN_WebRTC_HTable_Get(NBN_WebRTC_HTable *htable, uint32_t peer_id) { NBN_WebRTC_HTableEntry *entry = NBN_WebRTC_HTable_FindEntry(htable, peer_id); return entry ? entry->peer : NULL; } -static NBN_WebRTC_Peer *NBN_WebRTC_HTable_Remove(NBN_WebRTC_HTable *htable, uint32_t peer_id) -{ +static NBN_WebRTC_Peer *NBN_WebRTC_HTable_Remove(NBN_WebRTC_HTable *htable, uint32_t peer_id) { NBN_WebRTC_HTableEntry *entry = NBN_WebRTC_HTable_FindEntry(htable, peer_id); - if (entry) - { + if (entry) { NBN_WebRTC_Peer *peer = entry->peer; NBN_WebRTC_HTable_RemoveEntry(htable, entry); @@ -163,33 +154,30 @@ static NBN_WebRTC_Peer *NBN_WebRTC_HTable_Remove(NBN_WebRTC_HTable *htable, uint return NULL; } -static void NBN_WebRTC_HTable_InsertEntry(NBN_WebRTC_HTable *htable, NBN_WebRTC_HTableEntry *entry) -{ +static void NBN_WebRTC_HTable_InsertEntry(NBN_WebRTC_HTable *htable, NBN_WebRTC_HTableEntry *entry) { bool use_existing_slot = false; unsigned int slot = NBN_WebRTC_HTable_FindFreeSlot(htable, entry, &use_existing_slot); entry->slot = slot; htable->internal_array[slot] = entry; - if (!use_existing_slot) - { + if (!use_existing_slot) { htable->count++; htable->load_factor = (float)htable->count / htable->capacity; } } -static void NBN_WebRTC_HTable_RemoveEntry(NBN_WebRTC_HTable *htable, NBN_WebRTC_HTableEntry *entry) -{ +static void NBN_WebRTC_HTable_RemoveEntry(NBN_WebRTC_HTable *htable, NBN_WebRTC_HTableEntry *entry) { htable->internal_array[entry->slot] = NULL; - NBN_Deallocator(entry); + free(entry); htable->count--; htable->load_factor = htable->count / htable->capacity; } -static unsigned int NBN_WebRTC_HTable_FindFreeSlot(NBN_WebRTC_HTable *htable, NBN_WebRTC_HTableEntry *entry, bool *use_existing_slot) -{ +static unsigned int NBN_WebRTC_HTable_FindFreeSlot(NBN_WebRTC_HTable *htable, NBN_WebRTC_HTableEntry *entry, + bool *use_existing_slot) { unsigned long hash = entry->peer_id; unsigned int slot; @@ -198,8 +186,7 @@ static unsigned int NBN_WebRTC_HTable_FindFreeSlot(NBN_WebRTC_HTable *htable, NB NBN_WebRTC_HTableEntry *current_entry; unsigned int i = 0; - do - { + do { slot = (hash + (int)pow(i, 2)) % htable->capacity; current_entry = htable->internal_array[slot]; @@ -210,47 +197,43 @@ static unsigned int NBN_WebRTC_HTable_FindFreeSlot(NBN_WebRTC_HTable *htable, NB { *use_existing_slot = true; - NBN_Deallocator(current_entry); + free(current_entry); } - + return slot; } -static NBN_WebRTC_HTableEntry *NBN_WebRTC_HTable_FindEntry(NBN_WebRTC_HTable *htable, uint32_t peer_id) -{ +static NBN_WebRTC_HTableEntry *NBN_WebRTC_HTable_FindEntry(NBN_WebRTC_HTable *htable, uint32_t peer_id) { unsigned long hash = peer_id; unsigned int slot; - //quadratic probing + // quadratic probing NBN_WebRTC_HTableEntry *current_entry; unsigned int i = 0; - do - { + do { slot = (hash + (int)pow(i, 2)) % htable->capacity; current_entry = htable->internal_array[slot]; - if (current_entry != NULL && current_entry->peer_id == peer_id) - { + if (current_entry != NULL && current_entry->peer_id == peer_id) { return current_entry; } i++; } while (i < htable->capacity); - + return NULL; } -static void NBN_WebRTC_HTable_Grow(NBN_WebRTC_HTable *htable) -{ +static void NBN_WebRTC_HTable_Grow(NBN_WebRTC_HTable *htable) { unsigned int old_capacity = htable->capacity; unsigned int new_capacity = old_capacity * 2; - NBN_WebRTC_HTableEntry** old_internal_array = htable->internal_array; - NBN_WebRTC_HTableEntry** new_internal_array = (NBN_WebRTC_HTableEntry**)NBN_Allocator(sizeof(NBN_WebRTC_HTableEntry*) * new_capacity); + NBN_WebRTC_HTableEntry **old_internal_array = htable->internal_array; + NBN_WebRTC_HTableEntry **new_internal_array = + (NBN_WebRTC_HTableEntry **)malloc(sizeof(NBN_WebRTC_HTableEntry *) * new_capacity); - for (unsigned int i = 0; i < new_capacity; i++) - { + for (unsigned int i = 0; i < new_capacity; i++) { new_internal_array[i] = NULL; } @@ -261,13 +244,12 @@ static void NBN_WebRTC_HTable_Grow(NBN_WebRTC_HTable *htable) // rehash - for (unsigned int i = 0; i < old_capacity; i++) - { + for (unsigned int i = 0; i < old_capacity; i++) { if (old_internal_array[i]) NBN_WebRTC_HTable_InsertEntry(htable, old_internal_array[i]); } - NBN_Deallocator(old_internal_array); + free(old_internal_array); } #pragma endregion // Hashtable @@ -285,8 +267,7 @@ NBN_EXTERN void __js_game_server_stop(void); /* --- Driver implementation --- */ -typedef struct NBN_WebRTC_Server -{ +typedef struct NBN_WebRTC_Server { NBN_WebRTC_HTable *peers; uint8_t packet_buffer[NBN_PACKET_MAX_SIZE]; uint32_t protocol_id; @@ -295,10 +276,9 @@ typedef struct NBN_WebRTC_Server static NBN_WebRTC_Server nbn_wrtc_serv = {NULL, {0}, 0, false}; static NBN_WebRTC_Config nbn_wrtc_cfg; -static int NBN_WebRTC_ServStart(uint32_t protocol_id, uint16_t port) -{ +static int NBN_WebRTC_ServStart(uint32_t protocol_id, uint16_t port) { __js_game_server_init(protocol_id, nbn_wrtc_cfg.enable_tls, nbn_wrtc_cfg.key_path, nbn_wrtc_cfg.cert_path); - + if (__js_game_server_start(port) < 0) return -1; @@ -308,38 +288,31 @@ static int NBN_WebRTC_ServStart(uint32_t protocol_id, uint16_t port) return 0; } -static void NBN_WebRTC_ServStop(void) -{ +static void NBN_WebRTC_ServStop(void) { __js_game_server_stop(); NBN_WebRTC_HTable_Destroy(nbn_wrtc_serv.peers); } -static int NBN_WebRTC_ServRecvPackets(void) -{ +static int NBN_WebRTC_ServRecvPackets(void) { uint32_t peer_id; unsigned int len; - while ((len = __js_game_server_dequeue_packet(&peer_id, (uint8_t *)nbn_wrtc_serv.packet_buffer)) > 0) - { + while ((len = __js_game_server_dequeue_packet(&peer_id, (uint8_t *)nbn_wrtc_serv.packet_buffer)) > 0) { NBN_Packet packet; NBN_WebRTC_Peer *peer = NBN_WebRTC_HTable_Get(nbn_wrtc_serv.peers, peer_id); - if (peer == NULL) - { + if (peer == NULL) { if (GameServer_GetClientCount() >= NBN_MAX_CLIENTS) continue; NBN_LogTrace("Peer %d has connected", peer_id); - peer = (NBN_WebRTC_Peer *)NBN_Allocator(sizeof(NBN_WebRTC_Peer)); + peer = (NBN_WebRTC_Peer *)malloc(sizeof(NBN_WebRTC_Peer)); - peer->id = peer_id; - peer->conn = NBN_GameServer_CreateClientConnection( - NBN_WEBRTC_DRIVER_ID, - peer, - nbn_wrtc_serv.protocol_id, - peer_id); + peer->id = peer_id; + peer->conn = + NBN_GameServer_CreateClientConnection(NBN_WEBRTC_DRIVER_ID, peer, nbn_wrtc_serv.protocol_id, peer_id); NBN_WebRTC_HTable_Add(nbn_wrtc_serv.peers, peer_id, peer); @@ -357,24 +330,21 @@ static int NBN_WebRTC_ServRecvPackets(void) return 0; } -static void NBN_WebRTC_ServRemoveClientConnection(NBN_Connection *conn) -{ +static void NBN_WebRTC_ServRemoveClientConnection(NBN_Connection *conn) { assert(conn != NULL); __js_game_server_close_client_peer(conn->id); NBN_WebRTC_Peer *peer = NBN_WebRTC_HTable_Remove(nbn_wrtc_serv.peers, ((NBN_WebRTC_Peer *)conn->driver_data)->id); - if (peer) - { + if (peer) { NBN_LogDebug("Destroyed peer %d", peer->id); - NBN_Deallocator(peer); + free(peer); } } -static int NBN_WebRTC_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *conn) -{ +static int NBN_WebRTC_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *conn) { return __js_game_server_send_packet_to(packet->buffer, packet->size, conn->id); } @@ -392,15 +362,13 @@ NBN_EXTERN void __js_game_client_close(void); /* --- Driver implementation --- */ -typedef struct NBN_WebRTC_Client -{ +typedef struct NBN_WebRTC_Client { NBN_Connection *server_conn; } NBN_WebRTC_Client; static NBN_WebRTC_Client nbn_wrtc_cli = {NULL}; -static int NBN_WebRTC_CliStart(uint32_t protocol_id, const char *host, uint16_t port) -{ +static int NBN_WebRTC_CliStart(uint32_t protocol_id, const char *host, uint16_t port) { __js_game_client_init(protocol_id, nbn_wrtc_cfg.enable_tls); nbn_wrtc_cli.server_conn = NBN_GameClient_CreateServerConnection(NBN_WEBRTC_DRIVER_ID, NULL, protocol_id); @@ -413,17 +381,12 @@ static int NBN_WebRTC_CliStart(uint32_t protocol_id, const char *host, uint16_t return 0; } -static void NBN_WebRTC_CliStop(void) -{ - __js_game_client_close(); -} +static void NBN_WebRTC_CliStop(void) { __js_game_client_close(); } -static int NBN_WebRTC_CliRecvPackets(void) -{ +static int NBN_WebRTC_CliRecvPackets(void) { unsigned int len; - while ((len = __js_game_client_dequeue_packet((uint8_t *)nbn_wrtc_serv.packet_buffer)) > 0) - { + while ((len = __js_game_client_dequeue_packet((uint8_t *)nbn_wrtc_serv.packet_buffer)) > 0) { NBN_Packet packet; if (NBN_Packet_InitRead(&packet, nbn_wrtc_cli.server_conn, nbn_wrtc_serv.packet_buffer, len) < 0) @@ -435,8 +398,7 @@ static int NBN_WebRTC_CliRecvPackets(void) return 0; } -static int NBN_WebRTC_CliSendPacket(NBN_Packet *packet) -{ +static int NBN_WebRTC_CliSendPacket(NBN_Packet *packet) { return __js_game_client_send_packet(packet->buffer, packet->size); } @@ -444,30 +406,18 @@ static int NBN_WebRTC_CliSendPacket(NBN_Packet *packet) #pragma region Driver registering -void NBN_WebRTC_Register(NBN_WebRTC_Config config) -{ - NBN_DriverImplementation driver_impl = { - // Client implementation - NBN_WebRTC_CliStart, - NBN_WebRTC_CliStop, - NBN_WebRTC_CliRecvPackets, - NBN_WebRTC_CliSendPacket, - - // Server implementation - NBN_WebRTC_ServStart, - NBN_WebRTC_ServStop, - NBN_WebRTC_ServRecvPackets, - NBN_WebRTC_ServSendPacketTo, - NBN_WebRTC_ServRemoveClientConnection - }; +void NBN_WebRTC_Register(NBN_WebRTC_Config config) { + NBN_DriverImplementation driver_impl = {// Client implementation + NBN_WebRTC_CliStart, NBN_WebRTC_CliStop, NBN_WebRTC_CliRecvPackets, + NBN_WebRTC_CliSendPacket, + + // Server implementation + NBN_WebRTC_ServStart, NBN_WebRTC_ServStop, NBN_WebRTC_ServRecvPackets, + NBN_WebRTC_ServSendPacketTo, NBN_WebRTC_ServRemoveClientConnection}; nbn_wrtc_cfg = config; - NBN_Driver_Register( - NBN_WEBRTC_DRIVER_ID, - NBN_WEBRTC_DRIVER_NAME, - driver_impl - ); + NBN_Driver_Register(NBN_WEBRTC_DRIVER_ID, NBN_WEBRTC_DRIVER_NAME, driver_impl); } #pragma endregion /* Driver registering */ diff --git a/net_drivers/webrtc_c.h b/net_drivers/webrtc_c.h index e3eb5c5..2b246c4 100644 --- a/net_drivers/webrtc_c.h +++ b/net_drivers/webrtc_c.h @@ -30,23 +30,24 @@ freely, subject to the following restrictions: 1. libdatachannel (https://github.com/paullouisageneau/libdatachannel) 2. json.h (https://github.com/sheredom/json.h) - + How to use: 1. Include this header *once* after the nbnet header in the same file where you defined the NBNET_IMPL macro - 2. Call NBN_WebRTC_C_Register in both your client and server code before calling NBN_GameClient_Start or NBN_GameServer_Start + 2. Call NBN_WebRTC_C_Register in both your client and server code before calling NBN_GameClient_Start or + NBN_GameServer_Start */ -#include -#include -#include #include "json.h" +#include +#include +#include +#include #define NBN_WEBRTC_C_DRIVER_ID 2 #define NBN_WEBRTC_C_DRIVER_NAME "WebRTC_C" -typedef struct NBN_WebRTC_C_Config -{ +typedef struct NBN_WebRTC_C_Config { bool enable_tls; const char *cert_path; const char *key_path; @@ -63,8 +64,7 @@ void NBN_WebRTC_C_Unregister(void); #ifdef NBNET_IMPL -typedef struct -{ +typedef struct { int id; int channel_id; int ws; @@ -78,15 +78,13 @@ static void NBN_WebRTC_C_DestroyPeer(NBN_WebRTC_C_Peer *peer); #define HTABLE_DEFAULT_INITIAL_CAPACITY 32 #define HTABLE_LOAD_FACTOR_THRESHOLD 0.75 -typedef struct -{ +typedef struct { int peer_id; NBN_WebRTC_C_Peer *peer; unsigned int slot; } NBN_WebRTC_C_HTableEntry; -typedef struct -{ +typedef struct { NBN_WebRTC_C_HTableEntry **internal_array; unsigned int capacity; unsigned int count; @@ -105,47 +103,40 @@ static unsigned int NBN_WebRTC_C_HTable_FindFreeSlot(NBN_WebRTC_C_HTable *, NBN_ static NBN_WebRTC_C_HTableEntry *NBN_WebRTC_C_HTable_FindEntry(NBN_WebRTC_C_HTable *, int); static void NBN_WebRTC_C_HTable_Grow(NBN_WebRTC_C_HTable *); -static NBN_WebRTC_C_HTable *NBN_WebRTC_C_HTable_Create(void) -{ +static NBN_WebRTC_C_HTable *NBN_WebRTC_C_HTable_Create(void) { return NBN_WebRTC_C_HTable_CreateWithCapacity(HTABLE_DEFAULT_INITIAL_CAPACITY); } -static NBN_WebRTC_C_HTable *NBN_WebRTC_C_HTable_CreateWithCapacity(unsigned int capacity) -{ - NBN_WebRTC_C_HTable *htable = (NBN_WebRTC_C_HTable*)NBN_Allocator(sizeof(NBN_WebRTC_C_HTable)); +static NBN_WebRTC_C_HTable *NBN_WebRTC_C_HTable_CreateWithCapacity(unsigned int capacity) { + NBN_WebRTC_C_HTable *htable = (NBN_WebRTC_C_HTable *)malloc(sizeof(NBN_WebRTC_C_HTable)); - htable->internal_array = (NBN_WebRTC_C_HTableEntry**)NBN_Allocator(sizeof(NBN_WebRTC_C_HTableEntry *) * capacity); + htable->internal_array = (NBN_WebRTC_C_HTableEntry **)malloc(sizeof(NBN_WebRTC_C_HTableEntry *) * capacity); htable->capacity = capacity; htable->count = 0; htable->load_factor = 0; - for (unsigned int i = 0; i < htable->capacity; i++) - { + for (unsigned int i = 0; i < htable->capacity; i++) { htable->internal_array[i] = NULL; } return htable; } -static void NBN_WebRTC_C_HTable_Destroy(NBN_WebRTC_C_HTable *htable) -{ - for (unsigned int i = 0; i < htable->capacity; i++) - { +static void NBN_WebRTC_C_HTable_Destroy(NBN_WebRTC_C_HTable *htable) { + for (unsigned int i = 0; i < htable->capacity; i++) { NBN_WebRTC_C_HTableEntry *entry = htable->internal_array[i]; - if (entry) - { + if (entry) { NBN_WebRTC_C_DestroyPeer(entry->peer); } } - NBN_Deallocator(htable->internal_array); - NBN_Deallocator(htable); + free(htable->internal_array); + free(htable); } -static void NBN_WebRTC_C_HTable_Add(NBN_WebRTC_C_HTable *htable, int peer_id, NBN_WebRTC_C_Peer *peer) -{ - NBN_WebRTC_C_HTableEntry *entry = (NBN_WebRTC_C_HTableEntry*)NBN_Allocator(sizeof(NBN_WebRTC_C_HTableEntry)); +static void NBN_WebRTC_C_HTable_Add(NBN_WebRTC_C_HTable *htable, int peer_id, NBN_WebRTC_C_Peer *peer) { + NBN_WebRTC_C_HTableEntry *entry = (NBN_WebRTC_C_HTableEntry *)malloc(sizeof(NBN_WebRTC_C_HTableEntry)); entry->peer_id = peer_id; entry->peer = peer; @@ -156,19 +147,16 @@ static void NBN_WebRTC_C_HTable_Add(NBN_WebRTC_C_HTable *htable, int peer_id, NB NBN_WebRTC_C_HTable_Grow(htable); } -static NBN_WebRTC_C_Peer *NBN_WebRTC_C_HTable_Get(NBN_WebRTC_C_HTable *htable, int peer_id) -{ +static NBN_WebRTC_C_Peer *NBN_WebRTC_C_HTable_Get(NBN_WebRTC_C_HTable *htable, int peer_id) { NBN_WebRTC_C_HTableEntry *entry = NBN_WebRTC_C_HTable_FindEntry(htable, peer_id); return entry ? entry->peer : NULL; } -static NBN_WebRTC_C_Peer *NBN_WebRTC_C_HTable_Remove(NBN_WebRTC_C_HTable *htable, int peer_id) -{ +static NBN_WebRTC_C_Peer *NBN_WebRTC_C_HTable_Remove(NBN_WebRTC_C_HTable *htable, int peer_id) { NBN_WebRTC_C_HTableEntry *entry = NBN_WebRTC_C_HTable_FindEntry(htable, peer_id); - if (entry) - { + if (entry) { NBN_WebRTC_C_Peer *peer = entry->peer; NBN_WebRTC_C_HTable_RemoveEntry(htable, entry); @@ -179,33 +167,30 @@ static NBN_WebRTC_C_Peer *NBN_WebRTC_C_HTable_Remove(NBN_WebRTC_C_HTable *htable return NULL; } -static void NBN_WebRTC_C_HTable_InsertEntry(NBN_WebRTC_C_HTable *htable, NBN_WebRTC_C_HTableEntry *entry) -{ +static void NBN_WebRTC_C_HTable_InsertEntry(NBN_WebRTC_C_HTable *htable, NBN_WebRTC_C_HTableEntry *entry) { bool use_existing_slot = false; unsigned int slot = NBN_WebRTC_C_HTable_FindFreeSlot(htable, entry, &use_existing_slot); entry->slot = slot; htable->internal_array[slot] = entry; - if (!use_existing_slot) - { + if (!use_existing_slot) { htable->count++; htable->load_factor = (float)htable->count / htable->capacity; } } -static void NBN_WebRTC_C_HTable_RemoveEntry(NBN_WebRTC_C_HTable *htable, NBN_WebRTC_C_HTableEntry *entry) -{ +static void NBN_WebRTC_C_HTable_RemoveEntry(NBN_WebRTC_C_HTable *htable, NBN_WebRTC_C_HTableEntry *entry) { htable->internal_array[entry->slot] = NULL; - NBN_Deallocator(entry); + free(entry); htable->count--; htable->load_factor = htable->count / htable->capacity; } -static unsigned int NBN_WebRTC_C_HTable_FindFreeSlot(NBN_WebRTC_C_HTable *htable, NBN_WebRTC_C_HTableEntry *entry, bool *use_existing_slot) -{ +static unsigned int NBN_WebRTC_C_HTable_FindFreeSlot(NBN_WebRTC_C_HTable *htable, NBN_WebRTC_C_HTableEntry *entry, + bool *use_existing_slot) { unsigned long hash = entry->peer_id; unsigned int slot; @@ -214,8 +199,7 @@ static unsigned int NBN_WebRTC_C_HTable_FindFreeSlot(NBN_WebRTC_C_HTable *htable NBN_WebRTC_C_HTableEntry *current_entry; unsigned int i = 0; - do - { + do { slot = (hash + (int)pow(i, 2)) % htable->capacity; current_entry = htable->internal_array[slot]; @@ -226,47 +210,43 @@ static unsigned int NBN_WebRTC_C_HTable_FindFreeSlot(NBN_WebRTC_C_HTable *htable { *use_existing_slot = true; - NBN_Deallocator(current_entry); + free(current_entry); } - + return slot; } -static NBN_WebRTC_C_HTableEntry *NBN_WebRTC_C_HTable_FindEntry(NBN_WebRTC_C_HTable *htable, int peer_id) -{ +static NBN_WebRTC_C_HTableEntry *NBN_WebRTC_C_HTable_FindEntry(NBN_WebRTC_C_HTable *htable, int peer_id) { unsigned long hash = peer_id; unsigned int slot; - //quadratic probing + // quadratic probing NBN_WebRTC_C_HTableEntry *current_entry; unsigned int i = 0; - do - { + do { slot = (hash + (int)pow(i, 2)) % htable->capacity; current_entry = htable->internal_array[slot]; - if (current_entry != NULL && current_entry->peer_id == peer_id) - { + if (current_entry != NULL && current_entry->peer_id == peer_id) { return current_entry; } i++; } while (i < htable->capacity); - + return NULL; } -static void NBN_WebRTC_C_HTable_Grow(NBN_WebRTC_C_HTable *htable) -{ +static void NBN_WebRTC_C_HTable_Grow(NBN_WebRTC_C_HTable *htable) { unsigned int old_capacity = htable->capacity; unsigned int new_capacity = old_capacity * 2; - NBN_WebRTC_C_HTableEntry** old_internal_array = htable->internal_array; - NBN_WebRTC_C_HTableEntry** new_internal_array = (NBN_WebRTC_C_HTableEntry**)NBN_Allocator(sizeof(NBN_WebRTC_C_HTableEntry*) * new_capacity); + NBN_WebRTC_C_HTableEntry **old_internal_array = htable->internal_array; + NBN_WebRTC_C_HTableEntry **new_internal_array = + (NBN_WebRTC_C_HTableEntry **)malloc(sizeof(NBN_WebRTC_C_HTableEntry *) * new_capacity); - for (unsigned int i = 0; i < new_capacity; i++) - { + for (unsigned int i = 0; i < new_capacity; i++) { new_internal_array[i] = NULL; } @@ -277,13 +257,12 @@ static void NBN_WebRTC_C_HTable_Grow(NBN_WebRTC_C_HTable *htable) // rehash - for (unsigned int i = 0; i < old_capacity; i++) - { + for (unsigned int i = 0; i < old_capacity; i++) { if (old_internal_array[i]) NBN_WebRTC_C_HTable_InsertEntry(htable, old_internal_array[i]); } - NBN_Deallocator(old_internal_array); + free(old_internal_array); } #pragma endregion // Hashtable @@ -291,23 +270,19 @@ static void NBN_WebRTC_C_HTable_Grow(NBN_WebRTC_C_HTable *htable) #pragma region String utils // IMPORTANT: res needs to be pre allocated and big enough to old the resulting string -static void NBN_WebRTC_C_StringReplaceAll(char *res, const char *str, const char *a, const char *b) -{ - char *substr = (char*)strstr(str, a); +static void NBN_WebRTC_C_StringReplaceAll(char *res, const char *str, const char *a, const char *b) { + char *substr = (char *)strstr(str, a); size_t len_a = strlen(a); size_t len_b = strlen(b); - if (substr) - { + if (substr) { int pos = substr - str; strncpy(res, str, pos); strncpy(res + pos, b, len_b); NBN_WebRTC_C_StringReplaceAll(res + pos + len_b, str + pos + len_a, a, b); - } - else - { + } else { strncpy(res, str, strlen(str) + 1); } } @@ -316,44 +291,37 @@ static void NBN_WebRTC_C_StringReplaceAll(char *res, const char *str, const char #pragma region WebRTC common -static char *NBN_WebRTC_C_ParseSignalingMessage(const char *msg, size_t msg_len, const char *type) -{ +static char *NBN_WebRTC_C_ParseSignalingMessage(const char *msg, size_t msg_len, const char *type) { char *sdp = NULL; - struct json_value_s* root = json_parse(msg, msg_len); // this has to be freed - - struct json_object_s* object = (struct json_object_s*)root->payload; + struct json_value_s *root = json_parse(msg, msg_len); // this has to be freed + + struct json_object_s *object = (struct json_object_s *)root->payload; struct json_object_element_s *curr = object->start; - if (root->type != json_type_object) - { + if (root->type != json_type_object) { NBN_LogDebug("Received an invalid signaling message: %s", msg); goto leave_free_root; } - while (curr != NULL) - { - if (strncmp(curr->name->string, "type", 4) == 0) - { + while (curr != NULL) { + if (strncmp(curr->name->string, "type", 4) == 0) { struct json_string_s *str = json_value_as_string(curr->value); - if (strncmp(str->string, type, strlen(type))) - { + if (strncmp(str->string, type, strlen(type))) { // unexpected type - NBN_LogDebug("Received a signaling message with an unexpected type: %s (expected: %s)", str->string, type); + NBN_LogDebug("Received a signaling message with an unexpected type: %s (expected: %s)", str->string, + type); sdp = NULL; goto leave_free_root; } - } - else if (strncmp(curr->name->string, "sdp", 3) == 0) - { + } else if (strncmp(curr->name->string, "sdp", 3) == 0) { struct json_string_s *str = json_value_as_string(curr->value); - if (str) - { - // strdup equivalent using NBN_Allocator, make sure this get freed + if (str) { + // strdup equivalent using malloc, make sure this get freed size_t len = strlen(str->string); - sdp = (char*)NBN_Allocator(len + 1); + sdp = (char *)malloc(len + 1); memcpy(sdp, str->string, len + 1); } } @@ -367,72 +335,62 @@ static char *NBN_WebRTC_C_ParseSignalingMessage(const char *msg, size_t msg_len, return sdp; } -static char *NBN_WebRTC_C_EscapeSDP(const char *sdp) -{ +static char *NBN_WebRTC_C_EscapeSDP(const char *sdp) { size_t len = strlen(sdp) * 2; // TODO: kinda lame way of making sure it's going to be big enough, find a better way - char *escaped_sdp = (char*)NBN_Allocator(len); + char *escaped_sdp = (char *)malloc(len); NBN_WebRTC_C_StringReplaceAll(escaped_sdp, sdp, "\r\n", "\\r\\n"); return escaped_sdp; } -static void NBN_WebRTC_C_Log(rtcLogLevel level, const char *msg) -{ - switch (level) - { - case RTC_LOG_FATAL: - case RTC_LOG_ERROR: - NBN_LogError("%s",msg); - break; +static void NBN_WebRTC_C_Log(rtcLogLevel level, const char *msg) { + switch (level) { + case RTC_LOG_FATAL: + case RTC_LOG_ERROR: + NBN_LogError("%s", msg); + break; - case RTC_LOG_WARNING: - NBN_LogWarning("%s",msg); - break; + case RTC_LOG_WARNING: + NBN_LogWarning("%s", msg); + break; - case RTC_LOG_INFO: - NBN_LogInfo("%s",msg); - break; + case RTC_LOG_INFO: + NBN_LogInfo("%s", msg); + break; - case RTC_LOG_DEBUG: - NBN_LogDebug("%s",msg); - break; + case RTC_LOG_DEBUG: + NBN_LogDebug("%s", msg); + break; - case RTC_LOG_VERBOSE: - NBN_LogTrace("%s",msg); - break; + case RTC_LOG_VERBOSE: + NBN_LogTrace("%s", msg); + break; - case RTC_LOG_NONE: - break; + case RTC_LOG_NONE: + break; } } -static void NBN_WebRTC_C_OnWsError(int ws, const char *err_msg, void *user_ptr) -{ +static void NBN_WebRTC_C_OnWsError(int ws, const char *err_msg, void *user_ptr) { (void)user_ptr; NBN_LogError("Error on WS %d: %s", ws, err_msg); } -static NBN_WebRTC_C_Peer *NBN_WebRTC_C_CreatePeer( - int ws, - rtcDescriptionCallbackFunc on_rtc_description_cb, - rtcStateChangeCallbackFunc on_state_change_cb) -{ - rtcConfiguration rtcCfg = { - .iceServers = nbn_wrtc_c_cfg.ice_servers, - .iceServersCount = (int)nbn_wrtc_c_cfg.ice_servers_count, - .disableAutoNegotiation = false - }; +static NBN_WebRTC_C_Peer *NBN_WebRTC_C_CreatePeer(int ws, rtcDescriptionCallbackFunc on_rtc_description_cb, + rtcStateChangeCallbackFunc on_state_change_cb) { + rtcConfiguration rtcCfg = {.iceServers = nbn_wrtc_c_cfg.ice_servers, + .iceServersCount = (int)nbn_wrtc_c_cfg.ice_servers_count, + .disableAutoNegotiation = false}; int peer_id = rtcCreatePeerConnection(&rtcCfg); - if (peer_id < 0) - { + if (peer_id < 0) { NBN_LogError("Failed to create peer: %d", peer_id); return NULL; } - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)NBN_Allocator(sizeof(NBN_WebRTC_C_Peer)); + NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)malloc(sizeof(NBN_WebRTC_C_Peer)); peer->id = peer_id; peer->ws = ws; @@ -443,8 +401,7 @@ static NBN_WebRTC_C_Peer *NBN_WebRTC_C_CreatePeer( int ret = rtcSetLocalDescriptionCallback(peer->id, on_rtc_description_cb); - if (ret < 0) - { + if (ret < 0) { NBN_LogError("Failed to register local description callback for peer %d: %d", peer->id, ret); NBN_WebRTC_C_DestroyPeer(peer); return NULL; @@ -452,8 +409,7 @@ static NBN_WebRTC_C_Peer *NBN_WebRTC_C_CreatePeer( ret = rtcSetStateChangeCallback(peer_id, on_state_change_cb); - if (ret < 0) - { + if (ret < 0) { NBN_LogError("Failed to register state change callback for peer %d: %d", peer_id, ret); NBN_WebRTC_C_DestroyPeer(peer); return NULL; @@ -462,12 +418,10 @@ static NBN_WebRTC_C_Peer *NBN_WebRTC_C_CreatePeer( .reliability = {.unordered = true, .unreliable = true, .maxPacketLifeTime = 1000, .maxRetransmits = 0}, .negotiated = true, .manualStream = true, - .stream = 0 - }; + .stream = 0}; int channel_id = rtcCreateDataChannelEx(peer_id, "unreliable", &rtcDataChannel); - if (channel_id < 0) - { + if (channel_id < 0) { NBN_LogError("Failed to create data channel for peer %d: %d", peer_id, channel_id); NBN_WebRTC_C_DestroyPeer(peer); return NULL; @@ -475,61 +429,58 @@ static NBN_WebRTC_C_Peer *NBN_WebRTC_C_CreatePeer( peer->channel_id = channel_id; - NBN_LogDebug("Successfully created data channel for peer %d: %d", peer_id, channel_id); + NBN_LogDebug("Successfully created data channel for peer %d: %d", peer_id, channel_id); return peer; } -static void NBN_WebRTC_C_DestroyPeer(NBN_WebRTC_C_Peer *peer) -{ +static void NBN_WebRTC_C_DestroyPeer(NBN_WebRTC_C_Peer *peer) { NBN_LogDebug("Destroying peer %d", peer->id); - if (peer->channel_id >= 0) - { + if (peer->channel_id >= 0) { rtcDeleteDataChannel(peer->channel_id); } rtcDeletePeerConnection(peer->id); rtcDelete(peer->ws); - NBN_Deallocator(peer); + free(peer); } -static void NBN_WebRTC_C_ProcessLocalDescription(NBN_WebRTC_C_Peer *peer, const char *sdp, const char *type) -{ +static void NBN_WebRTC_C_ProcessLocalDescription(NBN_WebRTC_C_Peer *peer, const char *sdp, const char *type) { char *escaped_sdp = NBN_WebRTC_C_EscapeSDP(sdp); size_t signaling_json_size = snprintf(NULL, 0, "{\"type\":\"%s\", \"sdp\":\"%s\"}", type, escaped_sdp) + 1; - char* signaling_json = (char *) NBN_Allocator(signaling_json_size); + char *signaling_json = (char *)malloc(signaling_json_size); snprintf(signaling_json, signaling_json_size, "{\"type\":\"%s\", \"sdp\":\"%s\"}", type, escaped_sdp); NBN_LogDebug("Send signaling message of type %s to remote connection: %s", type, signaling_json); // pass -1 as the size (assume signaling_json to be a null-terminated string) - if (rtcSendMessage(peer->ws, signaling_json, -1) < 0) - { + if (rtcSendMessage(peer->ws, signaling_json, -1) < 0) { NBN_WebRTC_C_DestroyPeer(peer); } - NBN_Deallocator(signaling_json); - NBN_Deallocator(escaped_sdp); + free(signaling_json); + free(escaped_sdp); } -static void NBN_WebRTC_C_ProcessSignalingMessage(NBN_WebRTC_C_Peer *peer, int ws, const char *msg, int size, const char *type) -{ +static void NBN_WebRTC_C_ProcessSignalingMessage(NBN_WebRTC_C_Peer *peer, int ws, const char *msg, int size, + const char *type) { // for some reason the size of the message is negative - // in libdatachannel documentation (https://github.com/paullouisageneau/libdatachannel/blob/master/DOC.md) there is mention of: - // size: if size >= 0, data is interpreted as a binary message of length size, otherwise it is interpreted as a null-terminated UTF-8 string. - // so I guess in this case msg is a null terminated string? I could not find more information about this so I decided to go with - // flipping the size to positive even though it feels weird, but it works so... ¯\_(ツ)_/¯ - - if (size < 0) size *= -1; + // in libdatachannel documentation (https://github.com/paullouisageneau/libdatachannel/blob/master/DOC.md) there is + // mention of: size: if size >= 0, data is interpreted as a binary message of length size, otherwise it is + // interpreted as a null-terminated UTF-8 string. so I guess in this case msg is a null terminated string? I could + // not find more information about this so I decided to go with flipping the size to positive even though it feels + // weird, but it works so... ¯\_(ツ)_/¯ + + if (size < 0) + size *= -1; size -= 1; NBN_LogDebug("Received signaling message on WS %d (size: %d): %s", ws, size, msg); char *sdp = NBN_WebRTC_C_ParseSignalingMessage(msg, size, type); - if (!sdp) - { + if (!sdp) { NBN_LogWarning("Failed to parse signaling data for WS %d", ws); return; } @@ -538,22 +489,20 @@ static void NBN_WebRTC_C_ProcessSignalingMessage(NBN_WebRTC_C_Peer *peer, int ws int ret = rtcSetRemoteDescription(peer->id, sdp, type); - if (ret < 0) - { + if (ret < 0) { NBN_LogError("Failed to set remote description for peer %d (WS: %d): %d", peer->id, ws, ret); rtcClose(ws); } // IMPORTANT: not sure I can free this because it's passed to rtcSetRemoteDescription - NBN_Deallocator(sdp); + free(sdp); } #pragma endregion /* WebRTC common */ #pragma region Game server -typedef struct NBN_WebRTC_C_Server -{ +typedef struct NBN_WebRTC_C_Server { int wsserver; NBN_WebRTC_C_HTable *peers; uint16_t ws_port; @@ -563,12 +512,10 @@ typedef struct NBN_WebRTC_C_Server static NBN_WebRTC_C_Server nbn_wrtc_c_serv = {0, NULL, false, 0, 0, {0}}; -static void NBN_WebRTC_C_Serv_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) -{ +static void NBN_WebRTC_C_Serv_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { NBN_LogDebug("Processing local description of type '%s'", type); - if (strncmp(type, "answer", strlen("answer")) != 0) - { + if (strncmp(type, "answer", strlen("answer")) != 0) { NBN_LogWarning("Ignoring local description of type '%s' (expected 'answer')", type); return; } @@ -576,12 +523,10 @@ static void NBN_WebRTC_C_Serv_OnLocalDescription(int pc, const char *sdp, const NBN_WebRTC_C_ProcessLocalDescription((NBN_WebRTC_C_Peer *)user_ptr, sdp, "answer"); } -static void NBN_WebRTC_C_Serv_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) -{ +static void NBN_WebRTC_C_Serv_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { NBN_LogDebug("Peer %d state changed to %d", pc, state); - if (state == RTC_CONNECTED) - { + if (state == RTC_CONNECTED) { NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)user_ptr; NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, peer->conn); @@ -589,32 +534,26 @@ static void NBN_WebRTC_C_Serv_OnPeerStateChanged(int pc, rtcState state, void *u } } -static void NBN_WebRTC_C_Serv_OnWsOpen(int ws, void *user_ptr) -{ +static void NBN_WebRTC_C_Serv_OnWsOpen(int ws, void *user_ptr) { NBN_LogDebug("WS %d is open", ws); - NBN_WebRTC_C_Peer *peer = NBN_WebRTC_C_CreatePeer(ws, NBN_WebRTC_C_Serv_OnLocalDescription, NBN_WebRTC_C_Serv_OnPeerStateChanged); + NBN_WebRTC_C_Peer *peer = + NBN_WebRTC_C_CreatePeer(ws, NBN_WebRTC_C_Serv_OnLocalDescription, NBN_WebRTC_C_Serv_OnPeerStateChanged); - if (!peer) - { + if (!peer) { NBN_LogError("Failed to create peer"); return; } - peer->conn = NBN_GameServer_CreateClientConnection( - NBN_WEBRTC_C_DRIVER_ID, - peer, - nbn_wrtc_c_serv.protocol_id, - peer->id); + peer->conn = + NBN_GameServer_CreateClientConnection(NBN_WEBRTC_C_DRIVER_ID, peer, nbn_wrtc_c_serv.protocol_id, peer->id); NBN_WebRTC_C_HTable_Add(nbn_wrtc_c_serv.peers, peer->id, peer); } -static void NBN_WebRTC_C_Serv_OnWsClosed(int ws, void *user_ptr) -{ +static void NBN_WebRTC_C_Serv_OnWsClosed(int ws, void *user_ptr) { NBN_LogDebug("WS %d has closed", ws); - if (user_ptr) - { + if (user_ptr) { NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)user_ptr; NBN_LogDebug("Closing WebRTC peer and channel (peer: %d, channel: %d)", peer->id, peer->channel_id); @@ -624,13 +563,11 @@ static void NBN_WebRTC_C_Serv_OnWsClosed(int ws, void *user_ptr) } } -static void NBN_WebRTC_C_Serv_OnWsMessage(int ws, const char *msg, int size, void *user_ptr) -{ +static void NBN_WebRTC_C_Serv_OnWsMessage(int ws, const char *msg, int size, void *user_ptr) { NBN_WebRTC_C_ProcessSignalingMessage((NBN_WebRTC_C_Peer *)user_ptr, ws, msg, size, "offer"); } -static void NBN_WebRTC_C_Serv_OnWsConnection(int wsserver, int ws, void *user_ptr) -{ +static void NBN_WebRTC_C_Serv_OnWsConnection(int wsserver, int ws, void *user_ptr) { NBN_LogDebug("New WS connection %d (user_ptr: %p)", ws, user_ptr); rtcSetOpenCallback(ws, NBN_WebRTC_C_Serv_OnWsOpen); @@ -639,8 +576,7 @@ static void NBN_WebRTC_C_Serv_OnWsConnection(int wsserver, int ws, void *user_pt rtcSetMessageCallback(ws, NBN_WebRTC_C_Serv_OnWsMessage); } -static int NBN_WebRTC_C_ServStart(uint32_t protocol_id, uint16_t port) -{ +static int NBN_WebRTC_C_ServStart(uint32_t protocol_id, uint16_t port) { nbn_wrtc_c_serv.ws_port = port; nbn_wrtc_c_serv.peers = NBN_WebRTC_C_HTable_Create(); nbn_wrtc_c_serv.protocol_id = protocol_id; @@ -649,18 +585,15 @@ static int NBN_WebRTC_C_ServStart(uint32_t protocol_id, uint16_t port) rtcInitLogger(nbn_wrtc_c_cfg.log_level, NBN_WebRTC_C_Log); rtcPreload(); - rtcWsServerConfiguration cfg = { - .port = port, - .enableTls = nbn_wrtc_c_cfg.enable_tls, - .certificatePemFile = nbn_wrtc_c_cfg.cert_path, - .keyPemFile = nbn_wrtc_c_cfg.key_path, - .keyPemPass = nbn_wrtc_c_cfg.passphrase - }; + rtcWsServerConfiguration cfg = {.port = port, + .enableTls = nbn_wrtc_c_cfg.enable_tls, + .certificatePemFile = nbn_wrtc_c_cfg.cert_path, + .keyPemFile = nbn_wrtc_c_cfg.key_path, + .keyPemPass = nbn_wrtc_c_cfg.passphrase}; int wsserver = rtcCreateWebSocketServer(&cfg, NBN_WebRTC_C_Serv_OnWsConnection); - if (wsserver < 0) - { + if (wsserver < 0) { NBN_LogError("Failed to start WS server (code: %d)", wsserver); return NBN_ERROR; } @@ -670,31 +603,26 @@ static int NBN_WebRTC_C_ServStart(uint32_t protocol_id, uint16_t port) return 0; } -static void NBN_WebRTC_C_ServStop(void) -{ +static void NBN_WebRTC_C_ServStop(void) { NBN_WebRTC_C_HTable_Destroy(nbn_wrtc_c_serv.peers); - if (nbn_wrtc_c_serv.wsserver >= 0) - { + if (nbn_wrtc_c_serv.wsserver >= 0) { rtcDeleteWebSocketServer(nbn_wrtc_c_serv.wsserver); } rtcCleanup(); } -static int NBN_WebRTC_C_ServRecvPackets(void) -{ +static int NBN_WebRTC_C_ServRecvPackets(void) { const int buffer_size = sizeof(nbn_wrtc_c_serv.packet_buffer); int size = buffer_size; - for (unsigned int i = 0; i < nbn_wrtc_c_serv.peers->capacity; i++) - { + for (unsigned int i = 0; i < nbn_wrtc_c_serv.peers->capacity; i++) { NBN_WebRTC_C_HTableEntry *entry = nbn_wrtc_c_serv.peers->internal_array[i]; - if (entry) - { - while (rtcReceiveMessage(entry->peer->channel_id, nbn_wrtc_c_serv.packet_buffer, &size) == RTC_ERR_SUCCESS) - { + if (entry) { + while (rtcReceiveMessage(entry->peer->channel_id, nbn_wrtc_c_serv.packet_buffer, &size) == + RTC_ERR_SUCCESS) { NBN_Packet packet; if (NBN_Packet_InitRead(&packet, entry->peer->conn, (uint8_t *)nbn_wrtc_c_serv.packet_buffer, size) < 0) @@ -710,22 +638,20 @@ static int NBN_WebRTC_C_ServRecvPackets(void) return 0; } -static void NBN_WebRTC_C_ServRemoveClientConnection(NBN_Connection *conn) -{ - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer*)conn->driver_data; +static void NBN_WebRTC_C_ServRemoveClientConnection(NBN_Connection *conn) { + NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)conn->driver_data; NBN_WebRTC_C_HTable_Remove(nbn_wrtc_c_serv.peers, peer->id); NBN_WebRTC_C_DestroyPeer(peer); } -static int NBN_WebRTC_C_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *conn) -{ - if (!conn->is_accepted) return 0; +static int NBN_WebRTC_C_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *conn) { + if (!conn->is_accepted) + return 0; - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer*)conn->driver_data; + NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)conn->driver_data; - if (rtcSendMessage(peer->channel_id, (char *)packet->buffer, packet->size) < 0) - { + if (rtcSendMessage(peer->channel_id, (char *)packet->buffer, packet->size) < 0) { NBN_LogError("rtcSendMessage failed for peer %d", peer->id); } @@ -736,8 +662,7 @@ static int NBN_WebRTC_C_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *con #pragma region Game client -typedef struct NBN_WebRTC_C_Client -{ +typedef struct NBN_WebRTC_C_Client { uint32_t protocol_id; bool is_connected; NBN_WebRTC_C_Peer *peer; @@ -746,12 +671,10 @@ typedef struct NBN_WebRTC_C_Client static NBN_WebRTC_C_Client nbn_wrtc_c_cli = {0, false, false, NULL, {0}}; -static void NBN_WebRTC_C_Cli_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) -{ +static void NBN_WebRTC_C_Cli_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { NBN_LogDebug("Processing local description of type '%s'", type); - if (strncmp(type, "offer", strlen("offer")) != 0) - { + if (strncmp(type, "offer", strlen("offer")) != 0) { NBN_LogWarning("Ignoring local description of type '%s' (expected 'offer')", type); return; } @@ -759,12 +682,10 @@ static void NBN_WebRTC_C_Cli_OnLocalDescription(int pc, const char *sdp, const c NBN_WebRTC_C_ProcessLocalDescription((NBN_WebRTC_C_Peer *)user_ptr, sdp, "offer"); } -static void NBN_WebRTC_C_Cli_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) -{ +static void NBN_WebRTC_C_Cli_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { NBN_LogDebug("Server peer state changed to %d", pc, state); - if (state == RTC_CONNECTED) - { + if (state == RTC_CONNECTED) { NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)user_ptr; NBN_LogDebug("Server peer is connected !", pc); @@ -772,30 +693,27 @@ static void NBN_WebRTC_C_Cli_OnPeerStateChanged(int pc, rtcState state, void *us } } -static void NBN_WebRTC_C_Cli_OnWsOpen(int ws, void *user_ptr) -{ +static void NBN_WebRTC_C_Cli_OnWsOpen(int ws, void *user_ptr) { NBN_LogDebug("WS %d is open, creating peer...", ws); - NBN_WebRTC_C_Peer *peer = NBN_WebRTC_C_CreatePeer(ws, NBN_WebRTC_C_Cli_OnLocalDescription, NBN_WebRTC_C_Cli_OnPeerStateChanged); + NBN_WebRTC_C_Peer *peer = + NBN_WebRTC_C_CreatePeer(ws, NBN_WebRTC_C_Cli_OnLocalDescription, NBN_WebRTC_C_Cli_OnPeerStateChanged); - if (!peer) - { + if (!peer) { NBN_LogError("Failed to create peer"); return; - } + } - NBN_LogDebug("Successfully created peer: %d", peer->id); + NBN_LogDebug("Successfully created peer: %d", peer->id); peer->conn = NBN_GameClient_CreateServerConnection(NBN_WEBRTC_C_DRIVER_ID, peer, nbn_wrtc_c_cli.protocol_id); nbn_wrtc_c_cli.peer = peer; } -static void NBN_WebRTC_C_Cli_OnWsClosed(int ws, void *user_ptr) -{ +static void NBN_WebRTC_C_Cli_OnWsClosed(int ws, void *user_ptr) { NBN_LogDebug("WS %d has closed", ws); - if (user_ptr) - { + if (user_ptr) { NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)user_ptr; NBN_LogDebug("Destroying server peer (peer: %d, channel: %d)", peer->id, peer->channel_id); @@ -803,41 +721,35 @@ static void NBN_WebRTC_C_Cli_OnWsClosed(int ws, void *user_ptr) } } -static void NBN_WebRTC_C_Cli_OnWsMessage(int ws, const char *msg, int size, void *user_ptr) -{ +static void NBN_WebRTC_C_Cli_OnWsMessage(int ws, const char *msg, int size, void *user_ptr) { NBN_WebRTC_C_ProcessSignalingMessage((NBN_WebRTC_C_Peer *)user_ptr, ws, msg, size, "answer"); } -static int AttemptConnection(void) -{ +static int AttemptConnection(void) { double waitTime = 1.0 / 3.0; // wait time between connection attempts in seconds int retries = 9; // approximatively 3 seconds to get connected - + struct timespec rqtp; rqtp.tv_sec = 0; rqtp.tv_nsec = waitTime * 1e9; - while (true) - { - #if defined(_WIN32) || defined(_WIN64) + while (true) { +#if defined(_WIN32) || defined(_WIN64) Sleep(waitTime * 1000); - #else - if (nanosleep(&rqtp, NULL) < 0) - { +#else + if (nanosleep(&rqtp, NULL) < 0) { NBN_LogError("nanosleep failed"); return NBN_ERROR; } - #endif - if (--retries <= 0 || nbn_wrtc_c_cli.is_connected) - { +#endif + if (--retries <= 0 || nbn_wrtc_c_cli.is_connected) { break; } } - if (!nbn_wrtc_c_cli.is_connected) - { + if (!nbn_wrtc_c_cli.is_connected) { NBN_LogError("Failed to connect"); return NBN_ERROR; @@ -846,8 +758,7 @@ static int AttemptConnection(void) return 0; } -static int NBN_WebRTC_C_CliStart(uint32_t protocol_id, const char *host, uint16_t port) -{ +static int NBN_WebRTC_C_CliStart(uint32_t protocol_id, const char *host, uint16_t port) { rtcInitLogger(nbn_wrtc_c_cfg.log_level, NBN_WebRTC_C_Log); rtcPreload(); @@ -857,8 +768,7 @@ static int NBN_WebRTC_C_CliStart(uint32_t protocol_id, const char *host, uint16_ int cli_ws; - if ((cli_ws = rtcCreateWebSocket(ws_addr)) < 0) - { + if ((cli_ws = rtcCreateWebSocket(ws_addr)) < 0) { NBN_LogError("Failed to create websocket"); return NBN_ERROR; } @@ -875,12 +785,10 @@ static int NBN_WebRTC_C_CliStart(uint32_t protocol_id, const char *host, uint16_ return AttemptConnection(); } -static void NBN_WebRTC_C_CliStop(void) -{ +static void NBN_WebRTC_C_CliStop(void) { NBN_WebRTC_C_Peer *peer = nbn_wrtc_c_cli.peer; - if (peer) - { + if (peer) { NBN_WebRTC_C_DestroyPeer(peer); } @@ -888,14 +796,12 @@ static void NBN_WebRTC_C_CliStop(void) rtcCleanup(); } -static int NBN_WebRTC_C_CliRecvPackets(void) -{ +static int NBN_WebRTC_C_CliRecvPackets(void) { const int buffer_size = sizeof(nbn_wrtc_c_cli.packet_buffer); int size = buffer_size; NBN_WebRTC_C_Peer *peer = nbn_wrtc_c_cli.peer; - while (rtcReceiveMessage(peer->channel_id, nbn_wrtc_c_cli.packet_buffer, &size) == RTC_ERR_SUCCESS) - { + while (rtcReceiveMessage(peer->channel_id, nbn_wrtc_c_cli.packet_buffer, &size) == RTC_ERR_SUCCESS) { NBN_Packet packet; if (NBN_Packet_InitRead(&packet, peer->conn, (uint8_t *)nbn_wrtc_c_cli.packet_buffer, size) < 0) @@ -909,12 +815,10 @@ static int NBN_WebRTC_C_CliRecvPackets(void) return 0; } -static int NBN_WebRTC_C_CliSendPacket(NBN_Packet *packet) -{ +static int NBN_WebRTC_C_CliSendPacket(NBN_Packet *packet) { NBN_WebRTC_C_Peer *peer = nbn_wrtc_c_cli.peer; - if (rtcSendMessage(peer->channel_id, (char *)packet->buffer, packet->size) < 0) - { + if (rtcSendMessage(peer->channel_id, (char *)packet->buffer, packet->size) < 0) { NBN_LogError("rtcSendMessage failed for peer %d", peer->id); return NBN_ERROR; } @@ -924,16 +828,14 @@ static int NBN_WebRTC_C_CliSendPacket(NBN_Packet *packet) #pragma endregion /* Game client */ -void NBN_WebRTC_C_Register(NBN_WebRTC_C_Config config) -{ - const char **ice_servers = (const char**)NBN_Allocator(sizeof(char *) * config.ice_servers_count); +void NBN_WebRTC_C_Register(NBN_WebRTC_C_Config config) { + const char **ice_servers = (const char **)malloc(sizeof(char *) * config.ice_servers_count); - for (unsigned int i = 0; i < config.ice_servers_count; i++) - { - // strdup equivalent using NBN_Allocator, make sure this get freed + for (unsigned int i = 0; i < config.ice_servers_count; i++) { + // strdup equivalent using malloc, make sure this get freed const char *str = config.ice_servers[i]; size_t len = strlen(str); - char *dup_str = (char*)NBN_Allocator(len + 1); + char *dup_str = (char *)malloc(len + 1); memcpy(dup_str, str, len + 1); ice_servers[i] = dup_str; @@ -943,39 +845,29 @@ void NBN_WebRTC_C_Register(NBN_WebRTC_C_Config config) nbn_wrtc_c_cfg.ice_servers_count = config.ice_servers_count; nbn_wrtc_c_cfg.enable_tls = config.enable_tls; - if (config.enable_tls) - { + if (config.enable_tls) { nbn_wrtc_c_cfg.cert_path = config.cert_path; nbn_wrtc_c_cfg.key_path = config.key_path; nbn_wrtc_c_cfg.passphrase = config.passphrase; } - NBN_DriverImplementation driver_impl = { - // Client implementation - NBN_WebRTC_C_CliStart, - NBN_WebRTC_C_CliStop, - NBN_WebRTC_C_CliRecvPackets, - NBN_WebRTC_C_CliSendPacket, + NBN_DriverImplementation driver_impl = {// Client implementation + NBN_WebRTC_C_CliStart, NBN_WebRTC_C_CliStop, NBN_WebRTC_C_CliRecvPackets, + NBN_WebRTC_C_CliSendPacket, - // Server implementation - NBN_WebRTC_C_ServStart, - NBN_WebRTC_C_ServStop, - NBN_WebRTC_C_ServRecvPackets, - NBN_WebRTC_C_ServSendPacketTo, - NBN_WebRTC_C_ServRemoveClientConnection - }; + // Server implementation + NBN_WebRTC_C_ServStart, NBN_WebRTC_C_ServStop, NBN_WebRTC_C_ServRecvPackets, + NBN_WebRTC_C_ServSendPacketTo, NBN_WebRTC_C_ServRemoveClientConnection}; NBN_Driver_Register(NBN_WEBRTC_C_DRIVER_ID, NBN_WEBRTC_C_DRIVER_NAME, driver_impl); } -void NBN_WebRTC_C_Unregister(void) -{ - for (unsigned int i = 0; i < nbn_wrtc_c_cfg.ice_servers_count; i++) - { - NBN_Deallocator((void *)nbn_wrtc_c_cfg.ice_servers[i]); +void NBN_WebRTC_C_Unregister(void) { + for (unsigned int i = 0; i < nbn_wrtc_c_cfg.ice_servers_count; i++) { + free((void *)nbn_wrtc_c_cfg.ice_servers[i]); } - NBN_Deallocator(nbn_wrtc_c_cfg.ice_servers); + free(nbn_wrtc_c_cfg.ice_servers); } #endif // NBNET_IMPL diff --git a/soak/CMakeLists.txt b/soak/CMakeLists.txt index 3cca67d..b68b584 100644 --- a/soak/CMakeLists.txt +++ b/soak/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(soak) diff --git a/soak/client.c b/soak/client.c index ead7523..7b7ae90 100644 --- a/soak/client.c +++ b/soak/client.c @@ -39,6 +39,8 @@ #endif // __EMSCRIPTEN__ +#include + typedef struct { uint8_t data[SOAK_MESSAGE_BIG_MAX_LENGTH]; uint8_t channel_id; @@ -305,12 +307,10 @@ int main(int argc, char *argv[]) { #endif // __EMSCRIPTEN__ - NBN_GameClient_Config config = - NBN_GameClient_CreateConfig(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT, AllocateMessage, DeallocateMessage); - - NBN_GameClient_EnableCustomChannels(&config, options.channel_count); + NBN_GameClient_Init(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT); + NBN_GameClient_EnableCustomChannels(options.channel_count); - if (NBN_GameClient_Start(config) < 0) { + if (NBN_GameClient_Start() < 0) { Soak_LogError("Failed to start game client. Exit"); #ifdef __EMSCRIPTEN__ diff --git a/soak/server.c b/soak/server.c index 8ea7be6..b0fef0a 100644 --- a/soak/server.c +++ b/soak/server.c @@ -351,12 +351,11 @@ int main(int argc, char *argv[]) { #endif // WEBRTC_NATIVE SoakOptions options = Soak_GetOptions(); - NBN_GameServer_Config config = - NBN_GameServer_CreateConfig(SOAK_PROTOCOL_NAME, SOAK_PORT, AllocateMessage, DeallocateMessage); - NBN_GameServer_EnableCustomChannels(&config, options.channel_count); + NBN_GameServer_Init(SOAK_PROTOCOL_NAME, SOAK_PORT); + NBN_GameServer_EnableCustomChannels(options.channel_count); - if (NBN_GameServer_Start(config)) { + if (NBN_GameServer_Start()) { Soak_LogError("Failed to start game server"); return 1; diff --git a/soak/soak.c b/soak/soak.c index d579684..7fd2ae0 100644 --- a/soak/soak.c +++ b/soak/soak.c @@ -22,7 +22,9 @@ */ +#include #include +#include #include #ifdef __EMSCRIPTEN__ @@ -247,13 +249,18 @@ int SoakMessage_Read(NBN_Reader *reader, unsigned int *msg_id, uint8_t *data, un return 0; } -uint8_t *AllocateMessage(uint8_t type, uint16_t *length) { - if (type == SOAK_MESSAGE_SMALL) { - *length = SOAK_MESSAGE_SMALL_MAX_DATA_LENGTH + 32; - return (uint8_t *)NBN_Allocator(*length); +bool AllocateMessage(NBN_MessageHeader header, NBN_MessageType type, uint8_t **buffer) { + if (header.type == SOAK_MESSAGE_SMALL) { + *buffer = malloc(SOAK_MESSAGE_SMALL_MAX_DATA_LENGTH + 32); + + return true; } - NBN_Abort(); + return false; } -void DeallocateMessage(uint8_t type, uint8_t *data) { NBN_Deallocator(data); } +void DeallocateMessage(NBN_MessageHeader header, NBN_MessageType type, uint8_t *buffer) { + assert(header.type == SOAK_MESSAGE_SMALL); + + free(buffer); +} diff --git a/soak/soak.h b/soak/soak.h index 18f660f..971ee63 100644 --- a/soak/soak.h +++ b/soak/soak.h @@ -89,7 +89,7 @@ unsigned int Soak_GetCreatedIncomingSoakMessageCount(void); unsigned int Soak_GetDestroyedIncomingSoakMessageCount(void); void SoakMessage_Write(NBN_Writer *, unsigned int, uint8_t *, unsigned int); int SoakMessage_Read(NBN_Reader *reader, unsigned int *msg_id, uint8_t *data, unsigned int *data_length); -uint8_t *AllocateMessage(uint8_t type, uint16_t *); -void DeallocateMessage(uint8_t type, uint8_t *data); +bool AllocateMessage(NBN_MessageHeader header, NBN_MessageType type, uint8_t **buffer); +void DeallocateMessage(NBN_MessageHeader header, NBN_MessageType type, uint8_t *buffer); #endif // SOAK_H_INCLUDED From 106a3ade439c3a0bf85d39775f74ac81c87b776c Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sun, 25 Jan 2026 10:30:41 +0100 Subject: [PATCH 09/85] inlude nbnet.h in drivers --- examples/echo/CMakeLists.txt | 2 +- examples/echo/client.c | 5 ++--- examples/echo/server.c | 5 ++--- examples/echo/shared.c | 10 +--------- examples/echo/shared.h | 2 -- nbnet.h | 1 - net_drivers/udp.h | 4 ++++ net_drivers/webrtc.h | 4 ++++ net_drivers/webrtc_c.h | 4 ++++ 9 files changed, 18 insertions(+), 19 deletions(-) diff --git a/examples/echo/CMakeLists.txt b/examples/echo/CMakeLists.txt index 78aaf46..97a0519 100644 --- a/examples/echo/CMakeLists.txt +++ b/examples/echo/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(echo) diff --git a/examples/echo/client.c b/examples/echo/client.c index 4d83105..e69d404 100644 --- a/examples/echo/client.c +++ b/examples/echo/client.c @@ -152,10 +152,9 @@ int main(int argc, char *argv[]) { // Start the client with a protocol name (must be the same than the one used by the server) // the server host and port - NBN_GameClient_Config config = NBN_GameClient_CreateConfig(ECHO_PROTOCOL_NAME, "127.0.0.1", ECHO_EXAMPLE_PORT, - AllocateMessage, DeallocateMessage); + NBN_GameClient_Init(ECHO_PROTOCOL_NAME, "127.0.0.1", ECHO_EXAMPLE_PORT); - if (NBN_GameClient_Start(config) < 0) { + if (NBN_GameClient_Start() < 0) { Log(LOG_ERROR, "Failed to start client"); // Error, quit the client application diff --git a/examples/echo/server.c b/examples/echo/server.c index b712e7e..bc49fec 100644 --- a/examples/echo/server.c +++ b/examples/echo/server.c @@ -117,10 +117,9 @@ int main(int argc, const char **argv) { // Start the server with a protocol name and a port - NBN_GameServer_Config config = - NBN_GameServer_CreateConfig(ECHO_PROTOCOL_NAME, ECHO_EXAMPLE_PORT, AllocateMessage, DeallocateMessage); + NBN_GameServer_Init(ECHO_PROTOCOL_NAME, ECHO_EXAMPLE_PORT); - if (NBN_GameServer_Start(config) < 0) { + if (NBN_GameServer_Start() < 0) { Log(LOG_ERROR, "Failed to start the server"); // Error, quit the server application diff --git a/examples/echo/shared.c b/examples/echo/shared.c index b7f0bbb..0ec6d8f 100644 --- a/examples/echo/shared.c +++ b/examples/echo/shared.c @@ -20,6 +20,7 @@ */ +#include #include #include #include @@ -67,12 +68,3 @@ void Log(int type, const char *fmt, ...) { va_end(args); } - -uint8_t *AllocateMessage(uint8_t type, uint16_t *length) { - NBN_Assert(type == ECHO_MESSAGE_TYPE); - - *length = ECHO_MESSAGE_MAX_LENGTH; - return (uint8_t *)NBN_Allocator(*length); -} - -void DeallocateMessage(uint8_t type, uint8_t *data) { NBN_Deallocator(data); } diff --git a/examples/echo/shared.h b/examples/echo/shared.h index 27ae633..a683e09 100644 --- a/examples/echo/shared.h +++ b/examples/echo/shared.h @@ -63,7 +63,5 @@ void Log(int, const char *, ...); #endif // __EMSCRIPTEN__ void EchoSleep(double); -uint8_t *AllocateMessage(uint8_t type, uint16_t *); -void DeallocateMessage(uint8_t type, uint8_t *data); #endif /* ECHO_EXAMPLE_SHARED_H */ diff --git a/nbnet.h b/nbnet.h index cabbc17..7a67017 100644 --- a/nbnet.h +++ b/nbnet.h @@ -284,7 +284,6 @@ int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); #define NBN_MAX_CUSTOM_CHANNELS 8 #define NBN_CHANNEL_BUFFER_SIZE 1024 -#define NBN_CHANNEL_OUTGOING_MESSAGE_POOL_SIZE 512 /* Library reserved unreliable ordered channel */ #define NBN_CHANNEL_RESERVED_UNRELIABLE 0 diff --git a/net_drivers/udp.h b/net_drivers/udp.h index a42fc15..14a8d83 100644 --- a/net_drivers/udp.h +++ b/net_drivers/udp.h @@ -44,6 +44,10 @@ void NBN_UDP_Register(void); #include #include +#ifndef NBNET_H +#include "../nbnet.h" +#endif + #define NBN_UDP_DRIVER_ID 0 #define NBN_UDP_DRIVER_NAME "UDP" diff --git a/net_drivers/webrtc.h b/net_drivers/webrtc.h index 95204c5..f0566b8 100644 --- a/net_drivers/webrtc.h +++ b/net_drivers/webrtc.h @@ -34,6 +34,10 @@ freely, subject to the following restrictions: #include +#ifndef NBNET_H +#include "../nbnet.h" +#endif + typedef struct NBN_WebRTC_Config { bool enable_tls; const char *cert_path; diff --git a/net_drivers/webrtc_c.h b/net_drivers/webrtc_c.h index 2b246c4..4f0e8b3 100644 --- a/net_drivers/webrtc_c.h +++ b/net_drivers/webrtc_c.h @@ -44,6 +44,10 @@ freely, subject to the following restrictions: #include #include +#ifndef NBNET_H +#include "../nbnet.h" +#endif + #define NBN_WEBRTC_C_DRIVER_ID 2 #define NBN_WEBRTC_C_DRIVER_NAME "WebRTC_C" From 162969c3bde0032315f9378349f70e00345f4302 Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sun, 25 Jan 2026 12:42:56 +0100 Subject: [PATCH 10/85] statically allocated messages --- examples/echo/server.c | 4 + nbnet.h | 479 ++++++++++------------------------------- soak/client.c | 49 +---- soak/server.c | 44 +--- soak/soak.c | 25 +-- soak/soak.h | 4 +- 6 files changed, 140 insertions(+), 465 deletions(-) diff --git a/examples/echo/server.c b/examples/echo/server.c index bc49fec..da8d284 100644 --- a/examples/echo/server.c +++ b/examples/echo/server.c @@ -181,6 +181,10 @@ int main(int argc, const char **argv) { } } + if (error) { + break; + } + // Pack all enqueued messages as packets and send them if (NBN_GameServer_SendPackets() < 0) { Log(LOG_ERROR, "Failed to send packets"); diff --git a/nbnet.h b/nbnet.h index 7a67017..533e126 100644 --- a/nbnet.h +++ b/nbnet.h @@ -156,7 +156,7 @@ int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length #define NBN_MESSAGE_RESEND_DELAY 0.1 /* Number of seconds before a message is resent (reliable messages redundancy) */ #define NBN_MESSAGE_HEADER_SIZE 6 /* See NBN_MessageHeader struct */ #define NBN_RESERVED_MESSAGE_BUFFER_LEN 32 /* Fixed size used for all library reserved messages' buffer */ -#define NBN_SCRATCH_WRITE_BUFFER_LEN 256 +#define NBN_MESSAGE_MAX_SIZE 512 // IMPORTANT: make sure you update NBN_MESSAGE_HEADER_SIZE if you modify NBN_MessageHeader struct typedef struct NBN_MessageHeader { @@ -172,15 +172,14 @@ typedef struct NBN_Message { NBN_MessageHeader header; NBN_MessageType type; NBN_Connection *sender; - unsigned int ref_count; - bool custom_allocator; - uint8_t *data; + uint8_t data[NBN_MESSAGE_MAX_SIZE]; } NBN_Message; typedef struct NBN_OutgoingMessage { - NBN_Message *message; - uint16_t id; + NBN_Message message; + uint16_t id; // TODO: needed? double last_send_time; + bool free; } NBN_OutgoingMessage; typedef struct NBN_IncomingMessage { @@ -282,7 +281,13 @@ int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); #pragma region NBN_Channel -#define NBN_MAX_CUSTOM_CHANNELS 8 +#ifndef NBN_CHANNEL_COUNT +/** + * Number of channels per connection. + */ +#define NBN_CHANNEL_COUNT 2 +#endif + #define NBN_CHANNEL_BUFFER_SIZE 1024 /* Library reserved unreliable ordered channel */ @@ -370,10 +375,8 @@ struct NBN_Connection { uint8_t is_stale : 1; uint8_t is_closed : 1; struct NBN_Endpoint *endpoint; - NBN_Driver *driver; /* Network driver used for that connection */ - // TODO: do we need to allocate channels on the heap? - NBN_Channel **channels; /* Message channels (sending & receiving) */ - unsigned int channel_count; + NBN_Driver *driver; /* Network driver used for that connection */ + NBN_Channel channels[NBN_CHANNEL_COUNT]; /* Message channels (sending & receiving) */ NBN_ConnectionStats stats; void *driver_data; /* Data attached to the connection by the underlying driver */ void *user_data; /* Pointer to user-defined data */ @@ -562,7 +565,6 @@ void NBN_PacketSimulator_Stop(NBN_PacketSimulator *); typedef struct NBN_Endpoint_Config { const char *protocol_name; uint16_t port; - unsigned int custom_reliable_channels; NBN_MessageAllocator msg_allocator; NBN_MessageDeallocator msg_deallocator; } NBN_Endpoint_Config; @@ -570,19 +572,13 @@ typedef struct NBN_Endpoint_Config { struct NBN_Endpoint { NBN_EventQueue event_queue; uint32_t protocol_id; - unsigned int custom_reliable_channels; - unsigned int active_out_msg_count; - unsigned int active_inc_msg_buffer_count; - unsigned int active_out_msg_buffer_count; bool is_server; double time; - NBN_Message *write_message; + NBN_Message write_message; + bool can_create_message; unsigned int write_message_buffer_len; NBN_Writer message_writer; NBN_Reader message_reader; - uint8_t scratch_write_buffer[NBN_SCRATCH_WRITE_BUFFER_LEN]; - NBN_Message last_received_message; - bool last_message_need_free; NBN_MessageAllocator msg_allocator; NBN_MessageDeallocator msg_deallocator; @@ -642,12 +638,6 @@ extern NBN_GameClient nbn_game_client; */ void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port); -// TODO: doc -void NBN_GameClient_EnableCustomChannels(unsigned int count); - -// TODO: doc -void NBN_GameClient_EnableCustomMessageAllocation(NBN_MessageAllocator allocator, NBN_MessageDeallocator deallocator); - // TODO: doc NBN_Writer *NBN_GameClient_GetConnectionDataWriter(void); @@ -702,11 +692,6 @@ NBN_Writer *NBN_GameClient_GetMessageWriter(void); // TODO: doc NBN_Reader *NBN_GameClient_GetMessageReader(void); -int NBN_GameClient_GetActiveOutgoingMessageCount(void); - -int NBN_GameClient_GetActiveOutgoingMessageBufferCount(void); -int NBN_GameClient_GetActiveIncomingMessageBufferCount(void); - /** * Send a message to the server, unreliably. * @@ -823,12 +808,6 @@ extern NBN_GameServer nbn_game_server; */ void NBN_GameServer_Init(const char *protocol_name, uint16_t port); -// TODO: doc -void NBN_GameServer_EnableCustomChannels(unsigned int count); - -// TODO: doc -void NBN_GameServer_EnableCustomMessageAllocation(NBN_MessageAllocator allocator, NBN_MessageDeallocator deallocator); - /** * Start the game server with the provided configuration. * @@ -1282,14 +1261,11 @@ void NBN_Packet_InitWrite(NBN_Packet *packet, uint32_t protocol_id, uint16_t seq } int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_OutgoingMessage *out_msg) { - NBN_Message *message = out_msg->message; + NBN_Message *message = &out_msg->message; NBN_LogTrace("Write message %d (type: %d, length: %d) to packet %d", out_msg->id, message->header.type, message->header.length, packet->header.seq_number); - NBN_Assert((message->data != NULL && message->header.length > 0) || - (message->data == NULL && message->header.length == 0)); - if (packet->mode != NBN_PACKET_MODE_WRITE || packet->sealed) return NBN_PACKET_WRITE_ERROR; @@ -1311,8 +1287,7 @@ int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_OutgoingMessage *out_msg) { NBN_Assert(writer.position == NBN_MESSAGE_HEADER_SIZE); - if (message->data) { - NBN_Assert(message->header.length > 0); + if (message->header.length > 0) { NBN_Writer_WriteBytes(&writer, message->data, message->header.length); } @@ -1395,30 +1370,9 @@ int NBN_Packet_InitRead(NBN_Packet *packet, uint32_t protocol_id, unsigned int s #pragma region NBN_Channel -static void Endpoint_FreeMessage(NBN_Endpoint *endpoint, NBN_Message *message); static unsigned int Channel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2); -void NBN_Channel_Destroy(NBN_Endpoint *endpoint, NBN_Channel *channel) { - for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) { - NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[i]; - - if (!inc_msg->free) { - Endpoint_FreeMessage(endpoint, &inc_msg->message); - } - - NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[i]; - - if (out_msg->message) { - Endpoint_FreeMessage(endpoint, out_msg->message); - } - } - - free(channel); -} - -static NBN_Channel *Channel_Create(uint8_t id, NBN_ChannelType type) { - NBN_Channel *channel = malloc(sizeof(NBN_Channel)); - +static void Channel_Init(NBN_Channel *channel, uint8_t id, NBN_ChannelType type) { channel->id = id; channel->type = type; channel->next_outgoing_message_id = 0; @@ -1431,13 +1385,11 @@ static NBN_Channel *Channel_Create(uint8_t id, NBN_ChannelType type) { for (unsigned int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) { channel->incoming_messages_buffer[i].free = true; - channel->outgoing_messages_buffer[i].message = NULL; + channel->outgoing_messages_buffer[i].free = true; } for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) channel->ack_buffer[i] = false; - - return channel; } static void Channel_UpdateMessageSendTime(NBN_Channel *channel, uint16_t msg_id, double time) { @@ -1453,8 +1405,8 @@ static bool Channel_AddReceivedMessage(NBN_Endpoint *endpoint, NBN_Channel *chan NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; - inc_msg->message = *message; inc_msg->free = false; + memcpy(&inc_msg->message, message, sizeof(NBN_Message)); channel->last_received_message_id = message->header.id; @@ -1488,12 +1440,8 @@ static bool Channel_AddReceivedMessage(NBN_Endpoint *endpoint, NBN_Channel *chan NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; - if (!inc_msg->free) { - Endpoint_FreeMessage(endpoint, &inc_msg->message); - } - - inc_msg->message = *message; inc_msg->free = false; + memcpy(&inc_msg->message, message, sizeof(NBN_Message)); return true; } @@ -1502,53 +1450,32 @@ static bool Channel_AddReceivedMessage(NBN_Endpoint *endpoint, NBN_Channel *chan } static bool Channel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *message) { + NBN_Assert(channel->type == NBN_CHANNEL_UNRELIABLE || channel->type == NBN_CHANNEL_RELIABLE); + uint16_t msg_id = channel->next_outgoing_message_id; int index = msg_id % NBN_CHANNEL_BUFFER_SIZE; NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[index]; - if (channel->type == NBN_CHANNEL_UNRELIABLE) { - // make sure this buffer slot is not already in use - if (out_msg->message) { - NBN_LogError("No outgoing message available in unreliable channel %d (outgoing message count: %d)", - channel->id, channel->outgoing_message_count); - - return false; - } - - out_msg->id = msg_id; - out_msg->message = message; - out_msg->last_send_time = -1; - - channel->next_outgoing_message_id++; - channel->outgoing_message_count++; - - return true; - } else if (channel->type == NBN_CHANNEL_RELIABLE) { - // make sure the outgoing message is not already in use - if (out_msg->message) { - NBN_LogError("No outgoing message available in reliable channel %d (outgoing message count: %d)", - channel->id, channel->outgoing_message_count); + // make sure the outgoing message is not already in use + if (!out_msg->free) { + NBN_LogError("No outgoing message available in channel %d (type: %d, outgoing message count: %d)", + channel->type, channel->id, channel->outgoing_message_count); #ifdef NBN_DEBUG - NBN_Abort(); + NBN_Abort(); #endif - return false; - } - - out_msg->message = message; - out_msg->id = msg_id; - out_msg->last_send_time = -1; + return false; + } - channel->next_outgoing_message_id++; - channel->outgoing_message_count++; + out_msg->id = msg_id; + out_msg->free = false; + out_msg->last_send_time = -1; + memcpy(&out_msg->message, message, sizeof(NBN_Message)); - NBN_LogTrace("Added outgoing message %d of type %d to reliable channel %d (index: %d)", msg_id, - message->header.type, channel->id, index); + channel->next_outgoing_message_id++; + channel->outgoing_message_count++; - return true; - } else { - NBN_Abort(); - } + return true; } static NBN_Message *Channel_GetNextRecvedMessage(NBN_Channel *channel) { @@ -1585,11 +1512,11 @@ static bool Channel_GetNextOutgoingMessage(NBN_Channel *channel, NBN_OutgoingMes if (channel->type == NBN_CHANNEL_UNRELIABLE) { NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[channel->next_outgoing_message_slot]; - if (out_msg->message == NULL) + if (out_msg->free) return false; *res_out_msg = *out_msg; - out_msg->message = NULL; + out_msg->free = true; channel->next_outgoing_message_slot++; channel->next_outgoing_message_slot %= NBN_CHANNEL_BUFFER_SIZE; @@ -1605,7 +1532,7 @@ static bool Channel_GetNextOutgoingMessage(NBN_Channel *channel, NBN_OutgoingMes while (SEQUENCE_NUMBER_LT(msg_id, max_message_id)) { NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; - if (out_msg->message && + if (!out_msg->free && (out_msg->last_send_time < 0 || time - out_msg->last_send_time >= NBN_MESSAGE_RESEND_DELAY)) { *res_out_msg = *out_msg; return true; @@ -1625,7 +1552,6 @@ static int Channel_OnMessageSent(NBN_Endpoint *endpoint, NBN_Channel *channel, N return 0; } - Endpoint_FreeMessage(endpoint, message); channel->outgoing_message_count--; return 0; @@ -1639,11 +1565,10 @@ static int Channel_OnOutgoingMessageAcked(NBN_Endpoint *endpoint, NBN_Channel *c int index = msg_id % NBN_CHANNEL_BUFFER_SIZE; NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[index]; - if (out_msg->message == NULL || out_msg->id != msg_id) + if (out_msg->free || out_msg->id != msg_id) return 0; - NBN_Message *message = out_msg->message; - out_msg->message = NULL; + out_msg->free = true; NBN_LogTrace("Message %d acked on channel %d (buffer index: %d, oldest unacked: %d)", msg_id, channel->id, index, channel->oldest_unacked_message_id); @@ -1668,8 +1593,6 @@ static int Channel_OnOutgoingMessageAcked(NBN_Endpoint *endpoint, NBN_Channel *c channel->oldest_unacked_message_id); } - Endpoint_FreeMessage(endpoint, message); - return 0; } @@ -1718,7 +1641,6 @@ NBN_Connection *NBN_Connection_Create(uint32_t id, NBN_Endpoint *endpoint, NBN_D connection->is_closed = false; connection->vector_pos = -1; connection->user_data = NULL; - connection->channels = NULL; for (int i = 0; i < NBN_MAX_PACKET_ENTRIES; i++) { connection->packet_send_seq_buffer[i] = 0xFFFFFFFF; @@ -1734,15 +1656,7 @@ NBN_Connection *NBN_Connection_Create(uint32_t id, NBN_Endpoint *endpoint, NBN_D return connection; } -void NBN_Connection_Destroy(NBN_Endpoint *endpoint, NBN_Connection *connection) { - for (unsigned int i = 0; i < connection->channel_count; i++) { - NBN_Channel *channel = connection->channels[i]; - - NBN_Channel_Destroy(endpoint, channel); - } - - free(connection); -} +void NBN_Connection_Destroy(NBN_Endpoint *endpoint, NBN_Connection *connection) { free(connection); } int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, double time) { @@ -1770,9 +1684,8 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection for (int i = 0; i < packet->header.messages_count; i++) { NBN_LogTrace("Reading message number %d from packet %d", i, packet->header.seq_number); - NBN_Message message = {0}; + static NBN_Message message = {0}; message.type = NBN_INCOMING_MESSAGE; - message.custom_allocator = false; int msg_len = Connection_ReadNextMessageFromBuffer(endpoint, &msg_reader, &message); if (msg_len < 0) { @@ -1783,13 +1696,13 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection uint8_t channel_id = message.header.channel_id; - if (channel_id > connection->channel_count - 1) { + if (channel_id > NBN_CHANNEL_COUNT - 1) { NBN_LogError("Failed to read packet, message had invalid channel"); return NBN_ERROR; } - NBN_Channel *channel = connection->channels[channel_id]; + NBN_Channel *channel = &connection->channels[channel_id]; if (Channel_AddReceivedMessage(endpoint, channel, &message)) { NBN_LogTrace("Received message %d (type: %d) on channel %d", message.header.id, message.header.type, @@ -1801,8 +1714,6 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection #endif } else { NBN_LogDebug("Received message %d : discarded", message.header.id); - - Endpoint_FreeMessage(endpoint, &message); } } @@ -1820,8 +1731,8 @@ int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connect Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); - for (unsigned int i = 0; i < connection->channel_count; i++) { - NBN_Channel *channel = connection->channels[i]; + for (unsigned int i = 0; i < NBN_CHANNEL_COUNT; i++) { + NBN_Channel *channel = &connection->channels[i]; NBN_LogTrace("Flushing channel %d (message count: %d)", channel->id, channel->outgoing_message_count); @@ -1831,7 +1742,7 @@ int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connect // TODO: use bandwidth to determine how many packets to send at most while (j < channel->outgoing_message_count && sent_packet_count < NBN_CONNECTION_MAX_SENT_PACKET_COUNT && Channel_GetNextOutgoingMessage(channel, &out_msg, time)) { - NBN_Message *message = out_msg.message; + NBN_Message *message = &out_msg.message; uint16_t msg_id = out_msg.id; bool message_sent = false; int ret = NBN_Packet_WriteMessage(&packet, &out_msg); @@ -1963,7 +1874,7 @@ static int Connection_AckPacket(NBN_Endpoint *endpoint, NBN_Connection *connecti for (unsigned int i = 0; i < packet_entry->messages_count; i++) { NBN_MessageEntry *msg_entry = &packet_entry->messages[i]; - NBN_Channel *channel = connection->channels[msg_entry->channel_id]; + NBN_Channel *channel = &connection->channels[msg_entry->channel_id]; NBN_Assert(channel != NULL); @@ -2093,23 +2004,15 @@ static int Connection_ReadNextMessageFromBuffer(NBN_Endpoint *endpoint, NBN_Read return NBN_ERROR; } - if (message->header.length > 0) { - bool custom_allocator = - endpoint->msg_allocator != NULL && endpoint->msg_allocator(message->header, message->type, &message->data); - - message->custom_allocator = custom_allocator; + uint16_t msg_len = message->header.length; - if (!custom_allocator) { - message->data = malloc(message->header.length); + if (msg_len > 0) { + if (msg_len > NBN_MESSAGE_MAX_SIZE) { + NBN_LogError("Failed to read message: too big"); } - NBN_Assert(message->data != NULL); - - endpoint->active_inc_msg_buffer_count++; - - if (NBN_Reader_ReadBytes(reader, message->data, message->header.length) < 0) { + if (NBN_Reader_ReadBytes(reader, message->data, msg_len) < 0) { NBN_LogError("Failed to read message data"); - Endpoint_FreeMessage(endpoint, message); return NBN_ERROR; } @@ -2118,40 +2021,6 @@ static int Connection_ReadNextMessageFromBuffer(NBN_Endpoint *endpoint, NBN_Read return 0; } -static void Endpoint_FreeMessage(NBN_Endpoint *endpoint, NBN_Message *message) { - NBN_Assert(message->type == NBN_OUTGOING_MESSAGE || message->type == NBN_INCOMING_MESSAGE); - - if (message->type == NBN_OUTGOING_MESSAGE) { - NBN_Assert(message->ref_count > 0); - - message->ref_count--; - - if (message->ref_count == 0) { - if (message->header.length > 0) { - if (message->custom_allocator) { - endpoint->msg_deallocator(message->header, message->type, message->data); - } else { - free(message->data); - } - - endpoint->active_out_msg_buffer_count--; - } - - free(message); - endpoint->active_out_msg_count--; - } - } else if (message->type == NBN_INCOMING_MESSAGE) { - if (message->header.length > 0) { - if (message->custom_allocator) { - endpoint->msg_deallocator(message->header, message->type, message->data); - } else { - free(message->data); - } - endpoint->active_inc_msg_buffer_count--; - } - } -} - static void Connection_UpdateAveragePing(NBN_Connection *connection, double ping) { /* exponential smoothing with a factor of 0.05 */ connection->stats.ping = connection->stats.ping + .05f * (ping - connection->stats.ping); @@ -2246,20 +2115,14 @@ static void Endpoint_Deinit(NBN_Endpoint *); static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, uint32_t, int, void *); static uint32_t Endpoint_BuildProtocolId(const char *); static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *, NBN_Packet *, NBN_Connection *); -static void Endpoint_CreateMessageBuffer(NBN_Endpoint *, NBN_Message *); static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN_Message *); static void Endpoint_UpdateTime(NBN_Endpoint *); static void Endpoint_Init(NBN_Endpoint *endpoint, NBN_Endpoint_Config config, uint32_t protocol_id, bool is_server) { endpoint->is_server = is_server; endpoint->protocol_id = protocol_id; - endpoint->write_message = NULL; - endpoint->write_message_buffer_len = sizeof(endpoint->scratch_write_buffer); - endpoint->last_message_need_free = false; - endpoint->custom_reliable_channels = config.custom_reliable_channels; - endpoint->active_out_msg_count = 0; - endpoint->active_inc_msg_buffer_count = 0; - endpoint->active_out_msg_buffer_count = 0; + endpoint->write_message_buffer_len = sizeof(endpoint->write_message.data); + endpoint->can_create_message = true; endpoint->msg_allocator = config.msg_allocator; endpoint->msg_deallocator = config.msg_deallocator; @@ -2293,19 +2156,8 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_ NBN_Connection *connection = NBN_Connection_Create(id, endpoint, driver, driver_data); - connection->channel_count = 2 + endpoint->custom_reliable_channels; - connection->channels = malloc(sizeof(NBN_Channel *) * connection->channel_count); - - // create library channels - connection->channels[NBN_CHANNEL_RESERVED_UNRELIABLE] = - Channel_Create(NBN_CHANNEL_RESERVED_UNRELIABLE, NBN_CHANNEL_UNRELIABLE); - connection->channels[NBN_CHANNEL_RESERVED_RELIABLE] = - Channel_Create(NBN_CHANNEL_RESERVED_RELIABLE, NBN_CHANNEL_RELIABLE); - - // create custom user-defined reliable channels - - for (uint8_t chan_id = 2; chan_id < connection->channel_count; chan_id++) { - connection->channels[chan_id] = Channel_Create(chan_id, NBN_CHANNEL_RELIABLE); + for (int i = 0; i < NBN_CHANNEL_COUNT; i++) { + Channel_Init(&connection->channels[i], i, NBN_CHANNEL_RELIABLE); } return connection; @@ -2338,61 +2190,24 @@ static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *pa } static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, uint8_t type, uint8_t channel_id) { - NBN_Message *message = (NBN_Message *)malloc(sizeof(NBN_Message)); - NBN_Assert(message != NULL); - - endpoint->active_out_msg_count++; + NBN_Message *message = &endpoint->write_message; message->header = (NBN_MessageHeader){-1, 0, type, channel_id}; message->sender = NULL; message->type = NBN_OUTGOING_MESSAGE; - message->data = NULL; - message->ref_count = 0; - message->custom_allocator = false; endpoint->message_writer.position = 0; - message->data = endpoint->scratch_write_buffer; - - NBN_Assert(message->data != NULL); - - endpoint->active_out_msg_buffer_count++; - endpoint->write_message = message; -} - -static void Endpoint_CreateMessageBuffer(NBN_Endpoint *endpoint, NBN_Message *message) { - uint8_t *scratch_buffer = message->data; - message->header.length = endpoint->message_writer.position; - - NBN_Assert(message->header.length <= NBN_PACKET_MAX_DATA_SIZE); - - if (message->header.length == 0) { - message->data = NULL; - } else { - bool custom_allocator = - endpoint->msg_allocator != NULL && endpoint->msg_allocator(message->header, message->type, &message->data); - - message->custom_allocator = custom_allocator; - - if (!custom_allocator) { - message->data = malloc(message->header.length); - } - - NBN_Assert(message->data != NULL); - } - - memcpy(message->data, scratch_buffer, message->header.length); + endpoint->can_create_message = false; } static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Message *message) { NBN_Assert(!connection->is_closed || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); NBN_Assert(!connection->is_stale); - NBN_Channel *channel = connection->channels[message->header.channel_id]; + NBN_Channel *channel = &connection->channels[message->header.channel_id]; NBN_Assert(channel); - message->ref_count++; - NBN_LogTrace("Enqueue message of type %d on channel %d", message->header.type, channel->id); if (!Channel_AddOutgoingMessage(channel, message)) { @@ -2473,23 +2288,9 @@ static int GameClient_HandleEvent(void); static int GameClient_HandleMessageReceivedEvent(void); void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port) { - nbn_game_client.config = (NBN_GameClient_Config){.host = host, - .endpoint = {.protocol_name = protocol_name, - .port = port, - .custom_reliable_channels = 0, - .msg_allocator = NULL, - .msg_deallocator = NULL}}; -} - -void NBN_GameClient_EnableCustomChannels(unsigned int count) { - NBN_Assert(count <= NBN_MAX_CUSTOM_CHANNELS); - - nbn_game_client.config.endpoint.custom_reliable_channels = count; -} - -void NBN_GameClient_EnableCustomMessageAllocation(NBN_MessageAllocator allocator, NBN_MessageDeallocator deallocator) { - nbn_game_client.config.endpoint.msg_allocator = allocator; - nbn_game_client.config.endpoint.msg_deallocator = deallocator; + nbn_game_client.config = (NBN_GameClient_Config){ + .host = host, + .endpoint = {.protocol_name = protocol_name, .port = port, .msg_allocator = NULL, .msg_deallocator = NULL}}; } NBN_Writer *NBN_GameClient_GetConnectionDataWriter(void) { @@ -2611,11 +2412,6 @@ int NBN_GameClient_Poll(void) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - if (endpoint->last_message_need_free) { - Endpoint_FreeMessage(endpoint, &endpoint->last_received_message); - endpoint->last_message_need_free = false; - } - if (nbn_game_client.server_connection->is_stale) return NBN_NO_EVENT; @@ -2648,8 +2444,8 @@ int NBN_GameClient_Poll(void) { NBN_Connection *server_conn = nbn_game_client.server_connection; - for (unsigned int i = 0; i < server_conn->channel_count; i++) { - NBN_Channel *channel = server_conn->channels[i]; + for (unsigned int i = 0; i < NBN_CHANNEL_COUNT; i++) { + NBN_Channel *channel = &server_conn->channels[i]; NBN_Message *msg; @@ -2683,7 +2479,7 @@ int NBN_GameClient_SendPackets(void) { void NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - NBN_Assert(endpoint->write_message == NULL); + NBN_Assert(endpoint->can_create_message == true); Endpoint_CreateOutgoingMessage(endpoint, type, channel_id); } @@ -2698,12 +2494,9 @@ void NBN_GameClient_CreateReliableMessage(uint8_t type) { int NBN_GameClient_SendMessage(void) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - NBN_Message *message = endpoint->write_message; - - NBN_Assert(message != NULL); - NBN_Assert(message->ref_count == 0); + NBN_Message *message = &endpoint->write_message; - Endpoint_CreateMessageBuffer(endpoint, message); + message->header.length = endpoint->message_writer.position; if (Endpoint_EnqueueOutgoingMessage(endpoint, nbn_game_client.server_connection, message) < 0) { NBN_LogError("Failed to create outgoing message"); @@ -2711,7 +2504,7 @@ int NBN_GameClient_SendMessage(void) { return NBN_ERROR; } - endpoint->write_message = NULL; + endpoint->can_create_message = true; return 0; } @@ -2719,12 +2512,12 @@ int NBN_GameClient_SendMessage(void) { NBN_Writer *NBN_GameClient_GetMessageWriter(void) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - NBN_Assert(endpoint->write_message != NULL); + NBN_Assert(!endpoint->can_create_message); NBN_Assert(endpoint->write_message_buffer_len > 0); NBN_Writer *writer = &endpoint->message_writer; - NBN_Writer_Init(writer, endpoint->write_message->data, endpoint->write_message_buffer_len); + NBN_Writer_Init(writer, endpoint->write_message.data, endpoint->write_message_buffer_len); return writer; } @@ -2742,16 +2535,6 @@ NBN_Reader *NBN_GameClient_GetMessageReader(void) { return reader; } -int NBN_GameClient_GetActiveOutgoingMessageCount(void) { return nbn_game_client.endpoint.active_out_msg_count; } - -int NBN_GameClient_GetActiveOutgoingMessageBufferCount(void) { - return nbn_game_client.endpoint.active_out_msg_buffer_count; -} - -int NBN_GameClient_GetActiveIncomingMessageBufferCount(void) { - return nbn_game_client.endpoint.active_inc_msg_buffer_count; -} - NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *driver_data) { NBN_Connection *server_connection = Endpoint_CreateConnection(&nbn_game_client.endpoint, 0, driver_id, driver_data); @@ -2795,8 +2578,13 @@ static int GameClient_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio ev.type = NBN_MESSAGE_RECEIVED; - NBN_MessageInfo msg_info = {message->header.type, message->header.channel_id, message->data, message->header.length, - 0}; + NBN_MessageInfo msg_info; + + msg_info.type = message->header.type; + msg_info.channel_id = message->header.channel_id; + msg_info.length = message->header.length; + msg_info.sender = server_connection; + msg_info.data = message->data; ev.data.message_info = msg_info; @@ -2819,13 +2607,6 @@ static int GameClient_HandleEvent(void) { static int GameClient_HandleMessageReceivedEvent(void) { NBN_MessageInfo message_info = nbn_game_client.last_event.data.message_info; NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - NBN_Message *last_received_message = &endpoint->last_received_message; - - last_received_message->data = message_info.data; - last_received_message->type = NBN_INCOMING_MESSAGE; - last_received_message->header.length = message_info.length; - - endpoint->last_message_need_free = true; int ret = NBN_NO_EVENT; @@ -2910,22 +2691,8 @@ static int GameServer_HandleEvent(void); static int GameServer_HandleMessageReceivedEvent(void); void NBN_GameServer_Init(const char *protocol_name, uint16_t port) { - nbn_game_server.config = (NBN_GameServer_Config){.endpoint = {.protocol_name = protocol_name, - .port = port, - .custom_reliable_channels = 0, - .msg_allocator = NULL, - .msg_deallocator = NULL}}; -} - -void NBN_GameServer_EnableCustomChannels(unsigned int count) { - NBN_Assert(count <= NBN_MAX_CUSTOM_CHANNELS); - - nbn_game_server.config.endpoint.custom_reliable_channels = count; -} - -void NBN_GameServer_EnableCustomMessageAllocation(NBN_MessageAllocator allocator, NBN_MessageDeallocator deallocator) { - nbn_game_server.config.endpoint.msg_allocator = allocator; - nbn_game_server.config.endpoint.msg_deallocator = deallocator; + nbn_game_server.config = (NBN_GameServer_Config){ + .endpoint = {.protocol_name = protocol_name, .port = port, .msg_allocator = NULL, .msg_deallocator = NULL}}; } int NBN_GameServer_Start(void) { @@ -2960,7 +2727,7 @@ int NBN_GameServer_Start(void) { } } - NBN_LogInfo("Started"); + NBN_LogInfo("Started (channel count: %d)", NBN_CHANNEL_COUNT); return 0; } @@ -3007,11 +2774,6 @@ int NBN_GameServer_Poll(void) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - if (endpoint->last_message_need_free) { - Endpoint_FreeMessage(endpoint, &endpoint->last_received_message); - endpoint->last_message_need_free = false; - } - if (NBN_EventQueue_IsEmpty(&endpoint->event_queue)) { if (GameServer_CloseStaleClientConnections() < 0) return NBN_ERROR; @@ -3033,8 +2795,8 @@ int NBN_GameServer_Poll(void) { for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { NBN_Connection *client = nbn_game_server.clients->connections[i]; - for (unsigned int i = 0; i < client->channel_count; i++) { - NBN_Channel *channel = client->channels[i]; + for (unsigned int i = 0; i < NBN_CHANNEL_COUNT; i++) { + NBN_Channel *channel = &client->channels[i]; if (channel) { NBN_Message *msg; @@ -3125,20 +2887,26 @@ void NBN_GameServer_CreateReliableMessage(uint8_t type) { int NBN_GameServer_SendMessageTo(NBN_Connection *conn) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - NBN_Message *message = endpoint->write_message; - NBN_Assert(message != NULL); - Endpoint_CreateMessageBuffer(endpoint, message); + NBN_Assert(!endpoint->can_create_message); + + NBN_Message *message = &endpoint->write_message; + message->header.length = endpoint->message_writer.position; + + int ret = GameServer_SendMessageTo(conn, message); - return GameServer_SendMessageTo(conn, message); + endpoint->can_create_message = true; + return ret; } int NBN_GameServer_BroadcastMessage(void) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - NBN_Message *message = endpoint->write_message; - NBN_Assert(message != NULL); + NBN_Assert(!endpoint->can_create_message); + + NBN_Message *message = &endpoint->write_message; + message->header.length = endpoint->message_writer.position; - Endpoint_CreateMessageBuffer(endpoint, message); + int ret = 0; for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { NBN_Connection *conn = nbn_game_server.clients->connections[i]; @@ -3146,24 +2914,27 @@ int NBN_GameServer_BroadcastMessage(void) { if (!conn->is_accepted || conn->is_closed) continue; - if (GameServer_SendMessageTo(conn, message) < 0) { + if (GameServer_SendMessageTo(conn, &endpoint->write_message) < 0) { NBN_LogError("Failed to send message to client %d when broadcasting", conn->id); - return -1; + ret = -1; + break; } } - return 0; + endpoint->can_create_message = true; + + return ret; } NBN_Writer *NBN_GameServer_GetMessageWriter(void) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - NBN_Assert(endpoint->write_message != NULL); + NBN_Assert(!endpoint->can_create_message); NBN_Assert(endpoint->write_message_buffer_len > 0); NBN_Writer *writer = &endpoint->message_writer; - NBN_Writer_Init(writer, endpoint->write_message->data, endpoint->write_message_buffer_len); + NBN_Writer_Init(writer, endpoint->write_message.data, endpoint->write_message_buffer_len); return writer; } @@ -3262,16 +3033,6 @@ NBN_MessageInfo NBN_GameServer_GetMessageInfo(void) { NBN_GameServerStats NBN_GameServer_GetStats(void) { return nbn_game_server.stats; } -int NBN_GameServer_GetActiveOutgoingMessageCount(void) { return nbn_game_server.endpoint.active_out_msg_count; } - -int NBN_GameServer_GetActiveOutgoingMessageBufferCount(void) { - return nbn_game_server.endpoint.active_out_msg_buffer_count; -} - -int NBN_GameServer_GetActiveIncomingMessageBufferCount(void) { - return nbn_game_server.endpoint.active_inc_msg_buffer_count; -} - #ifdef NBN_DEBUG void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, void *cb) { @@ -3285,14 +3046,14 @@ void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, #endif /* NBN_DEBUG */ static int GameServer_SendMessageTo(NBN_Connection *client, NBN_Message *message) { - NBN_Assert(message != NULL); + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; /* Only NBN_CLIENT_ACCEPTED_MESSAGE_TYPE and NBN_CLIENT_CLOSED_MESSAGE_TYPE messages can be sent to an * unaccapted client */ NBN_Assert(client->is_accepted || message->header.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); - if (Endpoint_EnqueueOutgoingMessage(&nbn_game_server.endpoint, client, message) < 0) { + if (Endpoint_EnqueueOutgoingMessage(endpoint, client, message) < 0) { NBN_LogError("Failed to create outgoing message for client %d", client->id); /* Do not close the client if we failed to send the close client message to avoid infinite loops */ @@ -3391,8 +3152,13 @@ static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio ev.type = NBN_CLIENT_MESSAGE_RECEIVED; - NBN_MessageInfo msg_info = {message->header.type, message->header.channel_id, message->data, message->header.length, - client}; + NBN_MessageInfo msg_info; + + msg_info.type = message->header.type; + msg_info.channel_id = message->header.channel_id; + msg_info.length = message->header.length; + msg_info.sender = client; + msg_info.data = message->data; NBN_LogDebug("Received message (type: %d, id: %d) from client %d", message->header.type, message->header.id, client->id); @@ -3486,13 +3252,6 @@ static int GameServer_HandleMessageReceivedEvent(void) { NBN_MessageInfo message_info = last_event->data.message_info; NBN_Connection *sender = message_info.sender; NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - NBN_Message *last_received_message = &endpoint->last_received_message; - - last_received_message->data = message_info.data; - last_received_message->type = NBN_INCOMING_MESSAGE; - last_received_message->header.length = message_info.length; - - endpoint->last_message_need_free = true; if (sender->is_closed || sender->is_stale) return NBN_SKIP_EVENT; @@ -3514,12 +3273,6 @@ static int GameServer_HandleMessageReceivedEvent(void) { } if (message_info.type != NBN_CONNECTION_REQUEST_MESSAGE_TYPE) { - NBN_Message *last_received_message = &endpoint->last_received_message; - - last_received_message->data = message_info.data; - last_received_message->type = NBN_INCOMING_MESSAGE; - last_received_message->header.length = message_info.length; - return NBN_CLIENT_MESSAGE_RECEIVED; } diff --git a/soak/client.c b/soak/client.c index 7b7ae90..924174a 100644 --- a/soak/client.c +++ b/soak/client.c @@ -138,7 +138,7 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { } static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) { - SoakChannel *channel = &channels[channel_id - 2]; + SoakChannel *channel = &channels[channel_id]; unsigned int msg_id; unsigned int data_length; static uint8_t recv_buffer[SOAK_MESSAGE_BIG_MAX_DATA_LENGTH]; @@ -182,7 +182,6 @@ static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) channel->last_recved_message_id = msg_id; SoakOptions options = Soak_GetOptions(); - unsigned int channel_count = options.channel_count; Soak_LogInfo("Received soak message (length: %d, %d/%d) on channel %d", data_length, msg_id, channel->message_count, channel_id); @@ -192,7 +191,7 @@ static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) done_channel_count++; } - if (done_channel_count >= channel_count) { + if (done_channel_count >= NBN_CHANNEL_COUNT) { Soak_LogInfo("Received all soak message echoes on all channels"); Soak_Stop(); @@ -248,9 +247,7 @@ static int Tick(void *data) { } if (connected) { - unsigned int channel_count = Soak_GetOptions().channel_count; - - for (unsigned int c = 0; c < channel_count; c++) { + for (unsigned int c = 0; c < NBN_CHANNEL_COUNT; c++) { SoakChannel *channel = &channels[c]; if (SendSoakMessages(channel, channel->id) < 0) { @@ -308,7 +305,6 @@ int main(int argc, char *argv[]) { #endif // __EMSCRIPTEN__ NBN_GameClient_Init(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT); - NBN_GameClient_EnableCustomChannels(options.channel_count); if (NBN_GameClient_Start() < 0) { Soak_LogError("Failed to start game client. Exit"); @@ -325,16 +321,15 @@ int main(int argc, char *argv[]) { return 1; } - unsigned int channel_count = options.channel_count; unsigned int message_count = options.message_count; - unsigned int message_per_channel = message_count / channel_count; - unsigned int leftover_message_count = message_count % channel_count; - SoakChannel *channels = (SoakChannel *)malloc(sizeof(SoakChannel) * channel_count); + unsigned int message_per_channel = message_count / NBN_CHANNEL_COUNT; + unsigned int leftover_message_count = message_count % NBN_CHANNEL_COUNT; + SoakChannel *channels = (SoakChannel *)malloc(sizeof(SoakChannel) * NBN_CHANNEL_COUNT); - for (int c = 0; c < channel_count; c++) { + for (int c = 0; c < NBN_CHANNEL_COUNT; c++) { SoakChannel *channel = &channels[c]; - channel->id = 2 + c; // channels 0 and 1 are reserved by the library + channel->id = c; // channels 0 and 1 are reserved by the library channel->next_msg_id = 1; channel->sent_message_count = 0; channel->last_recved_message_id = 0; @@ -346,42 +341,16 @@ int main(int argc, char *argv[]) { } } - channels[channel_count - 1].message_count += leftover_message_count; + channels[NBN_CHANNEL_COUNT - 1].message_count += leftover_message_count; NBN_GameClient_Debug_RegisterCallback(NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE, (void *)Soak_Debug_PrintAddedToRecvQueue); int ret = Soak_MainLoop(Tick, channels); - int active_out_msg = NBN_GameClient_GetActiveOutgoingMessageCount(); - bool leak = false; - - if (active_out_msg > 0) { - Soak_LogError("Outgoing message leak detected: %d", active_out_msg); - leak = true; - } - - int active_inc_buffer_count = NBN_GameClient_GetActiveIncomingMessageBufferCount(); - - if (active_inc_buffer_count > 0) { - Soak_LogError("Incoming message buffer leak detected: %d", active_inc_buffer_count); - leak = true; - } - - int active_out_buffer_count = NBN_GameClient_GetActiveOutgoingMessageCount(); - - if (active_out_buffer_count > 0) { - Soak_LogError("Outgoing message buffer leak detected: %d", active_out_buffer_count); - leak = true; - } - NBN_GameClient_Stop(); free(channels); - if (!leak) { - Soak_LogInfo("No memory leak detected! Cool... cool cool cool"); - } - #ifdef WEBRTC_NATIVE if (options.webrtc) { diff --git a/soak/server.c b/soak/server.c index b0fef0a..46e74ff 100644 --- a/soak/server.c +++ b/soak/server.c @@ -87,17 +87,16 @@ static void HandleNewConnection(void) { NBN_GameServer_AcceptIncomingConnection(); SoakClient *soak_client = (SoakClient *)malloc(sizeof(SoakClient)); - unsigned int channel_count = Soak_GetOptions().channel_count; soak_client->conn = conn; soak_client->error = false; soak_client->is_closed = false; - soak_client->channels = (SoakChannel *)malloc(sizeof(SoakChannel) * channel_count); + soak_client->channels = (SoakChannel *)malloc(sizeof(SoakChannel) * NBN_CHANNEL_COUNT); - for (unsigned int c = 0; c < channel_count; c++) { + for (unsigned int c = 0; c < NBN_CHANNEL_COUNT; c++) { SoakChannel *channel = &soak_client->channels[c]; - channel->id = 2 + c; + channel->id = c; channel->recved_messages_count = 0; channel->last_recved_message_id = 0; @@ -130,15 +129,13 @@ static void HandleClientDisconnection(NBN_DisconnectionInfo info) { } static void EchoReceivedSoakMessages(void) { - unsigned int channel_count = Soak_GetOptions().channel_count; - for (unsigned int i = 0; i < SOAK_MAX_CLIENTS; i++) { SoakClient *soak_client = clients[i]; if (soak_client == NULL || soak_client->is_closed) continue; - for (unsigned int c = 0; c < channel_count; c++) { + for (unsigned int c = 0; c < NBN_CHANNEL_COUNT; c++) { SoakChannel *channel = &soak_client->channels[c]; while (channel->echo_queue.count > 0) { @@ -179,7 +176,7 @@ static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_Connection *sender, if (!soak_client || soak_client->error) return 0; - SoakChannel *channel = &soak_client->channels[channel_id - 2]; + SoakChannel *channel = &soak_client->channels[channel_id]; unsigned int msg_id; unsigned int data_length; @@ -292,35 +289,7 @@ static int Tick(void *data) { return 0; } -static void SigintHandler(int dummy) { - int active_out_msg = NBN_GameServer_GetActiveOutgoingMessageCount(); - bool leak = false; - - if (active_out_msg > 0) { - Soak_LogError("Outgoing message leak detected: %d", active_out_msg); - leak = true; - } - - int active_inc_buffer_count = NBN_GameServer_GetActiveIncomingMessageBufferCount(); - - if (active_inc_buffer_count > 0) { - Soak_LogError("Incoming message buffer leak detected: %d", active_inc_buffer_count); - leak = true; - } - - int active_out_buffer_count = NBN_GameServer_GetActiveOutgoingMessageCount(); - - if (active_out_buffer_count > 0) { - Soak_LogError("Outgoing message buffer leak detected: %d", active_out_buffer_count); - leak = true; - } - - if (!leak) { - Soak_LogInfo("No memory leak detected! Cool... cool cool cool"); - } - - Soak_Stop(); -} +static void SigintHandler(int dummy) { Soak_Stop(); } int main(int argc, char *argv[]) { signal(SIGINT, SigintHandler); @@ -353,7 +322,6 @@ int main(int argc, char *argv[]) { SoakOptions options = Soak_GetOptions(); NBN_GameServer_Init(SOAK_PROTOCOL_NAME, SOAK_PORT); - NBN_GameServer_EnableCustomChannels(options.channel_count); if (NBN_GameServer_Start()) { Soak_LogError("Failed to start game server"); diff --git a/soak/soak.c b/soak/soak.c index 7fd2ae0..17265ab 100644 --- a/soak/soak.c +++ b/soak/soak.c @@ -48,17 +48,17 @@ static void Usage(void) { #ifdef SOAK_CLIENT #ifdef WEBRTC_NATIVE - printf("Usage: client --message_count= --channel_count= [--packet_loss=] \ + printf("Usage: client --message_count= [--packet_loss=] \ [--packet_duplication=] [--ping=] [--jitter=] [--webrtc]\n"); #else - printf("Usage: client --message_count= --channel_count= [--packet_loss=] \ + printf("Usage: client --message_count= [--packet_loss=] \ [--packet_duplication=] [--ping=] [--jitter=]\n"); #endif // WEBRTC_NATIVE #endif // SOAK_CLIENT #ifdef SOAK_SERVER - printf("Usage: server --channel_count= [--packet_loss=] \ + printf("Usage: server [--packet_loss=] \ [--packet_duplication=] [--ping=] [--jitter=]\n"); #endif } @@ -109,7 +109,6 @@ int Soak_ReadCommandLine(int argc, char *argv[]) { #endif // SOAK_CLIENT - {'c', NULL, "channel_count", "VALUE", "Number of channels (1 - 8)"}, {'l', NULL, "packet_loss", "VALUE", "Packet loss frenquency (0-1)"}, {'d', NULL, "packet_duplication", "VALUE", "Packet duplication frequency (0-1)"}, {'p', NULL, "ping", "VALUE", "Ping in seconds"}, @@ -136,13 +135,7 @@ int Soak_ReadCommandLine(int argc, char *argv[]) { if (false) { } #endif - else if (option == 'c') { - const char *val = cag_option_get_value(&context); - - if (val) { - soak_options.channel_count = atoi(val); - } - } else if (option == 'l') { + else if (option == 'l') { soak_options.packet_loss = atof(cag_option_get_value(&context)); } else if (option == 'd') { soak_options.packet_duplication = atof(cag_option_get_value(&context)); @@ -153,16 +146,6 @@ int Soak_ReadCommandLine(int argc, char *argv[]) { } } - if (soak_options.channel_count <= 0) { - Usage(); - return -1; - } - - if (soak_options.channel_count > NBN_MAX_CUSTOM_CHANNELS) { - Soak_LogError("Channel count cannot exceed %d", NBN_MAX_CUSTOM_CHANNELS); - return -1; - } - #ifdef SOAK_CLIENT if (soak_options.message_count <= 0) { Usage(); diff --git a/soak/soak.h b/soak/soak.h index 971ee63..ae9b8ca 100644 --- a/soak/soak.h +++ b/soak/soak.h @@ -44,6 +44,7 @@ #define NBN_LogError Soak_LogError #define NBN_LogWarning Soak_LogWarn +#define NBN_CHANNEL_COUNT 4 #include "../nbnet.h" #define SOAK_PROTOCOL_NAME "nbnet_soak" @@ -68,7 +69,6 @@ typedef struct { unsigned int message_count; - unsigned int channel_count; float packet_loss; /* 0 - 1 */ float packet_duplication; /* 0 - 1 */ float ping; /* in seconds */ @@ -89,7 +89,5 @@ unsigned int Soak_GetCreatedIncomingSoakMessageCount(void); unsigned int Soak_GetDestroyedIncomingSoakMessageCount(void); void SoakMessage_Write(NBN_Writer *, unsigned int, uint8_t *, unsigned int); int SoakMessage_Read(NBN_Reader *reader, unsigned int *msg_id, uint8_t *data, unsigned int *data_length); -bool AllocateMessage(NBN_MessageHeader header, NBN_MessageType type, uint8_t **buffer); -void DeallocateMessage(NBN_MessageHeader header, NBN_MessageType type, uint8_t *buffer); #endif // SOAK_H_INCLUDED From 07a7753340320e61d85dbc78ecd9efdc11ee3bef Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sun, 25 Jan 2026 16:15:18 +0100 Subject: [PATCH 11/85] move code around --- nbnet.h | 70 ++++++++++++++++++++++++--------------------------------- 1 file changed, 29 insertions(+), 41 deletions(-) diff --git a/nbnet.h b/nbnet.h index 533e126..1a6fc9d 100644 --- a/nbnet.h +++ b/nbnet.h @@ -408,8 +408,6 @@ struct NBN_ConnectionListNode { typedef uint8_t *(*NBN_AllocMessageFunc)(NBN_MessageHeader *); typedef uint8_t *(*NBN_DeallocMessageFunc)(NBN_MessageHeader *, uint8_t *); -NBN_Connection *NBN_Connection_Create(uint32_t, NBN_Endpoint *, NBN_Driver *, void *); -void NBN_Connection_Destroy(NBN_Endpoint *, NBN_Connection *); int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); int NBN_Connection_FlushChannels(NBN_Endpoint *, NBN_Connection *, uint32_t, double); bool NBN_Connection_CheckIfStale(NBN_Connection *, double); @@ -798,7 +796,6 @@ typedef struct NBN_GameServer { extern NBN_GameServer nbn_game_server; -// TODO: add doc about msg allocators /** * Initialize the game server with minimal configuration. * @@ -1624,40 +1621,6 @@ static void Connection_UpdateAveragePacketLoss(NBN_Connection *, uint16_t); static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *, float); static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *, double); -// TODO: move this code into Endpoint_CreateConnection -NBN_Connection *NBN_Connection_Create(uint32_t id, NBN_Endpoint *endpoint, NBN_Driver *driver, void *driver_data) { - NBN_Connection *connection = (NBN_Connection *)malloc(sizeof(NBN_Connection)); - - connection->id = id; - connection->endpoint = endpoint; - connection->last_recv_packet_time = endpoint->time; - connection->next_packet_seq_number = 1; - connection->last_received_packet_seq_number = 0; - connection->last_flush_time = endpoint->time; - connection->last_read_packets_time = endpoint->time; - connection->downloaded_bytes = 0; - connection->is_accepted = false; - connection->is_stale = false; - connection->is_closed = false; - connection->vector_pos = -1; - connection->user_data = NULL; - - for (int i = 0; i < NBN_MAX_PACKET_ENTRIES; i++) { - connection->packet_send_seq_buffer[i] = 0xFFFFFFFF; - connection->packet_recv_seq_buffer[i] = 0xFFFFFFFF; - } - - NBN_ConnectionStats stats = {0}; - - connection->stats = stats; - connection->driver = driver; - connection->driver_data = driver_data; - - return connection; -} - -void NBN_Connection_Destroy(NBN_Endpoint *endpoint, NBN_Connection *connection) { free(connection); } - int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, double time) { if (Connection_DecodePacketHeader(endpoint, connection, packet, time) < 0) { @@ -2154,7 +2117,32 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_ NBN_Assert(driver->id >= 0); - NBN_Connection *connection = NBN_Connection_Create(id, endpoint, driver, driver_data); + NBN_Connection *connection = (NBN_Connection *)malloc(sizeof(NBN_Connection)); + + connection->id = id; + connection->endpoint = endpoint; + connection->last_recv_packet_time = endpoint->time; + connection->next_packet_seq_number = 1; + connection->last_received_packet_seq_number = 0; + connection->last_flush_time = endpoint->time; + connection->last_read_packets_time = endpoint->time; + connection->downloaded_bytes = 0; + connection->is_accepted = false; + connection->is_stale = false; + connection->is_closed = false; + connection->vector_pos = -1; + connection->user_data = NULL; + + for (int i = 0; i < NBN_MAX_PACKET_ENTRIES; i++) { + connection->packet_send_seq_buffer[i] = 0xFFFFFFFF; + connection->packet_recv_seq_buffer[i] = 0xFFFFFFFF; + } + + NBN_ConnectionStats stats = {0}; + + connection->stats = stats; + connection->driver = driver; + connection->driver_data = driver_data; for (int i = 0; i < NBN_CHANNEL_COUNT; i++) { Channel_Init(&connection->channels[i], i, NBN_CHANNEL_RELIABLE); @@ -2376,7 +2364,7 @@ void NBN_GameClient_Stop(void) { NBN_LogInfo("Disconnected"); } - NBN_Connection_Destroy(&nbn_game_client.endpoint, nbn_game_client.server_connection); + free(nbn_game_client.server_connection); nbn_game_client.server_connection = NULL; } @@ -2738,7 +2726,7 @@ void NBN_GameServer_Stop(void) { } for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { - NBN_Connection_Destroy(&nbn_game_server.endpoint, nbn_game_server.clients->connections[i]); + free(nbn_game_server.clients->connections[i]); } NBN_ConnectionVector_Destroy(nbn_game_server.clients); @@ -3213,7 +3201,7 @@ static void GameServer_RemoveClosedClientConnections(void) { // Destroy the connection - NBN_Connection_Destroy(&nbn_game_server.endpoint, client); + free(client); // Remove the connection from the closed clients list From 9424054967dc4fb0494c4d62c1a6254d95765778 Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Tue, 27 Jan 2026 17:47:36 +0100 Subject: [PATCH 12/85] update nbnet api --- nbnet.h | 149 +++++++++++++++++++------------------------------------- 1 file changed, 50 insertions(+), 99 deletions(-) diff --git a/nbnet.h b/nbnet.h index 1a6fc9d..609367c 100644 --- a/nbnet.h +++ b/nbnet.h @@ -573,8 +573,6 @@ struct NBN_Endpoint { bool is_server; double time; NBN_Message write_message; - bool can_create_message; - unsigned int write_message_buffer_len; NBN_Writer message_writer; NBN_Reader message_reader; NBN_MessageAllocator msg_allocator; @@ -672,17 +670,17 @@ int NBN_GameClient_Poll(void); * * @return 0 when successful, -1 otherwise */ -int NBN_GameClient_SendPackets(void); +int NBN_GameClient_Flush(void); // TODO: doc -void NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id); +NBN_Writer *NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id); // TODO: doc -void NBN_GameClient_CreateUnreliableMessage(uint8_t type); +NBN_Writer *NBN_GameClient_CreateUnreliableMessage(uint8_t type); // TODO: doc -void NBN_GameClient_CreateReliableMessage(uint8_t type); +NBN_Writer *NBN_GameClient_CreateReliableMessage(uint8_t type); // TODO: doc -int NBN_GameClient_SendMessage(void); +int NBN_GameClient_EnqueueMessage(void); // TODO: doc NBN_Writer *NBN_GameClient_GetMessageWriter(void); @@ -690,28 +688,6 @@ NBN_Writer *NBN_GameClient_GetMessageWriter(void); // TODO: doc NBN_Reader *NBN_GameClient_GetMessageReader(void); -/** - * Send a message to the server, unreliably. - * - * @param type The type of message to send - * @param data A pointer to the message data buffer (managed by user code) - * @param length The length of the message data buffer in bytes - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameClient_SendUnreliableMessage(uint8_t type, uint8_t *data, uint16_t length); - -/** - * Send a message to the server, reliably. - * - * @param type The type of message to send - * @param data A pointer to the message data buffer (managed by user code) - * @param length The length of the message data buffer in bytes - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameClient_SendReliableMessage(uint8_t type, uint8_t *data, uint16_t length); - /** * For drivers only! NOT MEANT TO BE USED BY USER CODE. */ @@ -834,7 +810,7 @@ int NBN_GameServer_Poll(void); * * @return 0 when successful, -1 otherwise */ -int NBN_GameServer_SendPackets(void); +int NBN_GameServer_Flush(void); /** * For drivers only! NOT MEANT TO BE USED BY USER CODE. @@ -863,18 +839,16 @@ int NBN_GameServer_CloseClient(NBN_Connection *conn); int NBN_GameServer_CloseClientWithCode(NBN_Connection *conn, int code); // TODO: doc -void NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id); +NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id); // TODO: doc -void NBN_GameServer_CreateUnreliableMessage(uint8_t type); +NBN_Writer *NBN_GameServer_CreateUnreliableMessage(uint8_t type); // TODO: doc -void NBN_GameServer_CreateReliableMessage(uint8_t type); +NBN_Writer *NBN_GameServer_CreateReliableMessage(uint8_t type); // TODO: doc -int NBN_GameServer_SendMessageTo(NBN_Connection *conn); +int NBN_GameServer_EnqueueMessageFor(NBN_Connection *conn); // TODO: doc -int NBN_GameServer_BroadcastMessage(void); +int NBN_GameServer_EnqueueBroadcastMessage(void); -// TODO: doc -NBN_Writer *NBN_GameServer_GetMessageWriter(void); // TODO: doc NBN_Reader *NBN_GameServer_GetMessageReader(void); @@ -2084,8 +2058,6 @@ static void Endpoint_UpdateTime(NBN_Endpoint *); static void Endpoint_Init(NBN_Endpoint *endpoint, NBN_Endpoint_Config config, uint32_t protocol_id, bool is_server) { endpoint->is_server = is_server; endpoint->protocol_id = protocol_id; - endpoint->write_message_buffer_len = sizeof(endpoint->write_message.data); - endpoint->can_create_message = true; endpoint->msg_allocator = config.msg_allocator; endpoint->msg_deallocator = config.msg_deallocator; @@ -2185,7 +2157,6 @@ static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, uint8_t type, message->type = NBN_OUTGOING_MESSAGE; endpoint->message_writer.position = 0; - endpoint->can_create_message = false; } static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Message *message) { @@ -2332,7 +2303,7 @@ int NBN_GameClient_Start(void) { NBN_Writer_WriteUInt32(writer, 0); } - if (NBN_GameClient_SendMessage() < 0) + if (NBN_GameClient_EnqueueMessage() < 0) return NBN_ERROR; NBN_LogInfo("Started"); @@ -2351,11 +2322,11 @@ void NBN_GameClient_Stop(void) { NBN_GameClient_CreateReliableMessage(NBN_DISCONNECTION_MESSAGE_TYPE); - if (NBN_GameClient_SendMessage() < 0) { + if (NBN_GameClient_EnqueueMessage() < 0) { NBN_LogError("Failed to send disconnection message"); } - if (NBN_GameClient_SendPackets() < 0) { + if (NBN_GameClient_Flush() < 0) { NBN_LogError("Failed to send packets"); } @@ -2460,27 +2431,30 @@ int NBN_GameClient_Poll(void) { return ret ? GameClient_HandleEvent() : NBN_NO_EVENT; } -int NBN_GameClient_SendPackets(void) { +int NBN_GameClient_Flush(void) { return NBN_Connection_FlushChannels(&nbn_game_client.endpoint, nbn_game_client.server_connection, nbn_game_client.endpoint.protocol_id, nbn_game_client.endpoint.time); } -void NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id) { +NBN_Writer *NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - NBN_Assert(endpoint->can_create_message == true); + NBN_Writer *writer = &endpoint->message_writer; + NBN_Writer_Init(writer, endpoint->write_message.data, sizeof(endpoint->write_message.data)); Endpoint_CreateOutgoingMessage(endpoint, type, channel_id); + + return writer; } -void NBN_GameClient_CreateUnreliableMessage(uint8_t type) { - NBN_GameClient_CreateMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE); +NBN_Writer *NBN_GameClient_CreateUnreliableMessage(uint8_t type) { + return NBN_GameClient_CreateMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE); } -void NBN_GameClient_CreateReliableMessage(uint8_t type) { - NBN_GameClient_CreateMessage(type, NBN_CHANNEL_RESERVED_RELIABLE); +NBN_Writer *NBN_GameClient_CreateReliableMessage(uint8_t type) { + return NBN_GameClient_CreateMessage(type, NBN_CHANNEL_RESERVED_RELIABLE); } -int NBN_GameClient_SendMessage(void) { +int NBN_GameClient_EnqueueMessage(void) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; NBN_Message *message = &endpoint->write_message; @@ -2492,20 +2466,14 @@ int NBN_GameClient_SendMessage(void) { return NBN_ERROR; } - endpoint->can_create_message = true; - return 0; } NBN_Writer *NBN_GameClient_GetMessageWriter(void) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - - NBN_Assert(!endpoint->can_create_message); - NBN_Assert(endpoint->write_message_buffer_len > 0); - NBN_Writer *writer = &endpoint->message_writer; - NBN_Writer_Init(writer, endpoint->write_message.data, endpoint->write_message_buffer_len); + NBN_Writer_Init(writer, endpoint->write_message.data, sizeof(endpoint->write_message.data)); return writer; } @@ -2667,7 +2635,7 @@ static void ClientDriver_OnPacketReceived(NBN_Packet *packet) { NBN_GameServer nbn_game_server; -static int GameServer_SendMessageTo(NBN_Connection *client, NBN_Message *message); +static int GameServer_EnqueueMessageFor(NBN_Connection *client, NBN_Message *message); static int GameServer_AddClient(NBN_Connection *); static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection); static void GameServer_AddClientToClosedList(NBN_Connection *client); @@ -2726,7 +2694,10 @@ void NBN_GameServer_Stop(void) { } for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { - free(nbn_game_server.clients->connections[i]); + NBN_Connection *conn = nbn_game_server.clients->connections[i]; + + conn->driver->impl.serv_remove_connection(conn); + free(conn); } NBN_ConnectionVector_Destroy(nbn_game_server.clients); @@ -2819,7 +2790,7 @@ int NBN_GameServer_Poll(void) { return NBN_NO_EVENT; } -int NBN_GameServer_SendPackets(void) { +int NBN_GameServer_Flush(void) { nbn_game_server.stats.upload_bandwidth = 0; GameServer_RemoveClosedClientConnections(); @@ -2859,38 +2830,36 @@ int NBN_GameServer_CloseClientWithCode(NBN_Connection *conn, int code) { int NBN_GameServer_CloseClient(NBN_Connection *conn) { return GameServer_CloseClientWithCode(conn, -1, false); } -void NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id) { +NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + NBN_Writer *writer = &endpoint->message_writer; + NBN_Writer_Init(writer, endpoint->write_message.data, sizeof(endpoint->write_message.data)); Endpoint_CreateOutgoingMessage(endpoint, type, channel_id); + + return writer; } -void NBN_GameServer_CreateUnreliableMessage(uint8_t type) { - NBN_GameServer_CreateMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE); +NBN_Writer *NBN_GameServer_CreateUnreliableMessage(uint8_t type) { + return NBN_GameServer_CreateMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE); } -void NBN_GameServer_CreateReliableMessage(uint8_t type) { - NBN_GameServer_CreateMessage(type, NBN_CHANNEL_RESERVED_RELIABLE); +NBN_Writer *NBN_GameServer_CreateReliableMessage(uint8_t type) { + return NBN_GameServer_CreateMessage(type, NBN_CHANNEL_RESERVED_RELIABLE); } -int NBN_GameServer_SendMessageTo(NBN_Connection *conn) { +int NBN_GameServer_EnqueueMessageFor(NBN_Connection *conn) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - - NBN_Assert(!endpoint->can_create_message); - NBN_Message *message = &endpoint->write_message; message->header.length = endpoint->message_writer.position; - int ret = GameServer_SendMessageTo(conn, message); + int ret = GameServer_EnqueueMessageFor(conn, message); - endpoint->can_create_message = true; return ret; } -int NBN_GameServer_BroadcastMessage(void) { +int NBN_GameServer_EnqueueBroadcastMessage(void) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - NBN_Assert(!endpoint->can_create_message); - NBN_Message *message = &endpoint->write_message; message->header.length = endpoint->message_writer.position; @@ -2902,31 +2871,16 @@ int NBN_GameServer_BroadcastMessage(void) { if (!conn->is_accepted || conn->is_closed) continue; - if (GameServer_SendMessageTo(conn, &endpoint->write_message) < 0) { + if (GameServer_EnqueueMessageFor(conn, &endpoint->write_message) < 0) { NBN_LogError("Failed to send message to client %d when broadcasting", conn->id); ret = -1; break; } } - endpoint->can_create_message = true; - return ret; } -NBN_Writer *NBN_GameServer_GetMessageWriter(void) { - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - - NBN_Assert(!endpoint->can_create_message); - NBN_Assert(endpoint->write_message_buffer_len > 0); - - NBN_Writer *writer = &endpoint->message_writer; - - NBN_Writer_Init(writer, endpoint->write_message.data, endpoint->write_message_buffer_len); - - return writer; -} - NBN_Reader *NBN_GameServer_GetMessageReader(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); @@ -2957,8 +2911,7 @@ int NBN_GameServer_AcceptIncomingConnection(void) { unsigned data_length = nbn_game_server.server_data_writer.position; NBN_Connection *client = nbn_game_server.last_event.data.connection; - NBN_GameServer_CreateReliableMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); - NBN_Writer *writer = NBN_GameServer_GetMessageWriter(); + NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); if (data_length > 0) { NBN_Assert(data_length <= NBN_SERVER_DATA_MAX_SIZE); @@ -2969,7 +2922,7 @@ int NBN_GameServer_AcceptIncomingConnection(void) { NBN_Writer_WriteUInt32(writer, 0); } - if (NBN_GameServer_SendMessageTo(client) < 0) + if (NBN_GameServer_EnqueueMessageFor(client) < 0) return NBN_ERROR; client->is_accepted = true; @@ -3033,7 +2986,7 @@ void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, #endif /* NBN_DEBUG */ -static int GameServer_SendMessageTo(NBN_Connection *client, NBN_Message *message) { +static int GameServer_EnqueueMessageFor(NBN_Connection *client, NBN_Message *message) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; /* Only NBN_CLIENT_ACCEPTED_MESSAGE_TYPE and NBN_CLIENT_CLOSED_MESSAGE_TYPE messages can be sent to an @@ -3098,11 +3051,9 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool if (!disconnection) { NBN_LogDebug("Send close message for client %d (code: %d)", client->id, code); - NBN_GameServer_CreateReliableMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE); - NBN_Writer *writer = NBN_GameServer_GetMessageWriter(); - + NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE); NBN_Writer_WriteInt32(writer, code); - NBN_GameServer_SendMessageTo(client); + NBN_GameServer_EnqueueMessageFor(client); } return 0; From 164d8d8bd011591f5b35f578148d17068addd934 Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Tue, 27 Jan 2026 17:47:45 +0100 Subject: [PATCH 13/85] update examples --- examples/echo/client.c | 7 +- examples/echo/server.c | 7 +- examples/raylib/CMakeLists.txt | 9 +- examples/raylib/client.c | 231 ++++++++++++--------------------- examples/raylib/server.c | 196 ++++++++++------------------ 5 files changed, 158 insertions(+), 292 deletions(-) diff --git a/examples/echo/client.c b/examples/echo/client.c index e69d404..8ad4f33 100644 --- a/examples/echo/client.c +++ b/examples/echo/client.c @@ -74,14 +74,13 @@ void OnMessageReceived(void) { } int SendMessage(const char *msg) { - NBN_GameClient_CreateReliableMessage(ECHO_MESSAGE_TYPE); - NBN_Writer *writer = NBN_GameClient_GetMessageWriter(); + NBN_Writer *writer = NBN_GameClient_CreateReliableMessage(ECHO_MESSAGE_TYPE); unsigned int length = strlen(msg); NBN_Writer_WriteUInt32(writer, length); NBN_Writer_WriteBytes(writer, (uint8_t *)msg, length); - return NBN_GameClient_SendMessage(); + return NBN_GameClient_EnqueueMessage(); } int main(int argc, char *argv[]) { @@ -213,7 +212,7 @@ int main(int argc, char *argv[]) { } // Pack all enqueued messages as packets and send them - if (NBN_GameClient_SendPackets() < 0) { + if (NBN_GameClient_Flush() < 0) { Log(LOG_ERROR, "Failed to send packets. Exit"); // Stop main loop diff --git a/examples/echo/server.c b/examples/echo/server.c index da8d284..8012eb2 100644 --- a/examples/echo/server.c +++ b/examples/echo/server.c @@ -57,13 +57,12 @@ static int EchoReceivedMessage(void) { msg_info.channel_id); // create and send an echo of the received message - NBN_GameServer_CreateReliableMessage(ECHO_MESSAGE_TYPE); - NBN_Writer *writer = NBN_GameServer_GetMessageWriter(); + NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(ECHO_MESSAGE_TYPE); NBN_Writer_WriteUInt32(writer, length); NBN_Writer_WriteBytes(writer, (uint8_t *)msg_str, length); - return NBN_GameServer_SendMessageTo(connection); + return NBN_GameServer_EnqueueMessageFor(connection); } static bool error = false; @@ -186,7 +185,7 @@ int main(int argc, const char **argv) { } // Pack all enqueued messages as packets and send them - if (NBN_GameServer_SendPackets() < 0) { + if (NBN_GameServer_Flush() < 0) { Log(LOG_ERROR, "Failed to send packets"); // Error, quit the server application diff --git a/examples/raylib/CMakeLists.txt b/examples/raylib/CMakeLists.txt index f198242..d131037 100644 --- a/examples/raylib/CMakeLists.txt +++ b/examples/raylib/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(raylib_example C) @@ -7,7 +7,7 @@ set(raylib_DIR cmake) set(CLIENT_SOURCES client.c shared.c) set(SERVER_SOURCES server.c shared.c) -add_compile_options(-Wall -Wextra -Wpedantic -Wno-unknown-pragmas -Wno-type-limits -std=c99) +add_compile_options(-Wall -Wextra -Wpedantic -Wno-unknown-pragmas -std=c99) add_executable(raylib_client ${CLIENT_SOURCES}) add_executable(raylib_server ${SERVER_SOURCES}) @@ -42,13 +42,8 @@ if (WEBRTC_C_DRIVER) target_compile_definitions(raylib_server PUBLIC SOAK_WEBRTC_C_DRIVER) - target_link_libraries(raylib_server ${LIBFACILIO_LIBRARY_PATH}) - target_link_libraries(raylib_server ${LIBCRYPTO_LIBRARY_PATH}) - target_link_libraries(raylib_server ${LIBSSL_LIBRARY_PATH}) target_link_libraries(raylib_server ${LIBDATACHANNEL_LIBRARY_PATH}) - target_include_directories(raylib_server PUBLIC "${LIBFACILIO_INCLUDE_PATH}") - target_include_directories(raylib_server PUBLIC "${OPENSSL_INCLUDE_PATH}") target_include_directories(raylib_server PUBLIC "${LIBDATACHANNEL_INCLUDE_PATH}") if (USE_HTTPS) diff --git a/examples/raylib/client.c b/examples/raylib/client.c index b20c857..4d66f25 100644 --- a/examples/raylib/client.c +++ b/examples/raylib/client.c @@ -19,6 +19,7 @@ */ +#include #include #ifdef __EMSCRIPTEN__ @@ -58,8 +59,7 @@ Color client_colors_to_raylib_colors[] = { PINK // CLI_PINK }; -static void SpawnLocalClient(int x, int y, uint32_t client_id) -{ +static void SpawnLocalClient(int x, int y, uint32_t client_id) { TraceLog(LOG_INFO, "Spawning at (%d, %d), client id: %d", x, y, client_id); // Update the local client state based on spawn info sent by the server @@ -70,25 +70,21 @@ static void SpawnLocalClient(int x, int y, uint32_t client_id) spawned = true; } -static int HandleConnection(void) -{ +static int HandleConnection(void) { TraceLog(LOG_INFO, "Connected, reading connection data..."); uint32_t x, y, client_id; NBN_Reader *reader = NBN_GameClient_GetServerDataReader(); - if (NBN_Reader_ReadUInt32(reader, &x) < 0) - { + if (NBN_Reader_ReadUInt32(reader, &x) < 0) { return -1; } - if (NBN_Reader_ReadUInt32(reader, &y) < 0) - { + if (NBN_Reader_ReadUInt32(reader, &y) < 0) { return -1; } - if (NBN_Reader_ReadUInt32(reader, &client_id) < 0) - { + if (NBN_Reader_ReadUInt32(reader, &client_id) < 0) { return -1; } @@ -98,8 +94,7 @@ static int HandleConnection(void) return 0; } -static void HandleDisconnection(void) -{ +static void HandleDisconnection(void) { int code = NBN_GameClient_GetServerCloseCode(); // Get the server code used when closing the client connection TraceLog(LOG_INFO, "Disconnected from server (code: %d)", code); @@ -108,10 +103,8 @@ static void HandleDisconnection(void) server_close_code = code; } -static bool ClientExists(uint32_t client_id) -{ - for (int i = 0; i < MAX_CLIENTS - 1; i++) - { +static bool ClientExists(uint32_t client_id) { + for (int i = 0; i < MAX_CLIENTS - 1; i++) { if (clients[i] && clients[i]->client_id == client_id) return true; } @@ -119,18 +112,15 @@ static bool ClientExists(uint32_t client_id) return false; } -static void CreateClient(ClientState state) -{ +static void CreateClient(ClientState state) { TraceLog(LOG_DEBUG, "CreateClient %d", state.client_id); assert(client_count < MAX_CLIENTS - 1); ClientState *client = NULL; // Create a new remote client state and store it in the remote clients array at the first free slot found - for (int i = 0; i < MAX_CLIENTS - 1; i++) - { - if (clients[i] == NULL) - { + for (int i = 0; i < MAX_CLIENTS - 1; i++) { + if (clients[i] == NULL) { client = malloc(sizeof(ClientState)); clients[i] = client; @@ -148,15 +138,12 @@ static void CreateClient(ClientState state) TraceLog(LOG_INFO, "New remote client (ID: %d)", client->client_id); } -static void UpdateClient(ClientState state) -{ +static void UpdateClient(ClientState state) { ClientState *client = NULL; // Find the client matching the client id of the received remote client state - for (int i = 0; i < MAX_CLIENTS - 1; i++) - { - if (clients[i] && clients[i]->client_id == state.client_id) - { + for (int i = 0; i < MAX_CLIENTS - 1; i++) { + if (clients[i] && clients[i]->client_id == state.client_id) { client = clients[i]; break; @@ -169,15 +156,12 @@ static void UpdateClient(ClientState state) memcpy(client, &state, sizeof(ClientState)); } -static void DestroyClient(uint32_t client_id) -{ +static void DestroyClient(uint32_t client_id) { // Find the client matching the client id and destroy it - for (int i = 0; i < MAX_CLIENTS - 1; i++) - { + for (int i = 0; i < MAX_CLIENTS - 1; i++) { ClientState *client = clients[i]; - if (client && client->client_id == client_id) - { + if (client && client->client_id == client_id) { TraceLog(LOG_INFO, "Destroy disconnected client (ID: %d)", client->client_id); free(client); @@ -189,24 +173,20 @@ static void DestroyClient(uint32_t client_id) } } -static void DestroyDisconnectedClients(void) -{ +static void DestroyDisconnectedClients(void) { /* Loop over all remote client states and remove the one that have not * been updated with the last received game state. * This is how we detect disconnected clients. */ - for (int i = 0; i < MAX_CLIENTS - 1; i++) - { + for (int i = 0; i < MAX_CLIENTS - 1; i++) { if (clients[i] == NULL) continue; uint32_t client_id = clients[i]->client_id; bool disconnected = true; - for (int j = 0; j < MAX_CLIENTS; j++) - { - if ((int)client_id == updated_ids[j]) - { + for (int j = 0; j < MAX_CLIENTS; j++) { + if ((int)client_id == updated_ids[j]) { disconnected = false; break; @@ -218,13 +198,12 @@ static void DestroyDisconnectedClients(void) } } -static void HandleGameStateMessage(void) -{ - if (!spawned) return; +static void HandleGameStateMessage(void) { + if (!spawned) + return; // Start by resetting the updated client ids array - for (int i = 0; i < MAX_CLIENTS; i++) - { + for (int i = 0; i < MAX_CLIENTS; i++) { updated_ids[i] = -1; } @@ -236,13 +215,11 @@ static void HandleGameStateMessage(void) GameStateMessage_Read(reader, &recv_game_state); // Loop over the received client states and update the clients - for (unsigned int i = 0; i < recv_game_state.client_count; i++) - { + for (unsigned int i = 0; i < recv_game_state.client_count; i++) { ClientState cli_state = recv_game_state.client_states[i]; // Ignore the state of the local client - if (cli_state.client_id != local_client_state.client_id) - { + if (cli_state.client_id != local_client_state.client_id) { // If the client already exists we update it with the latest received state if (ClientExists(cli_state.client_id)) UpdateClient(cli_state); @@ -257,13 +234,11 @@ static void HandleGameStateMessage(void) DestroyDisconnectedClients(); } -static void HandleReceivedMessage(void) -{ +static void HandleReceivedMessage(void) { // Fetch info about the last received message NBN_MessageInfo msg_info = NBN_GameClient_GetMessageInfo(); - switch (msg_info.type) - { + switch (msg_info.type) { // We received the latest game state from the server case GAME_STATE_MESSAGE: HandleGameStateMessage(); @@ -274,61 +249,52 @@ static void HandleReceivedMessage(void) abort(); } -static void HandleGameClientEvent(int ev) -{ - switch (ev) - { - case NBN_CONNECTED: - // We are connected to the server - if (HandleConnection() < 0) - { - TraceLog(LOG_ERROR, "Failed to handle connection"); - abort(); - } - break; +static void HandleGameClientEvent(int ev) { + switch (ev) { + case NBN_CONNECTED: + // We are connected to the server + if (HandleConnection() < 0) { + TraceLog(LOG_ERROR, "Failed to handle connection"); + abort(); + } + break; - case NBN_DISCONNECTED: - // The server has closed our connection - HandleDisconnection(); - break; + case NBN_DISCONNECTED: + // The server has closed our connection + HandleDisconnection(); + break; - case NBN_MESSAGE_RECEIVED: - // We received a message from the server - HandleReceivedMessage(); - break; + case NBN_MESSAGE_RECEIVED: + // We received a message from the server + HandleReceivedMessage(); + break; } } -static int SendStateUpdate(void) -{ +static int SendStateUpdate(void) { // Create a new UPDATE_STATE_MESSAGE unreliable message - NBN_GameClient_CreateUnreliableMessage(UPDATE_STATE_MESSAGE); - NBN_Writer *writer = NBN_GameClient_GetMessageWriter(); + NBN_Writer *writer = NBN_GameClient_CreateUnreliableMessage(UPDATE_STATE_MESSAGE); // Write the local client state to the message UpdateClientStateMessage_Write(writer, local_client_state); // Send the message to the server - if (NBN_GameClient_SendMessage() < 0) - { + if (NBN_GameClient_EnqueueMessage() < 0) { return -1; } return 0; } -static int SendColorUpdate(void) -{ +static int SendColorUpdate(void) { // Create a new CHANGE_COLOR_MESSAGE reliable message - NBN_GameClient_CreateReliableMessage(CHANGE_COLOR_MESSAGE); - NBN_Writer *writer = NBN_GameClient_GetMessageWriter(); + NBN_Writer *writer = NBN_GameClient_CreateReliableMessage(CHANGE_COLOR_MESSAGE); // Write the new client color to the message ChangeColorMessage_Write(writer, local_client_state.color); // Send the message to the server - if (NBN_GameClient_SendMessage() < 0) - { + if (NBN_GameClient_EnqueueMessage() < 0) { return -1; } @@ -337,8 +303,7 @@ static int SendColorUpdate(void) bool color_key_pressed = false; -static int Update(void) -{ +static int Update(void) { if (!spawned) return 0; @@ -354,15 +319,13 @@ static int Update(void) local_client_state.x = MIN(GAME_WIDTH - 50, local_client_state.x + 5); // Color switching - if (IsKeyDown(KEY_SPACE) && !color_key_pressed) - { + if (IsKeyDown(KEY_SPACE) && !color_key_pressed) { color_key_pressed = true; local_client_state.color = (local_client_state.color + 1) % MAX_COLORS; TraceLog(LOG_INFO, "Switched color, new color: %d", local_client_state.color); - if (SendColorUpdate() < 0) - { + if (SendColorUpdate() < 0) { TraceLog(LOG_WARNING, "Failed to send color update"); return -1; @@ -380,8 +343,7 @@ static int Update(void) local_client_state.val = MAX(MIN_FLOAT_VAL, local_client_state.val - 0.005); // Send the latest local client state to the server - if (SendStateUpdate() < 0) - { + if (SendStateUpdate() < 0) { TraceLog(LOG_WARNING, "Failed to send client state update"); return -1; @@ -390,8 +352,7 @@ static int Update(void) return 0; } -void DrawClient(ClientState *state, bool is_local) -{ +void DrawClient(ClientState *state, bool is_local) { Color color = client_colors_to_raylib_colors[state->color]; const char *text = TextFormat("%.3f", state->val); int font_size = 20; @@ -404,8 +365,7 @@ void DrawClient(ClientState *state, bool is_local) DrawRectangleLinesEx((Rectangle){state->x, state->y, 50, 50}, 3, DARKBROWN); } -void DrawHUD(void) -{ +void DrawHUD(void) { NBN_ConnectionStats stats = NBN_GameClient_GetStats(); unsigned int ping = stats.ping * 1000; unsigned int packet_loss = stats.packet_loss * 100; @@ -417,30 +377,22 @@ void DrawHUD(void) DrawText(TextFormat("Download: %.1f Bps", stats.download_bandwidth), 450, 550, 32, MAROON); } -void Draw(void) -{ +void Draw(void) { BeginDrawing(); ClearBackground(LIGHTGRAY); - if (disconnected) - { - if (server_close_code == -1) - { + if (disconnected) { + if (server_close_code == -1) { if (connected) DrawText("Connection to the server was lost", 265, 280, 20, RED); else DrawText("Server cannot be reached", 265, 280, 20, RED); - } - else if (server_close_code == SERVER_FULL_CODE) - { + } else if (server_close_code == SERVER_FULL_CODE) { DrawText("Cannot connect, server is full", 265, 280, 20, RED); } - } - else if (connected && spawned) - { + } else if (connected && spawned) { // Start by drawing the remote clients - for (int i = 0; i < MAX_CLIENTS - 1; i++) - { + for (int i = 0; i < MAX_CLIENTS - 1; i++) { if (clients[i]) DrawClient(clients[i], false); } @@ -450,9 +402,7 @@ void Draw(void) // Finally draw the HUD DrawHUD(); - } - else - { + } else { DrawText("Connecting to server...", 265, 280, 20, RED); } @@ -462,8 +412,7 @@ void Draw(void) static double tick_dt = 1.0 / TICK_RATE; // Tick delta time (in seconds) static double acc = 0; -void UpdateAndDraw(void) -{ +void UpdateAndDraw(void) { // Very basic fixed timestep implementation. // Target FPS is either 100 (in desktop) or whatever the browser frame rate is (in web) but the simulation runs at // TICK_RATE ticks per second. @@ -473,14 +422,11 @@ void UpdateAndDraw(void) acc += GetFrameTime(); // Accumulates time // Simulates as many ticks as we can - while (acc >= tick_dt) - { + while (acc >= tick_dt) { int ev; - while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { + while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) { + if (ev < 0) { TraceLog(LOG_WARNING, "An occured while polling network events. Exit"); break; @@ -489,16 +435,13 @@ void UpdateAndDraw(void) HandleGameClientEvent(ev); } - if (connected && !disconnected) - { + if (connected && !disconnected) { if (Update() < 0) break; } - if (!disconnected) - { - if (NBN_GameClient_SendPackets() < 0) - { + if (!disconnected) { + if (NBN_GameClient_Flush() < 0) { TraceLog(LOG_ERROR, "An occured while flushing the send queue. Exit"); break; @@ -511,13 +454,11 @@ void UpdateAndDraw(void) Draw(); } -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { // Read command line arguments expect when we are running in a web browser. // When running in web browser we need another way to provide arguments (TODO) #ifdef __EMSCRIPTEN__ - if (ReadCommandLine(argc, argv) < 0) - { + if (ReadCommandLine(argc, argv) < 0) { printf("Usage: client [--packet_loss=] [--packet_duplication=] [--ping=] \ [--jitter=]\n"); @@ -542,20 +483,12 @@ int main(int argc, char *argv[]) NBN_UDP_Register(); // Register the UDP driver #endif // __EMSCRIPTEN__ - // Create a client configuration with a protocol name, the server host and the server port + // Initialize the client with a protocol name, the server host and the server port // protocol name has to be the same as the one used by the server - NBN_GameClient_Config config = NBN_GameClient_CreateConfig( - RAYLIB_EXAMPLE_PROTOCOL_NAME, "127.0.0.1", RAYLIB_EXAMPLE_PORT); - - // Register all the messages of our protocol - // both server and clients have to register the same messages - NBN_GameClient_RegisterMessageType(&config, CHANGE_COLOR_MESSAGE, CHANGE_COLOR_MESSAGE_MAX_LENGTH); - NBN_GameClient_RegisterMessageType(&config, UPDATE_STATE_MESSAGE, UPDATE_STATE_MESSAGE_MAX_LENGTH); - NBN_GameClient_RegisterMessageType(&config, GAME_STATE_MESSAGE, GAME_STATE_MESSAGE_MAX_LENGTH); + NBN_GameClient_Init(RAYLIB_EXAMPLE_PROTOCOL_NAME, "127.0.0.1", RAYLIB_EXAMPLE_PORT); // Start the client with the configuration - if (NBN_GameClient_Start(config) < 0) - { + if (NBN_GameClient_Start() < 0) { TraceLog(LOG_WARNING, "Game client failed to start. Exit"); return 1; @@ -565,12 +498,11 @@ int main(int argc, char *argv[]) NBN_GameClient_SetPing(GetOptions().ping); NBN_GameClient_SetJitter(GetOptions().jitter); NBN_GameClient_SetPacketLoss(GetOptions().packet_loss); - NBN_GameClient_SetPacketDuplication(GetOptions().packet_duplication); + NBN_GameClient_SetPacketDuplication(GetOptions().packet_duplication); // Main loop #ifdef __EMSCRIPTEN__ - while (true) - { + while (true) { UpdateAndDraw(); // Since we don't set any target FPS when running in a web browser we need to sleep for the correct amount @@ -578,8 +510,7 @@ int main(int argc, char *argv[]) emscripten_sleep(1000 / TARGET_FPS); } #else - while (!WindowShouldClose()) - { + while (!WindowShouldClose()) { UpdateAndDraw(); } #endif diff --git a/examples/raylib/server.c b/examples/raylib/server.c index b4e1222..15a1a1a 100644 --- a/examples/raylib/server.c +++ b/examples/raylib/server.c @@ -20,15 +20,16 @@ */ +#include #include // For Sleep function #if defined(__EMSCRIPTEN__) -#include +#include #elif defined(_WIN32) || defined(_WIN64) -#include +#include #include -#include +#include #else #include #endif @@ -36,8 +37,7 @@ #include "shared.h" // A simple structure to represent connected clients -typedef struct -{ +typedef struct { // Underlying nbnet connection, used to send messages to that particular client NBN_Connection *conn; @@ -52,15 +52,10 @@ static Client *clients[MAX_CLIENTS] = {NULL}; static unsigned int client_count = 0; // Spawn positions -static Vector2 spawns[] = { - (Vector2){50, 50}, - (Vector2){GAME_WIDTH - 100, 50}, - (Vector2){50, GAME_HEIGHT - 100}, - (Vector2){GAME_WIDTH - 100, GAME_HEIGHT - 100} -}; - -static void AcceptConnection(Vector2 spawn, NBN_Connection *conn) -{ +static Vector2 spawns[] = {(Vector2){50, 50}, (Vector2){GAME_WIDTH - 100, 50}, (Vector2){50, GAME_HEIGHT - 100}, + (Vector2){GAME_WIDTH - 100, GAME_HEIGHT - 100}}; + +static void AcceptConnection(Vector2 spawn, NBN_Connection *conn) { // Accept the connection with some data // this data can be read by the client upon processing the connection event NBN_Writer *writer = NBN_GameServer_GetConnectionDataWriter(); @@ -72,13 +67,11 @@ static void AcceptConnection(Vector2 spawn, NBN_Connection *conn) NBN_GameServer_AcceptIncomingConnection(); } -static int HandleNewConnection(void) -{ +static int HandleNewConnection(void) { TraceLog(LOG_INFO, "New connection"); // If the server is full - if (client_count == MAX_CLIENTS) - { + if (client_count == MAX_CLIENTS) { // Reject the connection (send a SERVER_FULL_CODE code to the client) TraceLog(LOG_INFO, "Connection rejected"); NBN_GameServer_RejectIncomingConnectionWithCode(SERVER_FULL_CODE); @@ -104,10 +97,8 @@ static int HandleNewConnection(void) Client *client = NULL; // Find a free slot in the clients array and create a new client - for (int i = 0; i < MAX_CLIENTS; i++) - { - if (clients[i] == NULL) - { + for (int i = 0; i < MAX_CLIENTS; i++) { + if (clients[i] == NULL) { client = malloc(sizeof(Client)); clients[i] = client; @@ -128,12 +119,9 @@ static int HandleNewConnection(void) return 0; } -static void DestroyClient(Client *client) -{ - for (int i = 0; i < MAX_CLIENTS; i++) - { - if (clients[i] && clients[i]->state.client_id == client->state.client_id) - { +static void DestroyClient(Client *client) { + for (int i = 0; i < MAX_CLIENTS; i++) { + if (clients[i] && clients[i]->state.client_id == client->state.client_id) { clients[i] = NULL; return; @@ -143,8 +131,7 @@ static void DestroyClient(Client *client) free(client); } -static void HandleClientDisconnection(void) -{ +static void HandleClientDisconnection(void) { NBN_DisconnectionInfo info = NBN_GameServer_GetDisconnectionInfo(); TraceLog(LOG_INFO, "Client has disconnected (id: %d)", info.conn_id); @@ -157,93 +144,83 @@ static void HandleClientDisconnection(void) client_count--; } -static int HandleUpdateStateMessage(Client *sender) -{ +static int HandleUpdateStateMessage(Client *sender) { // Update the state of the client with the data from the received UPDATE_STATE_MESSAGE message NBN_Reader *reader = NBN_GameServer_GetMessageReader(); return UpdateClientStateMessage_Read(reader, &sender->state); } -static int HandleChangeColorMessage(Client *sender) -{ +static int HandleChangeColorMessage(Client *sender) { // Update the client color NBN_Reader *reader = NBN_GameServer_GetMessageReader(); return ChangeColorMessage_Read(reader, &sender->state.color); } -static int HandleReceivedMessage(void) -{ +static int HandleReceivedMessage(void) { // Fetch info about the last received message NBN_MessageInfo msg_info = NBN_GameServer_GetMessageInfo(); assert(msg_info.sender != NULL); Client *sender = msg_info.sender->user_data; assert(sender != NULL); - switch (msg_info.type) - { - case UPDATE_STATE_MESSAGE: - // The server received a client state update - return HandleUpdateStateMessage(sender); + switch (msg_info.type) { + case UPDATE_STATE_MESSAGE: + // The server received a client state update + return HandleUpdateStateMessage(sender); - case CHANGE_COLOR_MESSAGE: - // The server received a client switch color action - return HandleChangeColorMessage(sender); + case CHANGE_COLOR_MESSAGE: + // The server received a client switch color action + return HandleChangeColorMessage(sender); } // Received an unexpected message return -1; } -static int HandleGameServerEvent(int ev) -{ - switch (ev) - { - case NBN_NEW_CONNECTION: - // A new client has requested a connection - if (HandleNewConnection() < 0) - return -1; - break; +static int HandleGameServerEvent(int ev) { + switch (ev) { + case NBN_NEW_CONNECTION: + // A new client has requested a connection + if (HandleNewConnection() < 0) + return -1; + break; - case NBN_CLIENT_DISCONNECTED: - // A previously connected client has disconnected - HandleClientDisconnection(); - break; + case NBN_CLIENT_DISCONNECTED: + // A previously connected client has disconnected + HandleClientDisconnection(); + break; - case NBN_CLIENT_MESSAGE_RECEIVED: - // A message from a client has been received - if (HandleReceivedMessage() < 0) - { - // TODO: kick client - return -1; - } - break; + case NBN_CLIENT_MESSAGE_RECEIVED: + // A message from a client has been received + if (HandleReceivedMessage() < 0) { + // TODO: kick client + return -1; + } + break; } return 0; } // Broadcasts the latest game state to all connected clients -static int BroadcastGameState(void) -{ +static int BroadcastGameState(void) { static GameState game_state; unsigned int client_index = 0; // Loop over the clients and build the game state - for (int i = 0; i < MAX_CLIENTS; i++) - { + for (int i = 0; i < MAX_CLIENTS; i++) { Client *client = clients[i]; - if (client == NULL) continue; + if (client == NULL) + continue; - game_state.client_states[client_index] = (ClientState) { - .client_id = client->state.client_id, - .x = client->state.x, - .y = client->state.y, - .val = client->state.val, - .color = client->state.color - }; + game_state.client_states[client_index] = (ClientState){.client_id = client->state.client_id, + .x = client->state.x, + .y = client->state.y, + .val = client->state.val, + .color = client->state.color}; client_index++; } @@ -252,49 +229,27 @@ static int BroadcastGameState(void) game_state.client_count = client_count; // Create a unreliable message GAME_STATE_MESSAGE and write to it - NBN_GameServer_CreateUnreliableMessage(GAME_STATE_MESSAGE); - NBN_Writer *writer = NBN_GameServer_GetMessageWriter(); - + NBN_Writer *writer = NBN_GameServer_CreateUnreliableMessage(GAME_STATE_MESSAGE); GameStateMessage_Write(writer, &game_state); - for (int i = 0; i < MAX_CLIENTS; i++) - { - Client *client = clients[i]; - - if (client == NULL) continue; - - if (NBN_GameServer_SendMessageTo(client->conn) < 0) - { - return -1; - } - } - - return 0; - - // Broadcast the message to all connected clients - //return NBN_GameServer_BroadcastMessage(); + return NBN_GameServer_EnqueueBroadcastMessage(); } static bool running = true; #ifndef __EMSCRIPTEN__ -static void SigintHandler(int dummy) -{ - running = false; -} +static void SigintHandler(int dummy) { running = false; } #endif -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { #ifndef __EMSCRIPTEN__ signal(SIGINT, SigintHandler); #endif // Read command line arguments - if (ReadCommandLine(argc, argv)) - { + if (ReadCommandLine(argc, argv)) { printf("Usage: server [--packet_loss=] [--packet_duplication=] [--ping=] \ [--jitter=]\n"); @@ -311,42 +266,31 @@ int main(int argc, char *argv[]) #endif // __EMSCRIPTEN__ - // Create a server configuration with a protocol name and a port + // Initialize the server with a protocol name and a port // protocol name has to match between the server and the clients - NBN_GameServer_Config config = NBN_GameServer_CreateConfig( - RAYLIB_EXAMPLE_PROTOCOL_NAME, RAYLIB_EXAMPLE_PORT); - - // Register all the messages of our protocol - // both server and clients have to register the same messages - NBN_GameServer_RegisterMessageType(&config, CHANGE_COLOR_MESSAGE, CHANGE_COLOR_MESSAGE_MAX_LENGTH); - NBN_GameServer_RegisterMessageType(&config, UPDATE_STATE_MESSAGE, UPDATE_STATE_MESSAGE_MAX_LENGTH); - NBN_GameServer_RegisterMessageType(&config, GAME_STATE_MESSAGE, GAME_STATE_MESSAGE_MAX_LENGTH); + NBN_GameServer_Init(RAYLIB_EXAMPLE_PROTOCOL_NAME, RAYLIB_EXAMPLE_PORT); // Start the server with the configuration - if (NBN_GameServer_Start(config) < 0) - { + if (NBN_GameServer_Start() < 0) { TraceLog(LOG_ERROR, "Game server failed to start. Exit"); return 1; - } + } // Network conditions simulated variables (read from the command line, default is always 0) NBN_GameServer_SetPing(GetOptions().ping); NBN_GameServer_SetJitter(GetOptions().jitter); NBN_GameServer_SetPacketLoss(GetOptions().packet_loss); - NBN_GameServer_SetPacketDuplication(GetOptions().packet_duplication); + NBN_GameServer_SetPacketDuplication(GetOptions().packet_duplication); float tick_dt = 1.f / TICK_RATE; // Tick delta time - while (running) - { + while (running) { int ev; // Poll for server events - while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { + while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) { + if (ev < 0) { TraceLog(LOG_ERROR, "An occured while polling network events. Exit"); break; @@ -356,16 +300,14 @@ int main(int argc, char *argv[]) break; } - if (BroadcastGameState() < 0) - { + if (BroadcastGameState() < 0) { TraceLog(LOG_ERROR, "An occured while broadcasting game states. Exit"); break; } // Pack all enqueued messages as packets and send them - if (NBN_GameServer_SendPackets() < 0) - { + if (NBN_GameServer_Flush() < 0) { TraceLog(LOG_ERROR, "An occured while flushing the send queue. Exit"); break; From caa187ff5717f1d2786afaa96517b189a134fe43 Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Tue, 27 Jan 2026 17:47:56 +0100 Subject: [PATCH 14/85] update soak test --- soak/CMakeLists.txt | 2 +- soak/client.c | 7 +++---- soak/server.c | 8 +++----- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/soak/CMakeLists.txt b/soak/CMakeLists.txt index b68b584..61f3529 100644 --- a/soak/CMakeLists.txt +++ b/soak/CMakeLists.txt @@ -15,7 +15,7 @@ unset(CPP_COMPILE) add_executable(client client.c soak.c logging.c cargs.c) add_executable(server server.c soak.c logging.c cargs.c) -add_compile_options(-Wall -Wextra -Wpedantic) +add_compile_options(-Wall -Wextra -Wpedantic -std=c99) target_compile_definitions(client PUBLIC NBN_DEBUG NBN_DISABLE_STALE_CONNECTION_DETECTION NBN_USE_PACKET_SIMULATOR SOAK_CLIENT) target_compile_definitions(server PUBLIC NBN_DEBUG NBN_DISABLE_STALE_CONNECTION_DETECTION NBN_USE_PACKET_SIMULATOR SOAK_SERVER) diff --git a/soak/client.c b/soak/client.c index 924174a..300233f 100644 --- a/soak/client.c +++ b/soak/client.c @@ -121,12 +121,11 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { Soak_LogInfo("Send soak message (id: %d, data length: %d)", msg_id, data_length); // TODO: support big messages - NBN_GameClient_CreateMessage(SOAK_MESSAGE_SMALL, channel->id); - NBN_Writer *writer = NBN_GameClient_GetMessageWriter(); + NBN_Writer *writer = NBN_GameClient_CreateMessage(SOAK_MESSAGE_SMALL, channel->id); SoakMessage_Write(writer, msg_id, entry->data, entry->length); - if (NBN_GameClient_SendMessage() < 0) + if (NBN_GameClient_EnqueueMessage() < 0) return -1; channel->sent_message_count++; @@ -257,7 +256,7 @@ static int Tick(void *data) { } } - if (NBN_GameClient_SendPackets() < 0) { + if (NBN_GameClient_Flush() < 0) { Soak_LogError("Failed to flush game client send queue. Exit"); return -1; diff --git a/soak/server.c b/soak/server.c index 46e74ff..88d23e5 100644 --- a/soak/server.c +++ b/soak/server.c @@ -140,16 +140,14 @@ static void EchoReceivedSoakMessages(void) { while (channel->echo_queue.count > 0) { Soak_MessageEntry *msg_entry = &channel->echo_queue.messages[channel->echo_queue.head]; - - NBN_GameServer_CreateMessage(SOAK_MESSAGE_SMALL, channel->id); // TODO: support big - NBN_Writer *writer = NBN_GameServer_GetMessageWriter(); + NBN_Writer *writer = NBN_GameServer_CreateMessage(SOAK_MESSAGE_SMALL, channel->id); // TODO: support big SoakMessage_Write(writer, msg_entry->msg_id, msg_entry->data, msg_entry->length); Soak_LogInfo("Send soak message %d's echo (length: %d) to client %d", msg_entry->msg_id, msg_entry->length, soak_client->conn->id); - if (NBN_GameServer_SendMessageTo(soak_client->conn) < 0) { + if (NBN_GameServer_EnqueueMessageFor(soak_client->conn) < 0) { Soak_LogError("Failed to send soak message to client %d, closing client", soak_client->conn->id); if (NBN_GameServer_CloseClient(soak_client->conn) < 0) { @@ -280,7 +278,7 @@ static int Tick(void *data) { EchoReceivedSoakMessages(); - if (NBN_GameServer_SendPackets() < 0) { + if (NBN_GameServer_Flush() < 0) { Soak_LogError("Failed to flush game server send queue. Exit"); return -1; From 49d4a3873dd85f440131d93794adfa8f7d5f77b7 Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Tue, 27 Jan 2026 17:48:15 +0100 Subject: [PATCH 15/85] update drivers --- net_drivers/udp.h | 228 +-- net_drivers/webrtc.h | 217 +-- net_drivers/{webrtc_c.h => webrtc_native.h} | 427 ++--- stb_ds.h | 1895 +++++++++++++++++++ 4 files changed, 2047 insertions(+), 720 deletions(-) rename net_drivers/{webrtc_c.h => webrtc_native.h} (52%) create mode 100644 stb_ds.h diff --git a/net_drivers/udp.h b/net_drivers/udp.h index 14a8d83..085f8bb 100644 --- a/net_drivers/udp.h +++ b/net_drivers/udp.h @@ -48,6 +48,9 @@ void NBN_UDP_Register(void); #include "../nbnet.h" #endif +#define STB_DS_IMPLEMENTATION +#include "../stb_ds.h" + #define NBN_UDP_DRIVER_ID 0 #define NBN_UDP_DRIVER_NAME "UDP" @@ -104,202 +107,6 @@ typedef struct { static SOCKET nbn_udp_sock; -static bool CompareIPAddresses(NBN_IPAddress ip_addr1, NBN_IPAddress ip_addr2); - -#pragma region Hashtable - -#define HTABLE_DEFAULT_INITIAL_CAPACITY 32 -#define HTABLE_LOAD_FACTOR_THRESHOLD 0.75 - -typedef struct { - NBN_IPAddress ip_addr; - NBN_UDP_Connection *conn; - unsigned int slot; -} NBN_UDP_HTableEntry; - -typedef struct { - NBN_UDP_HTableEntry **internal_array; - unsigned int capacity; - unsigned int count; - float load_factor; -} NBN_UDP_HTable; - -static NBN_UDP_HTable *NBN_UDP_HTable_Create(void); -static NBN_UDP_HTable *NBN_UDP_HTable_CreateWithCapacity(unsigned int); -static void NBN_UDP_HTable_Destroy(NBN_UDP_HTable *); -static void NBN_UDP_HTable_Add(NBN_UDP_HTable *, NBN_IPAddress, NBN_UDP_Connection *); -static NBN_UDP_Connection *NBN_UDP_HTable_Get(NBN_UDP_HTable *, NBN_IPAddress); -static NBN_UDP_Connection *NBN_UDP_HTable_Remove(NBN_UDP_HTable *, NBN_IPAddress); -static void NBN_UDP_HTable_InsertEntry(NBN_UDP_HTable *, NBN_UDP_HTableEntry *); -static void NBN_UDP_HTable_RemoveEntry(NBN_UDP_HTable *, NBN_UDP_HTableEntry *); -static unsigned int NBN_UDP_HTable_FindFreeSlot(NBN_UDP_HTable *, NBN_UDP_HTableEntry *, bool *); -static NBN_UDP_HTableEntry *NBN_UDP_HTable_FindEntry(NBN_UDP_HTable *, NBN_IPAddress); -static void NBN_UDP_HTable_Grow(NBN_UDP_HTable *); -static unsigned long NBN_UDP_HTable_HashSDBM(NBN_IPAddress); - -static NBN_UDP_HTable *NBN_UDP_HTable_Create(void) { - return NBN_UDP_HTable_CreateWithCapacity(HTABLE_DEFAULT_INITIAL_CAPACITY); -} - -static NBN_UDP_HTable *NBN_UDP_HTable_CreateWithCapacity(unsigned int capacity) { - NBN_UDP_HTable *htable = (NBN_UDP_HTable *)malloc(sizeof(NBN_UDP_HTable)); - - htable->internal_array = (NBN_UDP_HTableEntry **)malloc(sizeof(NBN_UDP_HTableEntry *) * capacity); - htable->capacity = capacity; - htable->count = 0; - htable->load_factor = 0; - - for (unsigned int i = 0; i < htable->capacity; i++) - htable->internal_array[i] = NULL; - - return htable; -} - -static void NBN_UDP_HTable_Destroy(NBN_UDP_HTable *htable) { - for (unsigned int i = 0; i < htable->capacity; i++) { - NBN_UDP_HTableEntry *entry = htable->internal_array[i]; - - if (entry) - free(entry); - } - - free(htable->internal_array); - free(htable); -} - -static void NBN_UDP_HTable_Add(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr, NBN_UDP_Connection *conn) { - NBN_UDP_HTableEntry *entry = (NBN_UDP_HTableEntry *)malloc(sizeof(NBN_UDP_HTableEntry)); - - entry->ip_addr = ip_addr; - entry->conn = conn; - - NBN_UDP_HTable_InsertEntry(htable, entry); - - if (htable->load_factor >= HTABLE_LOAD_FACTOR_THRESHOLD) - NBN_UDP_HTable_Grow(htable); -} - -static NBN_UDP_Connection *NBN_UDP_HTable_Get(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr) { - NBN_UDP_HTableEntry *entry = NBN_UDP_HTable_FindEntry(htable, ip_addr); - - return entry ? entry->conn : NULL; -} - -static NBN_UDP_Connection *NBN_UDP_HTable_Remove(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr) { - NBN_UDP_HTableEntry *entry = NBN_UDP_HTable_FindEntry(htable, ip_addr); - - if (entry) { - NBN_UDP_Connection *conn = entry->conn; - NBN_UDP_HTable_RemoveEntry(htable, entry); - - return conn; - } - - return NULL; -} - -static void NBN_UDP_HTable_InsertEntry(NBN_UDP_HTable *htable, NBN_UDP_HTableEntry *entry) { - bool use_existing_slot = false; - unsigned int slot = NBN_UDP_HTable_FindFreeSlot(htable, entry, &use_existing_slot); - - entry->slot = slot; - htable->internal_array[slot] = entry; - - if (!use_existing_slot) { - htable->count++; - htable->load_factor = (float)htable->count / htable->capacity; - } -} - -static void NBN_UDP_HTable_RemoveEntry(NBN_UDP_HTable *htable, NBN_UDP_HTableEntry *entry) { - htable->internal_array[entry->slot] = NULL; - - free(entry); - - htable->count--; - htable->load_factor = (float)htable->count / htable->capacity; -} - -static unsigned int NBN_UDP_HTable_FindFreeSlot(NBN_UDP_HTable *htable, NBN_UDP_HTableEntry *entry, - bool *use_existing_slot) { - unsigned long hash = NBN_UDP_HTable_HashSDBM(entry->ip_addr); - unsigned int slot; - - // quadratic probing - - NBN_UDP_HTableEntry *current_entry; - unsigned int i = 0; - - do { - slot = (hash + (int)pow(i, 2)) % htable->capacity; - current_entry = htable->internal_array[slot]; - - i++; - } while (current_entry != NULL && !CompareIPAddresses(current_entry->ip_addr, entry->ip_addr)); - - if (current_entry != NULL) // it means the current entry as the same key as the inserted entry - { - *use_existing_slot = true; - - free(current_entry); - } - - return slot; -} - -static NBN_UDP_HTableEntry *NBN_UDP_HTable_FindEntry(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr) { - unsigned long hash = NBN_UDP_HTable_HashSDBM(ip_addr); - unsigned int slot; - - // quadratic probing - - NBN_UDP_HTableEntry *current_entry; - unsigned int i = 0; - - do { - slot = (hash + (int)pow(i, 2)) % htable->capacity; - current_entry = htable->internal_array[slot]; - - if (current_entry != NULL && CompareIPAddresses(current_entry->ip_addr, ip_addr)) { - return current_entry; - } - - i++; - } while (i < htable->capacity); - - return NULL; -} - -static void NBN_UDP_HTable_Grow(NBN_UDP_HTable *htable) { - unsigned int old_capacity = htable->capacity; - unsigned int new_capacity = old_capacity * 2; - NBN_UDP_HTableEntry **old_internal_array = htable->internal_array; - NBN_UDP_HTableEntry **new_internal_array = - (NBN_UDP_HTableEntry **)malloc(sizeof(NBN_UDP_HTableEntry *) * new_capacity); - - for (unsigned int i = 0; i < new_capacity; i++) { - new_internal_array[i] = NULL; - } - - htable->internal_array = new_internal_array; - htable->capacity = new_capacity; - htable->count = 0; - htable->load_factor = 0; - - // rehash - - for (unsigned int i = 0; i < old_capacity; i++) { - if (old_internal_array[i]) - NBN_UDP_HTable_InsertEntry(htable, old_internal_array[i]); - } - - free(old_internal_array); -} - -static unsigned long NBN_UDP_HTable_HashSDBM(NBN_IPAddress ip_addr) { return ip_addr.host ^ ip_addr.port; } - -#pragma endregion // Hashtable - #pragma region Socket functions #ifdef PLATFORM_WINDOWS @@ -417,7 +224,10 @@ static char *GetLastErrorMessage(void) { #pragma region Game server typedef struct NBN_UDP_Server { - NBN_UDP_HTable *connections; + struct { + NBN_IPAddress key; + NBN_UDP_Connection *value; + } *connections; uint32_t next_conn_id; // nbnet connection ids, starts at 1 uint32_t protocol_id; } NBN_UDP_Server; @@ -428,7 +238,8 @@ static NBN_Connection *FindOrCreateClientConnectionByAddress(NBN_IPAddress); static int NBN_UDP_ServStart(uint32_t protocol_id, uint16_t port) { nbn_udp_serv.protocol_id = protocol_id; - nbn_udp_serv.connections = NBN_UDP_HTable_Create(); + + hmdefault(nbn_udp_serv.connections, NULL); if (InitSocket() < 0) return NBN_ERROR; @@ -440,7 +251,7 @@ static int NBN_UDP_ServStart(uint32_t protocol_id, uint16_t port) { } static void NBN_UDP_ServStop(void) { - NBN_UDP_HTable_Destroy(nbn_udp_serv.connections); + hmfree(nbn_udp_serv.connections); DeinitSocket(); } @@ -486,14 +297,17 @@ static int NBN_UDP_ServRecvPackets(void) { } static void NBN_UDP_ServRemoveClientConnection(NBN_Connection *connection) { - assert(connection != NULL); + NBN_Assert(connection != NULL); - NBN_UDP_Connection *udp_conn = - NBN_UDP_HTable_Remove(nbn_udp_serv.connections, ((NBN_UDP_Connection *)connection->driver_data)->address); + NBN_IPAddress address = ((NBN_UDP_Connection *)connection->driver_data)->address; + NBN_UDP_Connection *udp_conn = hmget(nbn_udp_serv.connections, address); if (udp_conn) { NBN_LogDebug("Destroyed UDP connection %d", connection->id); + int ret = hmdel(nbn_udp_serv.connections, address); + + NBN_Assert(ret == 1); free(udp_conn); } } @@ -518,7 +332,7 @@ static int NBN_UDP_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *connecti } static NBN_Connection *FindOrCreateClientConnectionByAddress(NBN_IPAddress address) { - NBN_UDP_Connection *udp_conn = NBN_UDP_HTable_Get(nbn_udp_serv.connections, address); + NBN_UDP_Connection *udp_conn = hmget(nbn_udp_serv.connections, address); if (udp_conn == NULL) { /* this is a new connection */ @@ -532,7 +346,7 @@ static NBN_Connection *FindOrCreateClientConnectionByAddress(NBN_IPAddress addre udp_conn->address = address; udp_conn->conn = NBN_GameServer_CreateClientConnection(NBN_UDP_DRIVER_ID, udp_conn, udp_conn->id); - NBN_UDP_HTable_Add(nbn_udp_serv.connections, address, udp_conn); + hmput(nbn_udp_serv.connections, address, udp_conn); NBN_LogDebug("New UDP connection (id: %d)", udp_conn->id); @@ -546,10 +360,6 @@ static NBN_Connection *FindOrCreateClientConnectionByAddress(NBN_IPAddress addre return udp_conn->conn; } -static bool CompareIPAddresses(NBN_IPAddress ip_addr1, NBN_IPAddress ip_addr2) { - return ip_addr1.host == ip_addr2.host && ip_addr1.port == ip_addr2.port; -} - #pragma endregion /* Game server */ #pragma region Game client @@ -594,7 +404,7 @@ static void NBN_UDP_CliStop(void) { static int NBN_UDP_CliRecvPackets(void) { NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)nbn_udp_cli.server_conn->driver_data; - NBN_Packet packet = {0}; + static NBN_Packet packet = {0}; SOCKADDR_IN src_addr; socklen_t src_addr_len = sizeof(src_addr); diff --git a/net_drivers/webrtc.h b/net_drivers/webrtc.h index f0566b8..263f4d2 100644 --- a/net_drivers/webrtc.h +++ b/net_drivers/webrtc.h @@ -66,198 +66,6 @@ typedef struct { NBN_Connection *conn; } NBN_WebRTC_Peer; -#pragma region Hashtable - -#define HTABLE_DEFAULT_INITIAL_CAPACITY 32 -#define HTABLE_LOAD_FACTOR_THRESHOLD 0.75 - -typedef struct { - uint32_t peer_id; - NBN_WebRTC_Peer *peer; - unsigned int slot; -} NBN_WebRTC_HTableEntry; - -typedef struct { - NBN_WebRTC_HTableEntry **internal_array; - unsigned int capacity; - unsigned int count; - float load_factor; -} NBN_WebRTC_HTable; - -static NBN_WebRTC_HTable *NBN_WebRTC_HTable_Create(void); -static NBN_WebRTC_HTable *NBN_WebRTC_HTable_CreateWithCapacity(unsigned int); -static void NBN_WebRTC_HTable_Destroy(NBN_WebRTC_HTable *); -static void NBN_WebRTC_HTable_Add(NBN_WebRTC_HTable *, uint32_t, NBN_WebRTC_Peer *); -static NBN_WebRTC_Peer *NBN_WebRTC_HTable_Get(NBN_WebRTC_HTable *, uint32_t); -static NBN_WebRTC_Peer *NBN_WebRTC_HTable_Remove(NBN_WebRTC_HTable *, uint32_t); -static void NBN_WebRTC_HTable_InsertEntry(NBN_WebRTC_HTable *, NBN_WebRTC_HTableEntry *); -static void NBN_WebRTC_HTable_RemoveEntry(NBN_WebRTC_HTable *, NBN_WebRTC_HTableEntry *); -static unsigned int NBN_WebRTC_HTable_FindFreeSlot(NBN_WebRTC_HTable *, NBN_WebRTC_HTableEntry *, bool *); -static NBN_WebRTC_HTableEntry *NBN_WebRTC_HTable_FindEntry(NBN_WebRTC_HTable *, uint32_t); -static void NBN_WebRTC_HTable_Grow(NBN_WebRTC_HTable *); - -static NBN_WebRTC_HTable *NBN_WebRTC_HTable_Create(void) { - return NBN_WebRTC_HTable_CreateWithCapacity(HTABLE_DEFAULT_INITIAL_CAPACITY); -} - -static NBN_WebRTC_HTable *NBN_WebRTC_HTable_CreateWithCapacity(unsigned int capacity) { - NBN_WebRTC_HTable *htable = (NBN_WebRTC_HTable *)malloc(sizeof(NBN_WebRTC_HTable)); - - htable->internal_array = (NBN_WebRTC_HTableEntry **)malloc(sizeof(NBN_WebRTC_HTableEntry *) * capacity); - htable->capacity = capacity; - htable->count = 0; - htable->load_factor = 0; - - for (unsigned int i = 0; i < htable->capacity; i++) - htable->internal_array[i] = NULL; - - return htable; -} - -static void NBN_WebRTC_HTable_Destroy(NBN_WebRTC_HTable *htable) { - for (unsigned int i = 0; i < htable->capacity; i++) { - NBN_WebRTC_HTableEntry *entry = htable->internal_array[i]; - - if (entry) - free(entry); - } - - free(htable->internal_array); - free(htable); -} - -static void NBN_WebRTC_HTable_Add(NBN_WebRTC_HTable *htable, uint32_t peer_id, NBN_WebRTC_Peer *peer) { - NBN_WebRTC_HTableEntry *entry = (NBN_WebRTC_HTableEntry *)malloc(sizeof(NBN_WebRTC_HTableEntry)); - - entry->peer_id = peer_id; - entry->peer = peer; - - NBN_WebRTC_HTable_InsertEntry(htable, entry); - - if (htable->load_factor >= HTABLE_LOAD_FACTOR_THRESHOLD) - NBN_WebRTC_HTable_Grow(htable); -} - -static NBN_WebRTC_Peer *NBN_WebRTC_HTable_Get(NBN_WebRTC_HTable *htable, uint32_t peer_id) { - NBN_WebRTC_HTableEntry *entry = NBN_WebRTC_HTable_FindEntry(htable, peer_id); - - return entry ? entry->peer : NULL; -} - -static NBN_WebRTC_Peer *NBN_WebRTC_HTable_Remove(NBN_WebRTC_HTable *htable, uint32_t peer_id) { - NBN_WebRTC_HTableEntry *entry = NBN_WebRTC_HTable_FindEntry(htable, peer_id); - - if (entry) { - NBN_WebRTC_Peer *peer = entry->peer; - - NBN_WebRTC_HTable_RemoveEntry(htable, entry); - - return peer; - } - - return NULL; -} - -static void NBN_WebRTC_HTable_InsertEntry(NBN_WebRTC_HTable *htable, NBN_WebRTC_HTableEntry *entry) { - bool use_existing_slot = false; - unsigned int slot = NBN_WebRTC_HTable_FindFreeSlot(htable, entry, &use_existing_slot); - - entry->slot = slot; - htable->internal_array[slot] = entry; - - if (!use_existing_slot) { - htable->count++; - htable->load_factor = (float)htable->count / htable->capacity; - } -} - -static void NBN_WebRTC_HTable_RemoveEntry(NBN_WebRTC_HTable *htable, NBN_WebRTC_HTableEntry *entry) { - htable->internal_array[entry->slot] = NULL; - - free(entry); - - htable->count--; - htable->load_factor = htable->count / htable->capacity; -} - -static unsigned int NBN_WebRTC_HTable_FindFreeSlot(NBN_WebRTC_HTable *htable, NBN_WebRTC_HTableEntry *entry, - bool *use_existing_slot) { - unsigned long hash = entry->peer_id; - unsigned int slot; - - // quadratic probing - - NBN_WebRTC_HTableEntry *current_entry; - unsigned int i = 0; - - do { - slot = (hash + (int)pow(i, 2)) % htable->capacity; - current_entry = htable->internal_array[slot]; - - i++; - } while (current_entry != NULL && current_entry->peer_id != entry->peer_id); - - if (current_entry != NULL) // it means the current entry as the same key as the inserted entry - { - *use_existing_slot = true; - - free(current_entry); - } - - return slot; -} - -static NBN_WebRTC_HTableEntry *NBN_WebRTC_HTable_FindEntry(NBN_WebRTC_HTable *htable, uint32_t peer_id) { - unsigned long hash = peer_id; - unsigned int slot; - - // quadratic probing - - NBN_WebRTC_HTableEntry *current_entry; - unsigned int i = 0; - - do { - slot = (hash + (int)pow(i, 2)) % htable->capacity; - current_entry = htable->internal_array[slot]; - - if (current_entry != NULL && current_entry->peer_id == peer_id) { - return current_entry; - } - - i++; - } while (i < htable->capacity); - - return NULL; -} - -static void NBN_WebRTC_HTable_Grow(NBN_WebRTC_HTable *htable) { - unsigned int old_capacity = htable->capacity; - unsigned int new_capacity = old_capacity * 2; - NBN_WebRTC_HTableEntry **old_internal_array = htable->internal_array; - NBN_WebRTC_HTableEntry **new_internal_array = - (NBN_WebRTC_HTableEntry **)malloc(sizeof(NBN_WebRTC_HTableEntry *) * new_capacity); - - for (unsigned int i = 0; i < new_capacity; i++) { - new_internal_array[i] = NULL; - } - - htable->internal_array = new_internal_array; - htable->capacity = new_capacity; - htable->count = 0; - htable->load_factor = 0; - - // rehash - - for (unsigned int i = 0; i < old_capacity; i++) { - if (old_internal_array[i]) - NBN_WebRTC_HTable_InsertEntry(htable, old_internal_array[i]); - } - - free(old_internal_array); -} - -#pragma endregion // Hashtable - #pragma region Game server /* --- JS API --- */ @@ -272,7 +80,10 @@ NBN_EXTERN void __js_game_server_stop(void); /* --- Driver implementation --- */ typedef struct NBN_WebRTC_Server { - NBN_WebRTC_HTable *peers; + struct { + int key; + NBN_WebRTC_Peer *value; + } *peers; uint8_t packet_buffer[NBN_PACKET_MAX_SIZE]; uint32_t protocol_id; } NBN_WebRTC_Server; @@ -286,25 +97,25 @@ static int NBN_WebRTC_ServStart(uint32_t protocol_id, uint16_t port) { if (__js_game_server_start(port) < 0) return -1; - nbn_wrtc_serv.peers = NBN_WebRTC_HTable_Create(); nbn_wrtc_serv.protocol_id = protocol_id; + hmdefault(nbn_wrtc_serv.peers, NULL); + return 0; } static void NBN_WebRTC_ServStop(void) { __js_game_server_stop(); - NBN_WebRTC_HTable_Destroy(nbn_wrtc_serv.peers); + hmfree(nbn_wrtc_serv.peers); } static int NBN_WebRTC_ServRecvPackets(void) { + static NBN_Packet packet = {0}; uint32_t peer_id; unsigned int len; while ((len = __js_game_server_dequeue_packet(&peer_id, (uint8_t *)nbn_wrtc_serv.packet_buffer)) > 0) { - NBN_Packet packet; - - NBN_WebRTC_Peer *peer = NBN_WebRTC_HTable_Get(nbn_wrtc_serv.peers, peer_id); + NBN_WebRTC_Peer *peer = hmget(nbn_wrtc_serv.peers, peer_id); if (peer == NULL) { if (GameServer_GetClientCount() >= NBN_MAX_CLIENTS) @@ -318,7 +129,7 @@ static int NBN_WebRTC_ServRecvPackets(void) { peer->conn = NBN_GameServer_CreateClientConnection(NBN_WEBRTC_DRIVER_ID, peer, nbn_wrtc_serv.protocol_id, peer_id); - NBN_WebRTC_HTable_Add(nbn_wrtc_serv.peers, peer_id, peer); + hmput(nbn_wrtc_serv.peers, peer_id, peer); NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, peer->conn); } @@ -339,9 +150,10 @@ static void NBN_WebRTC_ServRemoveClientConnection(NBN_Connection *conn) { __js_game_server_close_client_peer(conn->id); - NBN_WebRTC_Peer *peer = NBN_WebRTC_HTable_Remove(nbn_wrtc_serv.peers, ((NBN_WebRTC_Peer *)conn->driver_data)->id); + NBN_WebRTC_Peer *peer = (NBN_WebRTC_Native_Peer *)conn->driver_data; + int ret = hmdel(nbn_wrtc_c_serv.peers, peer->id); - if (peer) { + if (ret == 1) { NBN_LogDebug("Destroyed peer %d", peer->id); free(peer); @@ -388,11 +200,10 @@ static int NBN_WebRTC_CliStart(uint32_t protocol_id, const char *host, uint16_t static void NBN_WebRTC_CliStop(void) { __js_game_client_close(); } static int NBN_WebRTC_CliRecvPackets(void) { + static NBN_Packet packet = {0}; unsigned int len; while ((len = __js_game_client_dequeue_packet((uint8_t *)nbn_wrtc_serv.packet_buffer)) > 0) { - NBN_Packet packet; - if (NBN_Packet_InitRead(&packet, nbn_wrtc_cli.server_conn, nbn_wrtc_serv.packet_buffer, len) < 0) continue; diff --git a/net_drivers/webrtc_c.h b/net_drivers/webrtc_native.h similarity index 52% rename from net_drivers/webrtc_c.h rename to net_drivers/webrtc_native.h index 4f0e8b3..7367b3a 100644 --- a/net_drivers/webrtc_c.h +++ b/net_drivers/webrtc_native.h @@ -34,13 +34,15 @@ freely, subject to the following restrictions: How to use: 1. Include this header *once* after the nbnet header in the same file where you defined the NBNET_IMPL macro - 2. Call NBN_WebRTC_C_Register in both your client and server code before calling NBN_GameClient_Start or + 2. Call NBN_WebRTC_Native_Register in both your client and server code before calling NBN_GameClient_Start or NBN_GameServer_Start */ +#include "../stb_ds.h" #include "json.h" #include #include +#include #include #include @@ -48,10 +50,10 @@ freely, subject to the following restrictions: #include "../nbnet.h" #endif -#define NBN_WEBRTC_C_DRIVER_ID 2 -#define NBN_WEBRTC_C_DRIVER_NAME "WebRTC_C" +#define NBN_WEBRTC_NATIVE_DRIVER_ID 2 +#define NBN_WEBRTC_NATIVE_DRIVER_NAME "WebRTC_C" -typedef struct NBN_WebRTC_C_Config { +typedef struct NBN_WebRTC_Native_Config { bool enable_tls; const char *cert_path; const char *key_path; @@ -59,12 +61,12 @@ typedef struct NBN_WebRTC_C_Config { const char **ice_servers; unsigned int ice_servers_count; rtcLogLevel log_level; -} NBN_WebRTC_C_Config; +} NBN_WebRTC_Native_Config; -static NBN_WebRTC_C_Config nbn_wrtc_c_cfg; +static NBN_WebRTC_Native_Config nbn_wrtc_c_cfg; -void NBN_WebRTC_C_Register(NBN_WebRTC_C_Config config); -void NBN_WebRTC_C_Unregister(void); +void NBN_WebRTC_Native_Register(NBN_WebRTC_Native_Config config); +void NBN_WebRTC_Native_Unregister(void); #ifdef NBNET_IMPL @@ -73,208 +75,14 @@ typedef struct { int channel_id; int ws; NBN_Connection *conn; -} NBN_WebRTC_C_Peer; +} NBN_WebRTC_Native_Peer; -static void NBN_WebRTC_C_DestroyPeer(NBN_WebRTC_C_Peer *peer); - -#pragma region Hashtable - -#define HTABLE_DEFAULT_INITIAL_CAPACITY 32 -#define HTABLE_LOAD_FACTOR_THRESHOLD 0.75 - -typedef struct { - int peer_id; - NBN_WebRTC_C_Peer *peer; - unsigned int slot; -} NBN_WebRTC_C_HTableEntry; - -typedef struct { - NBN_WebRTC_C_HTableEntry **internal_array; - unsigned int capacity; - unsigned int count; - float load_factor; -} NBN_WebRTC_C_HTable; - -static NBN_WebRTC_C_HTable *NBN_WebRTC_C_HTable_Create(void); -static NBN_WebRTC_C_HTable *NBN_WebRTC_C_HTable_CreateWithCapacity(unsigned int); -static void NBN_WebRTC_C_HTable_Destroy(NBN_WebRTC_C_HTable *); -static void NBN_WebRTC_C_HTable_Add(NBN_WebRTC_C_HTable *, int, NBN_WebRTC_C_Peer *); -static NBN_WebRTC_C_Peer *NBN_WebRTC_C_HTable_Get(NBN_WebRTC_C_HTable *, int); -static NBN_WebRTC_C_Peer *NBN_WebRTC_C_HTable_Remove(NBN_WebRTC_C_HTable *, int); -static void NBN_WebRTC_C_HTable_InsertEntry(NBN_WebRTC_C_HTable *, NBN_WebRTC_C_HTableEntry *); -static void NBN_WebRTC_C_HTable_RemoveEntry(NBN_WebRTC_C_HTable *, NBN_WebRTC_C_HTableEntry *); -static unsigned int NBN_WebRTC_C_HTable_FindFreeSlot(NBN_WebRTC_C_HTable *, NBN_WebRTC_C_HTableEntry *, bool *); -static NBN_WebRTC_C_HTableEntry *NBN_WebRTC_C_HTable_FindEntry(NBN_WebRTC_C_HTable *, int); -static void NBN_WebRTC_C_HTable_Grow(NBN_WebRTC_C_HTable *); - -static NBN_WebRTC_C_HTable *NBN_WebRTC_C_HTable_Create(void) { - return NBN_WebRTC_C_HTable_CreateWithCapacity(HTABLE_DEFAULT_INITIAL_CAPACITY); -} - -static NBN_WebRTC_C_HTable *NBN_WebRTC_C_HTable_CreateWithCapacity(unsigned int capacity) { - NBN_WebRTC_C_HTable *htable = (NBN_WebRTC_C_HTable *)malloc(sizeof(NBN_WebRTC_C_HTable)); - - htable->internal_array = (NBN_WebRTC_C_HTableEntry **)malloc(sizeof(NBN_WebRTC_C_HTableEntry *) * capacity); - htable->capacity = capacity; - htable->count = 0; - htable->load_factor = 0; - - for (unsigned int i = 0; i < htable->capacity; i++) { - htable->internal_array[i] = NULL; - } - - return htable; -} - -static void NBN_WebRTC_C_HTable_Destroy(NBN_WebRTC_C_HTable *htable) { - for (unsigned int i = 0; i < htable->capacity; i++) { - NBN_WebRTC_C_HTableEntry *entry = htable->internal_array[i]; - - if (entry) { - NBN_WebRTC_C_DestroyPeer(entry->peer); - } - } - - free(htable->internal_array); - free(htable); -} - -static void NBN_WebRTC_C_HTable_Add(NBN_WebRTC_C_HTable *htable, int peer_id, NBN_WebRTC_C_Peer *peer) { - NBN_WebRTC_C_HTableEntry *entry = (NBN_WebRTC_C_HTableEntry *)malloc(sizeof(NBN_WebRTC_C_HTableEntry)); - - entry->peer_id = peer_id; - entry->peer = peer; - - NBN_WebRTC_C_HTable_InsertEntry(htable, entry); - - if (htable->load_factor >= HTABLE_LOAD_FACTOR_THRESHOLD) - NBN_WebRTC_C_HTable_Grow(htable); -} - -static NBN_WebRTC_C_Peer *NBN_WebRTC_C_HTable_Get(NBN_WebRTC_C_HTable *htable, int peer_id) { - NBN_WebRTC_C_HTableEntry *entry = NBN_WebRTC_C_HTable_FindEntry(htable, peer_id); - - return entry ? entry->peer : NULL; -} - -static NBN_WebRTC_C_Peer *NBN_WebRTC_C_HTable_Remove(NBN_WebRTC_C_HTable *htable, int peer_id) { - NBN_WebRTC_C_HTableEntry *entry = NBN_WebRTC_C_HTable_FindEntry(htable, peer_id); - - if (entry) { - NBN_WebRTC_C_Peer *peer = entry->peer; - - NBN_WebRTC_C_HTable_RemoveEntry(htable, entry); - - return peer; - } - - return NULL; -} - -static void NBN_WebRTC_C_HTable_InsertEntry(NBN_WebRTC_C_HTable *htable, NBN_WebRTC_C_HTableEntry *entry) { - bool use_existing_slot = false; - unsigned int slot = NBN_WebRTC_C_HTable_FindFreeSlot(htable, entry, &use_existing_slot); - - entry->slot = slot; - htable->internal_array[slot] = entry; - - if (!use_existing_slot) { - htable->count++; - htable->load_factor = (float)htable->count / htable->capacity; - } -} - -static void NBN_WebRTC_C_HTable_RemoveEntry(NBN_WebRTC_C_HTable *htable, NBN_WebRTC_C_HTableEntry *entry) { - htable->internal_array[entry->slot] = NULL; - - free(entry); - - htable->count--; - htable->load_factor = htable->count / htable->capacity; -} - -static unsigned int NBN_WebRTC_C_HTable_FindFreeSlot(NBN_WebRTC_C_HTable *htable, NBN_WebRTC_C_HTableEntry *entry, - bool *use_existing_slot) { - unsigned long hash = entry->peer_id; - unsigned int slot; - - // quadratic probing - - NBN_WebRTC_C_HTableEntry *current_entry; - unsigned int i = 0; - - do { - slot = (hash + (int)pow(i, 2)) % htable->capacity; - current_entry = htable->internal_array[slot]; - - i++; - } while (current_entry != NULL && current_entry->peer_id != entry->peer_id); - - if (current_entry != NULL) // it means the current entry as the same key as the inserted entry - { - *use_existing_slot = true; - - free(current_entry); - } - - return slot; -} - -static NBN_WebRTC_C_HTableEntry *NBN_WebRTC_C_HTable_FindEntry(NBN_WebRTC_C_HTable *htable, int peer_id) { - unsigned long hash = peer_id; - unsigned int slot; - - // quadratic probing - - NBN_WebRTC_C_HTableEntry *current_entry; - unsigned int i = 0; - - do { - slot = (hash + (int)pow(i, 2)) % htable->capacity; - current_entry = htable->internal_array[slot]; - - if (current_entry != NULL && current_entry->peer_id == peer_id) { - return current_entry; - } - - i++; - } while (i < htable->capacity); - - return NULL; -} - -static void NBN_WebRTC_C_HTable_Grow(NBN_WebRTC_C_HTable *htable) { - unsigned int old_capacity = htable->capacity; - unsigned int new_capacity = old_capacity * 2; - NBN_WebRTC_C_HTableEntry **old_internal_array = htable->internal_array; - NBN_WebRTC_C_HTableEntry **new_internal_array = - (NBN_WebRTC_C_HTableEntry **)malloc(sizeof(NBN_WebRTC_C_HTableEntry *) * new_capacity); - - for (unsigned int i = 0; i < new_capacity; i++) { - new_internal_array[i] = NULL; - } - - htable->internal_array = new_internal_array; - htable->capacity = new_capacity; - htable->count = 0; - htable->load_factor = 0; - - // rehash - - for (unsigned int i = 0; i < old_capacity; i++) { - if (old_internal_array[i]) - NBN_WebRTC_C_HTable_InsertEntry(htable, old_internal_array[i]); - } - - free(old_internal_array); -} - -#pragma endregion // Hashtable +static void NBN_WebRTC_Native_DestroyPeer(NBN_WebRTC_Native_Peer *peer); #pragma region String utils // IMPORTANT: res needs to be pre allocated and big enough to old the resulting string -static void NBN_WebRTC_C_StringReplaceAll(char *res, const char *str, const char *a, const char *b) { +static void NBN_WebRTC_Native_StringReplaceAll(char *res, const char *str, const char *a, const char *b) { char *substr = (char *)strstr(str, a); size_t len_a = strlen(a); size_t len_b = strlen(b); @@ -285,7 +93,7 @@ static void NBN_WebRTC_C_StringReplaceAll(char *res, const char *str, const char strncpy(res, str, pos); strncpy(res + pos, b, len_b); - NBN_WebRTC_C_StringReplaceAll(res + pos + len_b, str + pos + len_a, a, b); + NBN_WebRTC_Native_StringReplaceAll(res + pos + len_b, str + pos + len_a, a, b); } else { strncpy(res, str, strlen(str) + 1); } @@ -295,7 +103,7 @@ static void NBN_WebRTC_C_StringReplaceAll(char *res, const char *str, const char #pragma region WebRTC common -static char *NBN_WebRTC_C_ParseSignalingMessage(const char *msg, size_t msg_len, const char *type) { +static char *NBN_WebRTC_Native_ParseSignalingMessage(const char *msg, size_t msg_len, const char *type) { char *sdp = NULL; struct json_value_s *root = json_parse(msg, msg_len); // this has to be freed @@ -339,16 +147,16 @@ static char *NBN_WebRTC_C_ParseSignalingMessage(const char *msg, size_t msg_len, return sdp; } -static char *NBN_WebRTC_C_EscapeSDP(const char *sdp) { +static char *NBN_WebRTC_Native_EscapeSDP(const char *sdp) { size_t len = strlen(sdp) * 2; // TODO: kinda lame way of making sure it's going to be big enough, find a better way char *escaped_sdp = (char *)malloc(len); - NBN_WebRTC_C_StringReplaceAll(escaped_sdp, sdp, "\r\n", "\\r\\n"); + NBN_WebRTC_Native_StringReplaceAll(escaped_sdp, sdp, "\r\n", "\\r\\n"); return escaped_sdp; } -static void NBN_WebRTC_C_Log(rtcLogLevel level, const char *msg) { +static void NBN_WebRTC_Native_Log(rtcLogLevel level, const char *msg) { switch (level) { case RTC_LOG_FATAL: case RTC_LOG_ERROR: @@ -376,14 +184,14 @@ static void NBN_WebRTC_C_Log(rtcLogLevel level, const char *msg) { } } -static void NBN_WebRTC_C_OnWsError(int ws, const char *err_msg, void *user_ptr) { +static void NBN_WebRTC_Native_OnWsError(int ws, const char *err_msg, void *user_ptr) { (void)user_ptr; NBN_LogError("Error on WS %d: %s", ws, err_msg); } -static NBN_WebRTC_C_Peer *NBN_WebRTC_C_CreatePeer(int ws, rtcDescriptionCallbackFunc on_rtc_description_cb, - rtcStateChangeCallbackFunc on_state_change_cb) { +static NBN_WebRTC_Native_Peer *NBN_WebRTC_Native_CreatePeer(int ws, rtcDescriptionCallbackFunc on_rtc_description_cb, + rtcStateChangeCallbackFunc on_state_change_cb) { rtcConfiguration rtcCfg = {.iceServers = nbn_wrtc_c_cfg.ice_servers, .iceServersCount = (int)nbn_wrtc_c_cfg.ice_servers_count, .disableAutoNegotiation = false}; @@ -394,7 +202,7 @@ static NBN_WebRTC_C_Peer *NBN_WebRTC_C_CreatePeer(int ws, rtcDescriptionCallback return NULL; } - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)malloc(sizeof(NBN_WebRTC_C_Peer)); + NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)malloc(sizeof(NBN_WebRTC_Native_Peer)); peer->id = peer_id; peer->ws = ws; @@ -407,7 +215,7 @@ static NBN_WebRTC_C_Peer *NBN_WebRTC_C_CreatePeer(int ws, rtcDescriptionCallback if (ret < 0) { NBN_LogError("Failed to register local description callback for peer %d: %d", peer->id, ret); - NBN_WebRTC_C_DestroyPeer(peer); + NBN_WebRTC_Native_DestroyPeer(peer); return NULL; } @@ -415,7 +223,7 @@ static NBN_WebRTC_C_Peer *NBN_WebRTC_C_CreatePeer(int ws, rtcDescriptionCallback if (ret < 0) { NBN_LogError("Failed to register state change callback for peer %d: %d", peer_id, ret); - NBN_WebRTC_C_DestroyPeer(peer); + NBN_WebRTC_Native_DestroyPeer(peer); return NULL; } rtcDataChannelInit rtcDataChannel = { @@ -427,7 +235,7 @@ static NBN_WebRTC_C_Peer *NBN_WebRTC_C_CreatePeer(int ws, rtcDescriptionCallback if (channel_id < 0) { NBN_LogError("Failed to create data channel for peer %d: %d", peer_id, channel_id); - NBN_WebRTC_C_DestroyPeer(peer); + NBN_WebRTC_Native_DestroyPeer(peer); return NULL; } @@ -438,7 +246,7 @@ static NBN_WebRTC_C_Peer *NBN_WebRTC_C_CreatePeer(int ws, rtcDescriptionCallback return peer; } -static void NBN_WebRTC_C_DestroyPeer(NBN_WebRTC_C_Peer *peer) { +static void NBN_WebRTC_Native_DestroyPeer(NBN_WebRTC_Native_Peer *peer) { NBN_LogDebug("Destroying peer %d", peer->id); if (peer->channel_id >= 0) { @@ -450,8 +258,8 @@ static void NBN_WebRTC_C_DestroyPeer(NBN_WebRTC_C_Peer *peer) { free(peer); } -static void NBN_WebRTC_C_ProcessLocalDescription(NBN_WebRTC_C_Peer *peer, const char *sdp, const char *type) { - char *escaped_sdp = NBN_WebRTC_C_EscapeSDP(sdp); +static void NBN_WebRTC_Native_ProcessLocalDescription(NBN_WebRTC_Native_Peer *peer, const char *sdp, const char *type) { + char *escaped_sdp = NBN_WebRTC_Native_EscapeSDP(sdp); size_t signaling_json_size = snprintf(NULL, 0, "{\"type\":\"%s\", \"sdp\":\"%s\"}", type, escaped_sdp) + 1; char *signaling_json = (char *)malloc(signaling_json_size); @@ -461,14 +269,14 @@ static void NBN_WebRTC_C_ProcessLocalDescription(NBN_WebRTC_C_Peer *peer, const // pass -1 as the size (assume signaling_json to be a null-terminated string) if (rtcSendMessage(peer->ws, signaling_json, -1) < 0) { - NBN_WebRTC_C_DestroyPeer(peer); + NBN_WebRTC_Native_DestroyPeer(peer); } free(signaling_json); free(escaped_sdp); } -static void NBN_WebRTC_C_ProcessSignalingMessage(NBN_WebRTC_C_Peer *peer, int ws, const char *msg, int size, - const char *type) { +static void NBN_WebRTC_Native_ProcessSignalingMessage(NBN_WebRTC_Native_Peer *peer, int ws, const char *msg, int size, + const char *type) { // for some reason the size of the message is negative // in libdatachannel documentation (https://github.com/paullouisageneau/libdatachannel/blob/master/DOC.md) there is // mention of: size: if size >= 0, data is interpreted as a binary message of length size, otherwise it is @@ -482,7 +290,7 @@ static void NBN_WebRTC_C_ProcessSignalingMessage(NBN_WebRTC_C_Peer *peer, int ws NBN_LogDebug("Received signaling message on WS %d (size: %d): %s", ws, size, msg); - char *sdp = NBN_WebRTC_C_ParseSignalingMessage(msg, size, type); + char *sdp = NBN_WebRTC_Native_ParseSignalingMessage(msg, size, type); if (!sdp) { NBN_LogWarning("Failed to parse signaling data for WS %d", ws); @@ -506,17 +314,20 @@ static void NBN_WebRTC_C_ProcessSignalingMessage(NBN_WebRTC_C_Peer *peer, int ws #pragma region Game server -typedef struct NBN_WebRTC_C_Server { +typedef struct NBN_WebRTC_Native_Server { int wsserver; - NBN_WebRTC_C_HTable *peers; + struct { + int key; + NBN_WebRTC_Native_Peer *value; + } *peers; uint16_t ws_port; uint32_t protocol_id; char packet_buffer[NBN_PACKET_MAX_SIZE]; -} NBN_WebRTC_C_Server; +} NBN_WebRTC_Native_Server; -static NBN_WebRTC_C_Server nbn_wrtc_c_serv = {0, NULL, false, 0, 0, {0}}; +static NBN_WebRTC_Native_Server nbn_wrtc_c_serv = {0, NULL, false, 0, 0, {0}}; -static void NBN_WebRTC_C_Serv_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { +static void NBN_WebRTC_Native_Serv_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { NBN_LogDebug("Processing local description of type '%s'", type); if (strncmp(type, "answer", strlen("answer")) != 0) { @@ -524,25 +335,25 @@ static void NBN_WebRTC_C_Serv_OnLocalDescription(int pc, const char *sdp, const return; } - NBN_WebRTC_C_ProcessLocalDescription((NBN_WebRTC_C_Peer *)user_ptr, sdp, "answer"); + NBN_WebRTC_Native_ProcessLocalDescription((NBN_WebRTC_Native_Peer *)user_ptr, sdp, "answer"); } -static void NBN_WebRTC_C_Serv_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { +static void NBN_WebRTC_Native_Serv_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { NBN_LogDebug("Peer %d state changed to %d", pc, state); if (state == RTC_CONNECTED) { - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)user_ptr; + NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)user_ptr; NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, peer->conn); NBN_LogDebug("Peer %d is connected !", pc); } } -static void NBN_WebRTC_C_Serv_OnWsOpen(int ws, void *user_ptr) { +static void NBN_WebRTC_Native_Serv_OnWsOpen(int ws, void *user_ptr) { NBN_LogDebug("WS %d is open", ws); - NBN_WebRTC_C_Peer *peer = - NBN_WebRTC_C_CreatePeer(ws, NBN_WebRTC_C_Serv_OnLocalDescription, NBN_WebRTC_C_Serv_OnPeerStateChanged); + NBN_WebRTC_Native_Peer *peer = NBN_WebRTC_Native_CreatePeer(ws, NBN_WebRTC_Native_Serv_OnLocalDescription, + NBN_WebRTC_Native_Serv_OnPeerStateChanged); if (!peer) { NBN_LogError("Failed to create peer"); @@ -551,14 +362,15 @@ static void NBN_WebRTC_C_Serv_OnWsOpen(int ws, void *user_ptr) { peer->conn = NBN_GameServer_CreateClientConnection(NBN_WEBRTC_C_DRIVER_ID, peer, nbn_wrtc_c_serv.protocol_id, peer->id); - NBN_WebRTC_C_HTable_Add(nbn_wrtc_c_serv.peers, peer->id, peer); + + hmput(nbn_wrtc_c_serv.peers, peer->id, peer); } -static void NBN_WebRTC_C_Serv_OnWsClosed(int ws, void *user_ptr) { +static void NBN_WebRTC_Native_Serv_OnWsClosed(int ws, void *user_ptr) { NBN_LogDebug("WS %d has closed", ws); if (user_ptr) { - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)user_ptr; + NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)user_ptr; NBN_LogDebug("Closing WebRTC peer and channel (peer: %d, channel: %d)", peer->id, peer->channel_id); @@ -567,26 +379,25 @@ static void NBN_WebRTC_C_Serv_OnWsClosed(int ws, void *user_ptr) { } } -static void NBN_WebRTC_C_Serv_OnWsMessage(int ws, const char *msg, int size, void *user_ptr) { - NBN_WebRTC_C_ProcessSignalingMessage((NBN_WebRTC_C_Peer *)user_ptr, ws, msg, size, "offer"); +static void NBN_WebRTC_Native_Serv_OnWsMessage(int ws, const char *msg, int size, void *user_ptr) { + NBN_WebRTC_Native_ProcessSignalingMessage((NBN_WebRTC_Native_Peer *)user_ptr, ws, msg, size, "offer"); } -static void NBN_WebRTC_C_Serv_OnWsConnection(int wsserver, int ws, void *user_ptr) { +static void NBN_WebRTC_Native_Serv_OnWsConnection(int wsserver, int ws, void *user_ptr) { NBN_LogDebug("New WS connection %d (user_ptr: %p)", ws, user_ptr); - rtcSetOpenCallback(ws, NBN_WebRTC_C_Serv_OnWsOpen); - rtcSetClosedCallback(ws, NBN_WebRTC_C_Serv_OnWsClosed); - rtcSetErrorCallback(ws, NBN_WebRTC_C_OnWsError); - rtcSetMessageCallback(ws, NBN_WebRTC_C_Serv_OnWsMessage); + rtcSetOpenCallback(ws, NBN_WebRTC_Native_Serv_OnWsOpen); + rtcSetClosedCallback(ws, NBN_WebRTC_Native_Serv_OnWsClosed); + rtcSetErrorCallback(ws, NBN_WebRTC_Native_OnWsError); + rtcSetMessageCallback(ws, NBN_WebRTC_Native_Serv_OnWsMessage); } -static int NBN_WebRTC_C_ServStart(uint32_t protocol_id, uint16_t port) { +static int NBN_WebRTC_Native_ServStart(uint32_t protocol_id, uint16_t port) { nbn_wrtc_c_serv.ws_port = port; - nbn_wrtc_c_serv.peers = NBN_WebRTC_C_HTable_Create(); nbn_wrtc_c_serv.protocol_id = protocol_id; nbn_wrtc_c_serv.wsserver = -1; - rtcInitLogger(nbn_wrtc_c_cfg.log_level, NBN_WebRTC_C_Log); + rtcInitLogger(nbn_wrtc_c_cfg.log_level, NBN_WebRTC_Native_Log); rtcPreload(); rtcWsServerConfiguration cfg = {.port = port, @@ -595,7 +406,7 @@ static int NBN_WebRTC_C_ServStart(uint32_t protocol_id, uint16_t port) { .keyPemFile = nbn_wrtc_c_cfg.key_path, .keyPemPass = nbn_wrtc_c_cfg.passphrase}; - int wsserver = rtcCreateWebSocketServer(&cfg, NBN_WebRTC_C_Serv_OnWsConnection); + int wsserver = rtcCreateWebSocketServer(&cfg, NBN_WebRTC_Native_Serv_OnWsConnection); if (wsserver < 0) { NBN_LogError("Failed to start WS server (code: %d)", wsserver); @@ -604,11 +415,13 @@ static int NBN_WebRTC_C_ServStart(uint32_t protocol_id, uint16_t port) { nbn_wrtc_c_serv.wsserver = wsserver; + hmdefault(nbn_wrtc_c_serv.peers, NULL); + return 0; } -static void NBN_WebRTC_C_ServStop(void) { - NBN_WebRTC_C_HTable_Destroy(nbn_wrtc_c_serv.peers); +static void NBN_WebRTC_Native_ServStop(void) { + hmfree(nbn_wrtc_c_serv.peers); if (nbn_wrtc_c_serv.wsserver >= 0) { rtcDeleteWebSocketServer(nbn_wrtc_c_serv.wsserver); @@ -617,43 +430,41 @@ static void NBN_WebRTC_C_ServStop(void) { rtcCleanup(); } -static int NBN_WebRTC_C_ServRecvPackets(void) { +static int NBN_WebRTC_Native_ServRecvPackets(void) { + static NBN_Packet packet = {0}; const int buffer_size = sizeof(nbn_wrtc_c_serv.packet_buffer); int size = buffer_size; - for (unsigned int i = 0; i < nbn_wrtc_c_serv.peers->capacity; i++) { - NBN_WebRTC_C_HTableEntry *entry = nbn_wrtc_c_serv.peers->internal_array[i]; - - if (entry) { - while (rtcReceiveMessage(entry->peer->channel_id, nbn_wrtc_c_serv.packet_buffer, &size) == - RTC_ERR_SUCCESS) { - NBN_Packet packet; + for (unsigned int i = 0; i < hmlen(nbn_wrtc_c_serv.peers); i++) { + NBN_WebRTC_Native_Peer *peer = nbn_wrtc_c_serv.peers[i].value; - if (NBN_Packet_InitRead(&packet, entry->peer->conn, (uint8_t *)nbn_wrtc_c_serv.packet_buffer, size) < 0) - continue; + while (rtcReceiveMessage(peer->channel_id, nbn_wrtc_c_serv.packet_buffer, &size) == RTC_ERR_SUCCESS) { + if (NBN_Packet_InitRead(&packet, peer->conn, (uint8_t *)nbn_wrtc_c_serv.packet_buffer, size) < 0) + continue; - packet.sender = entry->peer->conn; - NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet); - size = buffer_size; - } + packet.sender = peer->conn; + NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet); + size = buffer_size; } } return 0; } -static void NBN_WebRTC_C_ServRemoveClientConnection(NBN_Connection *conn) { - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)conn->driver_data; +static void NBN_WebRTC_Native_ServRemoveClientConnection(NBN_Connection *conn) { + NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)conn->driver_data; - NBN_WebRTC_C_HTable_Remove(nbn_wrtc_c_serv.peers, peer->id); - NBN_WebRTC_C_DestroyPeer(peer); + int ret = hmdel(nbn_wrtc_c_serv.peers, peer->id); + + NBN_Assert(ret == 1); + NBN_WebRTC_Native_DestroyPeer(peer); } -static int NBN_WebRTC_C_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *conn) { +static int NBN_WebRTC_Native_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *conn) { if (!conn->is_accepted) return 0; - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)conn->driver_data; + NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)conn->driver_data; if (rtcSendMessage(peer->channel_id, (char *)packet->buffer, packet->size) < 0) { NBN_LogError("rtcSendMessage failed for peer %d", peer->id); @@ -666,16 +477,16 @@ static int NBN_WebRTC_C_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *con #pragma region Game client -typedef struct NBN_WebRTC_C_Client { +typedef struct NBN_WebRTC_Native_Client { uint32_t protocol_id; bool is_connected; - NBN_WebRTC_C_Peer *peer; + NBN_WebRTC_Native_Peer *peer; char packet_buffer[NBN_PACKET_MAX_SIZE]; -} NBN_WebRTC_C_Client; +} NBN_WebRTC_Native_Client; -static NBN_WebRTC_C_Client nbn_wrtc_c_cli = {0, false, false, NULL, {0}}; +static NBN_WebRTC_Native_Client nbn_wrtc_c_cli = {0, false, false, NULL, {0}}; -static void NBN_WebRTC_C_Cli_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { +static void NBN_WebRTC_Native_Cli_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { NBN_LogDebug("Processing local description of type '%s'", type); if (strncmp(type, "offer", strlen("offer")) != 0) { @@ -683,25 +494,25 @@ static void NBN_WebRTC_C_Cli_OnLocalDescription(int pc, const char *sdp, const c return; } - NBN_WebRTC_C_ProcessLocalDescription((NBN_WebRTC_C_Peer *)user_ptr, sdp, "offer"); + NBN_WebRTC_Native_ProcessLocalDescription((NBN_WebRTC_Native_Peer *)user_ptr, sdp, "offer"); } -static void NBN_WebRTC_C_Cli_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { +static void NBN_WebRTC_Native_Cli_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { NBN_LogDebug("Server peer state changed to %d", pc, state); if (state == RTC_CONNECTED) { - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)user_ptr; + NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)user_ptr; NBN_LogDebug("Server peer is connected !", pc); nbn_wrtc_c_cli.is_connected = true; } } -static void NBN_WebRTC_C_Cli_OnWsOpen(int ws, void *user_ptr) { +static void NBN_WebRTC_Native_Cli_OnWsOpen(int ws, void *user_ptr) { NBN_LogDebug("WS %d is open, creating peer...", ws); - NBN_WebRTC_C_Peer *peer = - NBN_WebRTC_C_CreatePeer(ws, NBN_WebRTC_C_Cli_OnLocalDescription, NBN_WebRTC_C_Cli_OnPeerStateChanged); + NBN_WebRTC_Native_Peer *peer = NBN_WebRTC_Native_CreatePeer(ws, NBN_WebRTC_Native_Cli_OnLocalDescription, + NBN_WebRTC_Native_Cli_OnPeerStateChanged); if (!peer) { NBN_LogError("Failed to create peer"); @@ -714,19 +525,19 @@ static void NBN_WebRTC_C_Cli_OnWsOpen(int ws, void *user_ptr) { nbn_wrtc_c_cli.peer = peer; } -static void NBN_WebRTC_C_Cli_OnWsClosed(int ws, void *user_ptr) { +static void NBN_WebRTC_Native_Cli_OnWsClosed(int ws, void *user_ptr) { NBN_LogDebug("WS %d has closed", ws); if (user_ptr) { - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)user_ptr; + NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)user_ptr; NBN_LogDebug("Destroying server peer (peer: %d, channel: %d)", peer->id, peer->channel_id); - NBN_WebRTC_C_DestroyPeer(peer); + NBN_WebRTC_Native_DestroyPeer(peer); } } -static void NBN_WebRTC_C_Cli_OnWsMessage(int ws, const char *msg, int size, void *user_ptr) { - NBN_WebRTC_C_ProcessSignalingMessage((NBN_WebRTC_C_Peer *)user_ptr, ws, msg, size, "answer"); +static void NBN_WebRTC_Native_Cli_OnWsMessage(int ws, const char *msg, int size, void *user_ptr) { + NBN_WebRTC_Native_ProcessSignalingMessage((NBN_WebRTC_Native_Peer *)user_ptr, ws, msg, size, "answer"); } static int AttemptConnection(void) { @@ -762,8 +573,8 @@ static int AttemptConnection(void) { return 0; } -static int NBN_WebRTC_C_CliStart(uint32_t protocol_id, const char *host, uint16_t port) { - rtcInitLogger(nbn_wrtc_c_cfg.log_level, NBN_WebRTC_C_Log); +static int NBN_WebRTC_Native_CliStart(uint32_t protocol_id, const char *host, uint16_t port) { + rtcInitLogger(nbn_wrtc_c_cfg.log_level, NBN_WebRTC_Native_Log); rtcPreload(); char ws_addr[256] = {0}; @@ -781,33 +592,32 @@ static int NBN_WebRTC_C_CliStart(uint32_t protocol_id, const char *host, uint16_ NBN_LogDebug("Successfully created client WS: %d", cli_ws); - rtcSetOpenCallback(cli_ws, NBN_WebRTC_C_Cli_OnWsOpen); - rtcSetClosedCallback(cli_ws, NBN_WebRTC_C_Cli_OnWsClosed); - rtcSetErrorCallback(cli_ws, NBN_WebRTC_C_OnWsError); - rtcSetMessageCallback(cli_ws, NBN_WebRTC_C_Cli_OnWsMessage); + rtcSetOpenCallback(cli_ws, NBN_WebRTC_Native_Cli_OnWsOpen); + rtcSetClosedCallback(cli_ws, NBN_WebRTC_Native_Cli_OnWsClosed); + rtcSetErrorCallback(cli_ws, NBN_WebRTC_Native_OnWsError); + rtcSetMessageCallback(cli_ws, NBN_WebRTC_Native_Cli_OnWsMessage); return AttemptConnection(); } -static void NBN_WebRTC_C_CliStop(void) { - NBN_WebRTC_C_Peer *peer = nbn_wrtc_c_cli.peer; +static void NBN_WebRTC_Native_CliStop(void) { + NBN_WebRTC_Native_Peer *peer = nbn_wrtc_c_cli.peer; if (peer) { - NBN_WebRTC_C_DestroyPeer(peer); + NBN_WebRTC_Native_DestroyPeer(peer); } nbn_wrtc_c_cli.is_connected = false; rtcCleanup(); } -static int NBN_WebRTC_C_CliRecvPackets(void) { +static int NBN_WebRTC_Native_CliRecvPackets(void) { + static NBN_Packet packet = {0}; const int buffer_size = sizeof(nbn_wrtc_c_cli.packet_buffer); int size = buffer_size; - NBN_WebRTC_C_Peer *peer = nbn_wrtc_c_cli.peer; + NBN_WebRTC_Native_Peer *peer = nbn_wrtc_c_cli.peer; while (rtcReceiveMessage(peer->channel_id, nbn_wrtc_c_cli.packet_buffer, &size) == RTC_ERR_SUCCESS) { - NBN_Packet packet; - if (NBN_Packet_InitRead(&packet, peer->conn, (uint8_t *)nbn_wrtc_c_cli.packet_buffer, size) < 0) continue; @@ -819,8 +629,8 @@ static int NBN_WebRTC_C_CliRecvPackets(void) { return 0; } -static int NBN_WebRTC_C_CliSendPacket(NBN_Packet *packet) { - NBN_WebRTC_C_Peer *peer = nbn_wrtc_c_cli.peer; +static int NBN_WebRTC_Native_CliSendPacket(NBN_Packet *packet) { + NBN_WebRTC_Native_Peer *peer = nbn_wrtc_c_cli.peer; if (rtcSendMessage(peer->channel_id, (char *)packet->buffer, packet->size) < 0) { NBN_LogError("rtcSendMessage failed for peer %d", peer->id); @@ -832,7 +642,7 @@ static int NBN_WebRTC_C_CliSendPacket(NBN_Packet *packet) { #pragma endregion /* Game client */ -void NBN_WebRTC_C_Register(NBN_WebRTC_C_Config config) { +void NBN_WebRTC_Native_Register(NBN_WebRTC_Native_Config config) { const char **ice_servers = (const char **)malloc(sizeof(char *) * config.ice_servers_count); for (unsigned int i = 0; i < config.ice_servers_count; i++) { @@ -856,17 +666,18 @@ void NBN_WebRTC_C_Register(NBN_WebRTC_C_Config config) { } NBN_DriverImplementation driver_impl = {// Client implementation - NBN_WebRTC_C_CliStart, NBN_WebRTC_C_CliStop, NBN_WebRTC_C_CliRecvPackets, - NBN_WebRTC_C_CliSendPacket, + NBN_WebRTC_Native_CliStart, NBN_WebRTC_Native_CliStop, + NBN_WebRTC_Native_CliRecvPackets, NBN_WebRTC_Native_CliSendPacket, // Server implementation - NBN_WebRTC_C_ServStart, NBN_WebRTC_C_ServStop, NBN_WebRTC_C_ServRecvPackets, - NBN_WebRTC_C_ServSendPacketTo, NBN_WebRTC_C_ServRemoveClientConnection}; + NBN_WebRTC_Native_ServStart, NBN_WebRTC_Native_ServStop, + NBN_WebRTC_Native_ServRecvPackets, NBN_WebRTC_Native_ServSendPacketTo, + NBN_WebRTC_Native_ServRemoveClientConnection}; NBN_Driver_Register(NBN_WEBRTC_C_DRIVER_ID, NBN_WEBRTC_C_DRIVER_NAME, driver_impl); } -void NBN_WebRTC_C_Unregister(void) { +void NBN_WebRTC_Native_Unregister(void) { for (unsigned int i = 0; i < nbn_wrtc_c_cfg.ice_servers_count; i++) { free((void *)nbn_wrtc_c_cfg.ice_servers[i]); } diff --git a/stb_ds.h b/stb_ds.h new file mode 100644 index 0000000..e84c82d --- /dev/null +++ b/stb_ds.h @@ -0,0 +1,1895 @@ +/* stb_ds.h - v0.67 - public domain data structures - Sean Barrett 2019 + + This is a single-header-file library that provides easy-to-use + dynamic arrays and hash tables for C (also works in C++). + + For a gentle introduction: + http://nothings.org/stb_ds + + To use this library, do this in *one* C or C++ file: + #define STB_DS_IMPLEMENTATION + #include "stb_ds.h" + +TABLE OF CONTENTS + + Table of Contents + Compile-time options + License + Documentation + Notes + Notes - Dynamic arrays + Notes - Hash maps + Credits + +COMPILE-TIME OPTIONS + + #define STBDS_NO_SHORT_NAMES + + This flag needs to be set globally. + + By default stb_ds exposes shorter function names that are not qualified + with the "stbds_" prefix. If these names conflict with the names in your + code, define this flag. + + #define STBDS_SIPHASH_2_4 + + This flag only needs to be set in the file containing #define STB_DS_IMPLEMENTATION. + + By default stb_ds.h hashes using a weaker variant of SipHash and a custom hash for + 4- and 8-byte keys. On 64-bit platforms, you can define the above flag to force + stb_ds.h to use specification-compliant SipHash-2-4 for all keys. Doing so makes + hash table insertion about 20% slower on 4- and 8-byte keys, 5% slower on + 64-byte keys, and 10% slower on 256-byte keys on my test computer. + + #define STBDS_REALLOC(context,ptr,size) better_realloc + #define STBDS_FREE(context,ptr) better_free + + These defines only need to be set in the file containing #define STB_DS_IMPLEMENTATION. + + By default stb_ds uses stdlib realloc() and free() for memory management. You can + substitute your own functions instead by defining these symbols. You must either + define both, or neither. Note that at the moment, 'context' will always be NULL. + @TODO add an array/hash initialization function that takes a memory context pointer. + + #define STBDS_UNIT_TESTS + + Defines a function stbds_unit_tests() that checks the functioning of the data structures. + + Note that on older versions of gcc (e.g. 5.x.x) you may need to build with '-std=c++0x' + (or equivalentally '-std=c++11') when using anonymous structures as seen on the web + page or in STBDS_UNIT_TESTS. + +LICENSE + + Placed in the public domain and also MIT licensed. + See end of file for detailed license information. + +DOCUMENTATION + + Dynamic Arrays + + Non-function interface: + + Declare an empty dynamic array of type T + T* foo = NULL; + + Access the i'th item of a dynamic array 'foo' of type T, T* foo: + foo[i] + + Functions (actually macros) + + arrfree: + void arrfree(T*); + Frees the array. + + arrlen: + ptrdiff_t arrlen(T*); + Returns the number of elements in the array. + + arrlenu: + size_t arrlenu(T*); + Returns the number of elements in the array as an unsigned type. + + arrpop: + T arrpop(T* a) + Removes the final element of the array and returns it. + + arrput: + T arrput(T* a, T b); + Appends the item b to the end of array a. Returns b. + + arrins: + T arrins(T* a, int p, T b); + Inserts the item b into the middle of array a, into a[p], + moving the rest of the array over. Returns b. + + arrinsn: + void arrinsn(T* a, int p, int n); + Inserts n uninitialized items into array a starting at a[p], + moving the rest of the array over. + + arraddnptr: + T* arraddnptr(T* a, int n) + Appends n uninitialized items onto array at the end. + Returns a pointer to the first uninitialized item added. + + arraddnindex: + size_t arraddnindex(T* a, int n) + Appends n uninitialized items onto array at the end. + Returns the index of the first uninitialized item added. + + arrdel: + void arrdel(T* a, int p); + Deletes the element at a[p], moving the rest of the array over. + + arrdeln: + void arrdeln(T* a, int p, int n); + Deletes n elements starting at a[p], moving the rest of the array over. + + arrdelswap: + void arrdelswap(T* a, int p); + Deletes the element at a[p], replacing it with the element from + the end of the array. O(1) performance. + + arrsetlen: + void arrsetlen(T* a, int n); + Changes the length of the array to n. Allocates uninitialized + slots at the end if necessary. + + arrsetcap: + size_t arrsetcap(T* a, int n); + Sets the length of allocated storage to at least n. It will not + change the length of the array. + + arrcap: + size_t arrcap(T* a); + Returns the number of total elements the array can contain without + needing to be reallocated. + + Hash maps & String hash maps + + Given T is a structure type: struct { TK key; TV value; }. Note that some + functions do not require TV value and can have other fields. For string + hash maps, TK must be 'char *'. + + Special interface: + + stbds_rand_seed: + void stbds_rand_seed(size_t seed); + For security against adversarially chosen data, you should seed the + library with a strong random number. Or at least seed it with time(). + + stbds_hash_string: + size_t stbds_hash_string(char *str, size_t seed); + Returns a hash value for a string. + + stbds_hash_bytes: + size_t stbds_hash_bytes(void *p, size_t len, size_t seed); + These functions hash an arbitrary number of bytes. The function + uses a custom hash for 4- and 8-byte data, and a weakened version + of SipHash for everything else. On 64-bit platforms you can get + specification-compliant SipHash-2-4 on all data by defining + STBDS_SIPHASH_2_4, at a significant cost in speed. + + Non-function interface: + + Declare an empty hash map of type T + T* foo = NULL; + + Access the i'th entry in a hash table T* foo: + foo[i] + + Function interface (actually macros): + + hmfree + shfree + void hmfree(T*); + void shfree(T*); + Frees the hashmap and sets the pointer to NULL. + + hmlen + shlen + ptrdiff_t hmlen(T*) + ptrdiff_t shlen(T*) + Returns the number of elements in the hashmap. + + hmlenu + shlenu + size_t hmlenu(T*) + size_t shlenu(T*) + Returns the number of elements in the hashmap. + + hmgeti + shgeti + hmgeti_ts + ptrdiff_t hmgeti(T*, TK key) + ptrdiff_t shgeti(T*, char* key) + ptrdiff_t hmgeti_ts(T*, TK key, ptrdiff_t tempvar) + Returns the index in the hashmap which has the key 'key', or -1 + if the key is not present. + + hmget + hmget_ts + shget + TV hmget(T*, TK key) + TV shget(T*, char* key) + TV hmget_ts(T*, TK key, ptrdiff_t tempvar) + Returns the value corresponding to 'key' in the hashmap. + The structure must have a 'value' field + + hmgets + shgets + T hmgets(T*, TK key) + T shgets(T*, char* key) + Returns the structure corresponding to 'key' in the hashmap. + + hmgetp + shgetp + hmgetp_ts + hmgetp_null + shgetp_null + T* hmgetp(T*, TK key) + T* shgetp(T*, char* key) + T* hmgetp_ts(T*, TK key, ptrdiff_t tempvar) + T* hmgetp_null(T*, TK key) + T* shgetp_null(T*, char *key) + Returns a pointer to the structure corresponding to 'key' in + the hashmap. Functions ending in "_null" return NULL if the key + is not present in the hashmap; the others return a pointer to a + structure holding the default value (but not the searched-for key). + + hmdefault + shdefault + TV hmdefault(T*, TV value) + TV shdefault(T*, TV value) + Sets the default value for the hashmap, the value which will be + returned by hmget/shget if the key is not present. + + hmdefaults + shdefaults + TV hmdefaults(T*, T item) + TV shdefaults(T*, T item) + Sets the default struct for the hashmap, the contents which will be + returned by hmgets/shgets if the key is not present. + + hmput + shput + TV hmput(T*, TK key, TV value) + TV shput(T*, char* key, TV value) + Inserts a pair into the hashmap. If the key is already + present in the hashmap, updates its value. + + hmputs + shputs + T hmputs(T*, T item) + T shputs(T*, T item) + Inserts a struct with T.key into the hashmap. If the struct is already + present in the hashmap, updates it. + + hmdel + shdel + int hmdel(T*, TK key) + int shdel(T*, char* key) + If 'key' is in the hashmap, deletes its entry and returns 1. + Otherwise returns 0. + + Function interface (actually macros) for strings only: + + sh_new_strdup + void sh_new_strdup(T*); + Overwrites the existing pointer with a newly allocated + string hashmap which will automatically allocate and free + each string key using realloc/free + + sh_new_arena + void sh_new_arena(T*); + Overwrites the existing pointer with a newly allocated + string hashmap which will automatically allocate each string + key to a string arena. Every string key ever used by this + hash table remains in the arena until the arena is freed. + Additionally, any key which is deleted and reinserted will + be allocated multiple times in the string arena. + +NOTES + + * These data structures are realloc'd when they grow, and the macro + "functions" write to the provided pointer. This means: (a) the pointer + must be an lvalue, and (b) the pointer to the data structure is not + stable, and you must maintain it the same as you would a realloc'd + pointer. For example, if you pass a pointer to a dynamic array to a + function which updates it, the function must return back the new + pointer to the caller. This is the price of trying to do this in C. + + * The following are the only functions that are thread-safe on a single data + structure, i.e. can be run in multiple threads simultaneously on the same + data structure + hmlen shlen + hmlenu shlenu + hmget_ts shget_ts + hmgeti_ts shgeti_ts + hmgets_ts shgets_ts + + * You iterate over the contents of a dynamic array and a hashmap in exactly + the same way, using arrlen/hmlen/shlen: + + for (i=0; i < arrlen(foo); ++i) + ... foo[i] ... + + * All operations except arrins/arrdel are O(1) amortized, but individual + operations can be slow, so these data structures may not be suitable + for real time use. Dynamic arrays double in capacity as needed, so + elements are copied an average of once. Hash tables double/halve + their size as needed, with appropriate hysteresis to maintain O(1) + performance. + +NOTES - DYNAMIC ARRAY + + * If you know how long a dynamic array is going to be in advance, you can avoid + extra memory allocations by using arrsetlen to allocate it to that length in + advance and use foo[n] while filling it out, or arrsetcap to allocate the memory + for that length and use arrput/arrpush as normal. + + * Unlike some other versions of the dynamic array, this version should + be safe to use with strict-aliasing optimizations. + +NOTES - HASH MAP + + * For compilers other than GCC and clang (e.g. Visual Studio), for hmput/hmget/hmdel + and variants, the key must be an lvalue (so the macro can take the address of it). + Extensions are used that eliminate this requirement if you're using C99 and later + in GCC or clang, or if you're using C++ in GCC. But note that this can make your + code less portable. + + * To test for presence of a key in a hashmap, just do 'hmgeti(foo,key) >= 0'. + + * The iteration order of your data in the hashmap is determined solely by the + order of insertions and deletions. In particular, if you never delete, new + keys are always added at the end of the array. This will be consistent + across all platforms and versions of the library. However, you should not + attempt to serialize the internal hash table, as the hash is not consistent + between different platforms, and may change with future versions of the library. + + * Use sh_new_arena() for string hashmaps that you never delete from. Initialize + with NULL if you're managing the memory for your strings, or your strings are + never freed (at least until the hashmap is freed). Otherwise, use sh_new_strdup(). + @TODO: make an arena variant that garbage collects the strings with a trivial + copy collector into a new arena whenever the table shrinks / rebuilds. Since + current arena recommendation is to only use arena if it never deletes, then + this can just replace current arena implementation. + + * If adversarial input is a serious concern and you're on a 64-bit platform, + enable STBDS_SIPHASH_2_4 (see the 'Compile-time options' section), and pass + a strong random number to stbds_rand_seed. + + * The default value for the hash table is stored in foo[-1], so if you + use code like 'hmget(T,k)->value = 5' you can accidentally overwrite + the value stored by hmdefault if 'k' is not present. + +CREDITS + + Sean Barrett -- library, idea for dynamic array API/implementation + Per Vognsen -- idea for hash table API/implementation + Rafael Sachetto -- arrpop() + github:HeroicKatora -- arraddn() reworking + + Bugfixes: + Andy Durdin + Shane Liesegang + Vinh Truong + Andreas Molzer + github:hashitaku + github:srdjanstipic + Macoy Madson + Andreas Vennstrom + Tobias Mansfield-Williams +*/ + +#ifdef STBDS_UNIT_TESTS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#ifndef INCLUDE_STB_DS_H +#define INCLUDE_STB_DS_H + +#include +#include + +#ifndef STBDS_NO_SHORT_NAMES +#define arrlen stbds_arrlen +#define arrlenu stbds_arrlenu +#define arrput stbds_arrput +#define arrpush stbds_arrput +#define arrpop stbds_arrpop +#define arrfree stbds_arrfree +#define arraddn stbds_arraddn // deprecated, use one of the following instead: +#define arraddnptr stbds_arraddnptr +#define arraddnindex stbds_arraddnindex +#define arrsetlen stbds_arrsetlen +#define arrlast stbds_arrlast +#define arrins stbds_arrins +#define arrinsn stbds_arrinsn +#define arrdel stbds_arrdel +#define arrdeln stbds_arrdeln +#define arrdelswap stbds_arrdelswap +#define arrcap stbds_arrcap +#define arrsetcap stbds_arrsetcap + +#define hmput stbds_hmput +#define hmputs stbds_hmputs +#define hmget stbds_hmget +#define hmget_ts stbds_hmget_ts +#define hmgets stbds_hmgets +#define hmgetp stbds_hmgetp +#define hmgetp_ts stbds_hmgetp_ts +#define hmgetp_null stbds_hmgetp_null +#define hmgeti stbds_hmgeti +#define hmgeti_ts stbds_hmgeti_ts +#define hmdel stbds_hmdel +#define hmlen stbds_hmlen +#define hmlenu stbds_hmlenu +#define hmfree stbds_hmfree +#define hmdefault stbds_hmdefault +#define hmdefaults stbds_hmdefaults + +#define shput stbds_shput +#define shputi stbds_shputi +#define shputs stbds_shputs +#define shget stbds_shget +#define shgeti stbds_shgeti +#define shgets stbds_shgets +#define shgetp stbds_shgetp +#define shgetp_null stbds_shgetp_null +#define shdel stbds_shdel +#define shlen stbds_shlen +#define shlenu stbds_shlenu +#define shfree stbds_shfree +#define shdefault stbds_shdefault +#define shdefaults stbds_shdefaults +#define sh_new_arena stbds_sh_new_arena +#define sh_new_strdup stbds_sh_new_strdup + +#define stralloc stbds_stralloc +#define strreset stbds_strreset +#endif + +#if defined(STBDS_REALLOC) && !defined(STBDS_FREE) || !defined(STBDS_REALLOC) && defined(STBDS_FREE) +#error "You must define both STBDS_REALLOC and STBDS_FREE, or neither." +#endif +#if !defined(STBDS_REALLOC) && !defined(STBDS_FREE) +#include +#define STBDS_REALLOC(c,p,s) realloc(p,s) +#define STBDS_FREE(c,p) free(p) +#endif + +#ifdef _MSC_VER +#define STBDS_NOTUSED(v) (void)(v) +#else +#define STBDS_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// for security against attackers, seed the library with a random number, at least time() but stronger is better +extern void stbds_rand_seed(size_t seed); + +// these are the hash functions used internally if you want to test them or use them for other purposes +extern size_t stbds_hash_bytes(void *p, size_t len, size_t seed); +extern size_t stbds_hash_string(char *str, size_t seed); + +// this is a simple string arena allocator, initialize with e.g. 'stbds_string_arena my_arena={0}'. +typedef struct stbds_string_arena stbds_string_arena; +extern char * stbds_stralloc(stbds_string_arena *a, char *str); +extern void stbds_strreset(stbds_string_arena *a); + +// have to #define STBDS_UNIT_TESTS to call this +extern void stbds_unit_tests(void); + +/////////////// +// +// Everything below here is implementation details +// + +extern void * stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap); +extern void stbds_arrfreef(void *a); +extern void stbds_hmfree_func(void *p, size_t elemsize); +extern void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode); +extern void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode); +extern void * stbds_hmput_default(void *a, size_t elemsize); +extern void * stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode); +extern void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode); +extern void * stbds_shmode_func(size_t elemsize, int mode); + +#ifdef __cplusplus +} +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define STBDS_HAS_TYPEOF +#ifdef __cplusplus +//#define STBDS_HAS_LITERAL_ARRAY // this is currently broken for clang +#endif +#endif + +#if !defined(__cplusplus) +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define STBDS_HAS_LITERAL_ARRAY +#endif +#endif + +// this macro takes the address of the argument, but on gcc/clang can accept rvalues +#if defined(STBDS_HAS_LITERAL_ARRAY) && defined(STBDS_HAS_TYPEOF) + #if __clang__ + #define STBDS_ADDRESSOF(typevar, value) ((__typeof__(typevar)[1]){value}) // literal array decays to pointer to value + #else + #define STBDS_ADDRESSOF(typevar, value) ((typeof(typevar)[1]){value}) // literal array decays to pointer to value + #endif +#else +#define STBDS_ADDRESSOF(typevar, value) &(value) +#endif + +#define STBDS_OFFSETOF(var,field) ((char *) &(var)->field - (char *) (var)) + +#define stbds_header(t) ((stbds_array_header *) (t) - 1) +#define stbds_temp(t) stbds_header(t)->temp +#define stbds_temp_key(t) (*(char **) stbds_header(t)->hash_table) + +#define stbds_arrsetcap(a,n) (stbds_arrgrow(a,0,n)) +#define stbds_arrsetlen(a,n) ((stbds_arrcap(a) < (size_t) (n) ? stbds_arrsetcap((a),(size_t)(n)),0 : 0), (a) ? stbds_header(a)->length = (size_t) (n) : 0) +#define stbds_arrcap(a) ((a) ? stbds_header(a)->capacity : 0) +#define stbds_arrlen(a) ((a) ? (ptrdiff_t) stbds_header(a)->length : 0) +#define stbds_arrlenu(a) ((a) ? stbds_header(a)->length : 0) +#define stbds_arrput(a,v) (stbds_arrmaybegrow(a,1), (a)[stbds_header(a)->length++] = (v)) +#define stbds_arrpush stbds_arrput // synonym +#define stbds_arrpop(a) (stbds_header(a)->length--, (a)[stbds_header(a)->length]) +#define stbds_arraddn(a,n) ((void)(stbds_arraddnindex(a, n))) // deprecated, use one of the following instead: +#define stbds_arraddnptr(a,n) (stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), &(a)[stbds_header(a)->length-(n)]) : (a)) +#define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length-(n)) : stbds_arrlen(a)) +#define stbds_arraddnoff stbds_arraddnindex +#define stbds_arrlast(a) ((a)[stbds_header(a)->length-1]) +#define stbds_arrfree(a) ((void) ((a) ? STBDS_FREE(NULL,stbds_header(a)) : (void)0), (a)=NULL) +#define stbds_arrdel(a,i) stbds_arrdeln(a,i,1) +#define stbds_arrdeln(a,i,n) (memmove(&(a)[i], &(a)[(i)+(n)], sizeof *(a) * (stbds_header(a)->length-(n)-(i))), stbds_header(a)->length -= (n)) +#define stbds_arrdelswap(a,i) ((a)[i] = stbds_arrlast(a), stbds_header(a)->length -= 1) +#define stbds_arrinsn(a,i,n) (stbds_arraddn((a),(n)), memmove(&(a)[(i)+(n)], &(a)[i], sizeof *(a) * (stbds_header(a)->length-(n)-(i)))) +#define stbds_arrins(a,i,v) (stbds_arrinsn((a),(i),1), (a)[i]=(v)) + +#define stbds_arrmaybegrow(a,n) ((!(a) || stbds_header(a)->length + (n) > stbds_header(a)->capacity) \ + ? (stbds_arrgrow(a,n,0),0) : 0) + +#define stbds_arrgrow(a,b,c) ((a) = stbds_arrgrowf_wrapper((a), sizeof *(a), (b), (c))) + +#define stbds_hmput(t, k, v) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, 0), \ + (t)[stbds_temp((t)-1)].key = (k), \ + (t)[stbds_temp((t)-1)].value = (v)) + +#define stbds_hmputs(t, s) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), &(s).key, sizeof (s).key, STBDS_HM_BINARY), \ + (t)[stbds_temp((t)-1)] = (s)) + +#define stbds_hmgeti(t,k) \ + ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_HM_BINARY), \ + stbds_temp((t)-1)) + +#define stbds_hmgeti_ts(t,k,temp) \ + ((t) = stbds_hmget_key_ts_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, &(temp), STBDS_HM_BINARY), \ + (temp)) + +#define stbds_hmgetp(t, k) \ + ((void) stbds_hmgeti(t,k), &(t)[stbds_temp((t)-1)]) + +#define stbds_hmgetp_ts(t, k, temp) \ + ((void) stbds_hmgeti_ts(t,k,temp), &(t)[temp]) + +#define stbds_hmdel(t,k) \ + (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_BINARY)),(t)?stbds_temp((t)-1):0) + +#define stbds_hmdefault(t, v) \ + ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1].value = (v)) + +#define stbds_hmdefaults(t, s) \ + ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1] = (s)) + +#define stbds_hmfree(p) \ + ((void) ((p) != NULL ? stbds_hmfree_func((p)-1,sizeof*(p)),0 : 0),(p)=NULL) + +#define stbds_hmgets(t, k) (*stbds_hmgetp(t,k)) +#define stbds_hmget(t, k) (stbds_hmgetp(t,k)->value) +#define stbds_hmget_ts(t, k, temp) (stbds_hmgetp_ts(t,k,temp)->value) +#define stbds_hmlen(t) ((t) ? (ptrdiff_t) stbds_header((t)-1)->length-1 : 0) +#define stbds_hmlenu(t) ((t) ? stbds_header((t)-1)->length-1 : 0) +#define stbds_hmgetp_null(t,k) (stbds_hmgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)]) + +#define stbds_shput(t, k, v) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \ + (t)[stbds_temp((t)-1)].value = (v)) + +#define stbds_shputi(t, k, v) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \ + (t)[stbds_temp((t)-1)].value = (v), stbds_temp((t)-1)) + +#define stbds_shputs(t, s) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (s).key, sizeof (s).key, STBDS_HM_STRING), \ + (t)[stbds_temp((t)-1)] = (s), \ + (t)[stbds_temp((t)-1)].key = stbds_temp_key((t)-1)) // above line overwrites whole structure, so must rewrite key here if it was allocated internally + +#define stbds_pshput(t, p) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (p)->key, sizeof (p)->key, STBDS_HM_PTR_TO_STRING), \ + (t)[stbds_temp((t)-1)] = (p)) + +#define stbds_shgeti(t,k) \ + ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \ + stbds_temp((t)-1)) + +#define stbds_pshgeti(t,k) \ + ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_HM_PTR_TO_STRING), \ + stbds_temp((t)-1)) + +#define stbds_shgetp(t, k) \ + ((void) stbds_shgeti(t,k), &(t)[stbds_temp((t)-1)]) + +#define stbds_pshget(t, k) \ + ((void) stbds_pshgeti(t,k), (t)[stbds_temp((t)-1)]) + +#define stbds_shdel(t,k) \ + (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_STRING)),(t)?stbds_temp((t)-1):0) +#define stbds_pshdel(t,k) \ + (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_OFFSETOF(*(t),key), STBDS_HM_PTR_TO_STRING)),(t)?stbds_temp((t)-1):0) + +#define stbds_sh_new_arena(t) \ + ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_ARENA)) +#define stbds_sh_new_strdup(t) \ + ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_STRDUP)) + +#define stbds_shdefault(t, v) stbds_hmdefault(t,v) +#define stbds_shdefaults(t, s) stbds_hmdefaults(t,s) + +#define stbds_shfree stbds_hmfree +#define stbds_shlenu stbds_hmlenu + +#define stbds_shgets(t, k) (*stbds_shgetp(t,k)) +#define stbds_shget(t, k) (stbds_shgetp(t,k)->value) +#define stbds_shgetp_null(t,k) (stbds_shgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)]) +#define stbds_shlen stbds_hmlen + +typedef struct +{ + size_t length; + size_t capacity; + void * hash_table; + ptrdiff_t temp; +} stbds_array_header; + +typedef struct stbds_string_block +{ + struct stbds_string_block *next; + char storage[8]; +} stbds_string_block; + +struct stbds_string_arena +{ + stbds_string_block *storage; + size_t remaining; + unsigned char block; + unsigned char mode; // this isn't used by the string arena itself +}; + +#define STBDS_HM_BINARY 0 +#define STBDS_HM_STRING 1 + +enum +{ + STBDS_SH_NONE, + STBDS_SH_DEFAULT, + STBDS_SH_STRDUP, + STBDS_SH_ARENA +}; + +#ifdef __cplusplus +// in C we use implicit assignment from these void*-returning functions to T*. +// in C++ these templates make the same code work +template static T * stbds_arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, size_t min_cap) { + return (T*)stbds_arrgrowf((void *)a, elemsize, addlen, min_cap); +} +template static T * stbds_hmget_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) { + return (T*)stbds_hmget_key((void*)a, elemsize, key, keysize, mode); +} +template static T * stbds_hmget_key_ts_wrapper(T *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode) { + return (T*)stbds_hmget_key_ts((void*)a, elemsize, key, keysize, temp, mode); +} +template static T * stbds_hmput_default_wrapper(T *a, size_t elemsize) { + return (T*)stbds_hmput_default((void *)a, elemsize); +} +template static T * stbds_hmput_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) { + return (T*)stbds_hmput_key((void*)a, elemsize, key, keysize, mode); +} +template static T * stbds_hmdel_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode){ + return (T*)stbds_hmdel_key((void*)a, elemsize, key, keysize, keyoffset, mode); +} +template static T * stbds_shmode_func_wrapper(T *, size_t elemsize, int mode) { + return (T*)stbds_shmode_func(elemsize, mode); +} +#else +#define stbds_arrgrowf_wrapper stbds_arrgrowf +#define stbds_hmget_key_wrapper stbds_hmget_key +#define stbds_hmget_key_ts_wrapper stbds_hmget_key_ts +#define stbds_hmput_default_wrapper stbds_hmput_default +#define stbds_hmput_key_wrapper stbds_hmput_key +#define stbds_hmdel_key_wrapper stbds_hmdel_key +#define stbds_shmode_func_wrapper(t,e,m) stbds_shmode_func(e,m) +#endif + +#endif // INCLUDE_STB_DS_H + + +////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION +// + +#ifdef STB_DS_IMPLEMENTATION +#include +#include + +#ifndef STBDS_ASSERT +#define STBDS_ASSERT_WAS_UNDEFINED +#define STBDS_ASSERT(x) ((void) 0) +#endif + +#ifdef STBDS_STATISTICS +#define STBDS_STATS(x) x +size_t stbds_array_grow; +size_t stbds_hash_grow; +size_t stbds_hash_shrink; +size_t stbds_hash_rebuild; +size_t stbds_hash_probes; +size_t stbds_hash_alloc; +size_t stbds_rehash_probes; +size_t stbds_rehash_items; +#else +#define STBDS_STATS(x) +#endif + +// +// stbds_arr implementation +// + +//int *prev_allocs[65536]; +//int num_prev; + +void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap) +{ + stbds_array_header temp={0}; // force debugging + void *b; + size_t min_len = stbds_arrlen(a) + addlen; + (void) sizeof(temp); + + // compute the minimum capacity needed + if (min_len > min_cap) + min_cap = min_len; + + if (min_cap <= stbds_arrcap(a)) + return a; + + // increase needed capacity to guarantee O(1) amortized + if (min_cap < 2 * stbds_arrcap(a)) + min_cap = 2 * stbds_arrcap(a); + else if (min_cap < 4) + min_cap = 4; + + //if (num_prev < 65536) if (a) prev_allocs[num_prev++] = (int *) ((char *) a+1); + //if (num_prev == 2201) + // num_prev = num_prev; + b = STBDS_REALLOC(NULL, (a) ? stbds_header(a) : 0, elemsize * min_cap + sizeof(stbds_array_header)); + //if (num_prev < 65536) prev_allocs[num_prev++] = (int *) (char *) b; + b = (char *) b + sizeof(stbds_array_header); + if (a == NULL) { + stbds_header(b)->length = 0; + stbds_header(b)->hash_table = 0; + stbds_header(b)->temp = 0; + } else { + STBDS_STATS(++stbds_array_grow); + } + stbds_header(b)->capacity = min_cap; + + return b; +} + +void stbds_arrfreef(void *a) +{ + STBDS_FREE(NULL, stbds_header(a)); +} + +// +// stbds_hm hash table implementation +// + +#ifdef STBDS_INTERNAL_SMALL_BUCKET +#define STBDS_BUCKET_LENGTH 4 +#else +#define STBDS_BUCKET_LENGTH 8 +#endif + +#define STBDS_BUCKET_SHIFT (STBDS_BUCKET_LENGTH == 8 ? 3 : 2) +#define STBDS_BUCKET_MASK (STBDS_BUCKET_LENGTH-1) +#define STBDS_CACHE_LINE_SIZE 64 + +#define STBDS_ALIGN_FWD(n,a) (((n) + (a) - 1) & ~((a)-1)) + +typedef struct +{ + size_t hash [STBDS_BUCKET_LENGTH]; + ptrdiff_t index[STBDS_BUCKET_LENGTH]; +} stbds_hash_bucket; // in 32-bit, this is one 64-byte cache line; in 64-bit, each array is one 64-byte cache line + +typedef struct +{ + char * temp_key; // this MUST be the first field of the hash table + size_t slot_count; + size_t used_count; + size_t used_count_threshold; + size_t used_count_shrink_threshold; + size_t tombstone_count; + size_t tombstone_count_threshold; + size_t seed; + size_t slot_count_log2; + stbds_string_arena string; + stbds_hash_bucket *storage; // not a separate allocation, just 64-byte aligned storage after this struct +} stbds_hash_index; + +#define STBDS_INDEX_EMPTY -1 +#define STBDS_INDEX_DELETED -2 +#define STBDS_INDEX_IN_USE(x) ((x) >= 0) + +#define STBDS_HASH_EMPTY 0 +#define STBDS_HASH_DELETED 1 + +static size_t stbds_hash_seed=0x31415926; + +void stbds_rand_seed(size_t seed) +{ + stbds_hash_seed = seed; +} + +#define stbds_load_32_or_64(var, temp, v32, v64_hi, v64_lo) \ + temp = v64_lo ^ v32, temp <<= 16, temp <<= 16, temp >>= 16, temp >>= 16, /* discard if 32-bit */ \ + var = v64_hi, var <<= 16, var <<= 16, /* discard if 32-bit */ \ + var ^= temp ^ v32 + +#define STBDS_SIZE_T_BITS ((sizeof (size_t)) * 8) + +static size_t stbds_probe_position(size_t hash, size_t slot_count, size_t slot_log2) +{ + size_t pos; + STBDS_NOTUSED(slot_log2); + pos = hash & (slot_count-1); + #ifdef STBDS_INTERNAL_BUCKET_START + pos &= ~STBDS_BUCKET_MASK; + #endif + return pos; +} + +static size_t stbds_log2(size_t slot_count) +{ + size_t n=0; + while (slot_count > 1) { + slot_count >>= 1; + ++n; + } + return n; +} + +static stbds_hash_index *stbds_make_hash_index(size_t slot_count, stbds_hash_index *ot) +{ + stbds_hash_index *t; + t = (stbds_hash_index *) STBDS_REALLOC(NULL,0,(slot_count >> STBDS_BUCKET_SHIFT) * sizeof(stbds_hash_bucket) + sizeof(stbds_hash_index) + STBDS_CACHE_LINE_SIZE-1); + t->storage = (stbds_hash_bucket *) STBDS_ALIGN_FWD((size_t) (t+1), STBDS_CACHE_LINE_SIZE); + t->slot_count = slot_count; + t->slot_count_log2 = stbds_log2(slot_count); + t->tombstone_count = 0; + t->used_count = 0; + + #if 0 // A1 + t->used_count_threshold = slot_count*12/16; // if 12/16th of table is occupied, grow + t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild + t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink + #elif 1 // A2 + //t->used_count_threshold = slot_count*12/16; // if 12/16th of table is occupied, grow + //t->tombstone_count_threshold = slot_count* 3/16; // if tombstones are 3/16th of table, rebuild + //t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink + + // compute without overflowing + t->used_count_threshold = slot_count - (slot_count>>2); + t->tombstone_count_threshold = (slot_count>>3) + (slot_count>>4); + t->used_count_shrink_threshold = slot_count >> 2; + + #elif 0 // B1 + t->used_count_threshold = slot_count*13/16; // if 13/16th of table is occupied, grow + t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild + t->used_count_shrink_threshold = slot_count* 5/16; // if table is only 5/16th full, shrink + #else // C1 + t->used_count_threshold = slot_count*14/16; // if 14/16th of table is occupied, grow + t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild + t->used_count_shrink_threshold = slot_count* 6/16; // if table is only 6/16th full, shrink + #endif + // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2 + // Note that the larger tables have high variance as they were run fewer times + // A1 A2 B1 C1 + // 0.10ms : 0.10ms : 0.10ms : 0.11ms : 2,000 inserts creating 2K table + // 0.96ms : 0.95ms : 0.97ms : 1.04ms : 20,000 inserts creating 20K table + // 14.48ms : 14.46ms : 10.63ms : 11.00ms : 200,000 inserts creating 200K table + // 195.74ms : 196.35ms : 203.69ms : 214.92ms : 2,000,000 inserts creating 2M table + // 2193.88ms : 2209.22ms : 2285.54ms : 2437.17ms : 20,000,000 inserts creating 20M table + // 65.27ms : 53.77ms : 65.33ms : 65.47ms : 500,000 inserts & deletes in 2K table + // 72.78ms : 62.45ms : 71.95ms : 72.85ms : 500,000 inserts & deletes in 20K table + // 89.47ms : 77.72ms : 96.49ms : 96.75ms : 500,000 inserts & deletes in 200K table + // 97.58ms : 98.14ms : 97.18ms : 97.53ms : 500,000 inserts & deletes in 2M table + // 118.61ms : 119.62ms : 120.16ms : 118.86ms : 500,000 inserts & deletes in 20M table + // 192.11ms : 194.39ms : 196.38ms : 195.73ms : 500,000 inserts & deletes in 200M table + + if (slot_count <= STBDS_BUCKET_LENGTH) + t->used_count_shrink_threshold = 0; + // to avoid infinite loop, we need to guarantee that at least one slot is empty and will terminate probes + STBDS_ASSERT(t->used_count_threshold + t->tombstone_count_threshold < t->slot_count); + STBDS_STATS(++stbds_hash_alloc); + if (ot) { + t->string = ot->string; + // reuse old seed so we can reuse old hashes so below "copy out old data" doesn't do any hashing + t->seed = ot->seed; + } else { + size_t a,b,temp; + memset(&t->string, 0, sizeof(t->string)); + t->seed = stbds_hash_seed; + // LCG + // in 32-bit, a = 2147001325 b = 715136305 + // in 64-bit, a = 2862933555777941757 b = 3037000493 + stbds_load_32_or_64(a,temp, 2147001325, 0x27bb2ee6, 0x87b0b0fd); + stbds_load_32_or_64(b,temp, 715136305, 0, 0xb504f32d); + stbds_hash_seed = stbds_hash_seed * a + b; + } + + { + size_t i,j; + for (i=0; i < slot_count >> STBDS_BUCKET_SHIFT; ++i) { + stbds_hash_bucket *b = &t->storage[i]; + for (j=0; j < STBDS_BUCKET_LENGTH; ++j) + b->hash[j] = STBDS_HASH_EMPTY; + for (j=0; j < STBDS_BUCKET_LENGTH; ++j) + b->index[j] = STBDS_INDEX_EMPTY; + } + } + + // copy out the old data, if any + if (ot) { + size_t i,j; + t->used_count = ot->used_count; + for (i=0; i < ot->slot_count >> STBDS_BUCKET_SHIFT; ++i) { + stbds_hash_bucket *ob = &ot->storage[i]; + for (j=0; j < STBDS_BUCKET_LENGTH; ++j) { + if (STBDS_INDEX_IN_USE(ob->index[j])) { + size_t hash = ob->hash[j]; + size_t pos = stbds_probe_position(hash, t->slot_count, t->slot_count_log2); + size_t step = STBDS_BUCKET_LENGTH; + STBDS_STATS(++stbds_rehash_items); + for (;;) { + size_t limit,z; + stbds_hash_bucket *bucket; + bucket = &t->storage[pos >> STBDS_BUCKET_SHIFT]; + STBDS_STATS(++stbds_rehash_probes); + + for (z=pos & STBDS_BUCKET_MASK; z < STBDS_BUCKET_LENGTH; ++z) { + if (bucket->hash[z] == 0) { + bucket->hash[z] = hash; + bucket->index[z] = ob->index[j]; + goto done; + } + } + + limit = pos & STBDS_BUCKET_MASK; + for (z = 0; z < limit; ++z) { + if (bucket->hash[z] == 0) { + bucket->hash[z] = hash; + bucket->index[z] = ob->index[j]; + goto done; + } + } + + pos += step; // quadratic probing + step += STBDS_BUCKET_LENGTH; + pos &= (t->slot_count-1); + } + } + done: + ; + } + } + } + + return t; +} + +#define STBDS_ROTATE_LEFT(val, n) (((val) << (n)) | ((val) >> (STBDS_SIZE_T_BITS - (n)))) +#define STBDS_ROTATE_RIGHT(val, n) (((val) >> (n)) | ((val) << (STBDS_SIZE_T_BITS - (n)))) + +size_t stbds_hash_string(char *str, size_t seed) +{ + size_t hash = seed; + while (*str) + hash = STBDS_ROTATE_LEFT(hash, 9) + (unsigned char) *str++; + + // Thomas Wang 64-to-32 bit mix function, hopefully also works in 32 bits + hash ^= seed; + hash = (~hash) + (hash << 18); + hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,31); + hash = hash * 21; + hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,11); + hash += (hash << 6); + hash ^= STBDS_ROTATE_RIGHT(hash,22); + return hash+seed; +} + +#ifdef STBDS_SIPHASH_2_4 +#define STBDS_SIPHASH_C_ROUNDS 2 +#define STBDS_SIPHASH_D_ROUNDS 4 +typedef int STBDS_SIPHASH_2_4_can_only_be_used_in_64_bit_builds[sizeof(size_t) == 8 ? 1 : -1]; +#endif + +#ifndef STBDS_SIPHASH_C_ROUNDS +#define STBDS_SIPHASH_C_ROUNDS 1 +#endif +#ifndef STBDS_SIPHASH_D_ROUNDS +#define STBDS_SIPHASH_D_ROUNDS 1 +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4127) // conditional expression is constant, for do..while(0) and sizeof()== +#endif + +static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed) +{ + unsigned char *d = (unsigned char *) p; + size_t i,j; + size_t v0,v1,v2,v3, data; + + // hash that works on 32- or 64-bit registers without knowing which we have + // (computes different results on 32-bit and 64-bit platform) + // derived from siphash, but on 32-bit platforms very different as it uses 4 32-bit state not 4 64-bit + v0 = ((((size_t) 0x736f6d65 << 16) << 16) + 0x70736575) ^ seed; + v1 = ((((size_t) 0x646f7261 << 16) << 16) + 0x6e646f6d) ^ ~seed; + v2 = ((((size_t) 0x6c796765 << 16) << 16) + 0x6e657261) ^ seed; + v3 = ((((size_t) 0x74656462 << 16) << 16) + 0x79746573) ^ ~seed; + + #ifdef STBDS_TEST_SIPHASH_2_4 + // hardcoded with key material in the siphash test vectors + v0 ^= 0x0706050403020100ull ^ seed; + v1 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed; + v2 ^= 0x0706050403020100ull ^ seed; + v3 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed; + #endif + + #define STBDS_SIPROUND() \ + do { \ + v0 += v1; v1 = STBDS_ROTATE_LEFT(v1, 13); v1 ^= v0; v0 = STBDS_ROTATE_LEFT(v0,STBDS_SIZE_T_BITS/2); \ + v2 += v3; v3 = STBDS_ROTATE_LEFT(v3, 16); v3 ^= v2; \ + v2 += v1; v1 = STBDS_ROTATE_LEFT(v1, 17); v1 ^= v2; v2 = STBDS_ROTATE_LEFT(v2,STBDS_SIZE_T_BITS/2); \ + v0 += v3; v3 = STBDS_ROTATE_LEFT(v3, 21); v3 ^= v0; \ + } while (0) + + for (i=0; i+sizeof(size_t) <= len; i += sizeof(size_t), d += sizeof(size_t)) { + data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); + data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // discarded if size_t == 4 + + v3 ^= data; + for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j) + STBDS_SIPROUND(); + v0 ^= data; + } + data = len << (STBDS_SIZE_T_BITS-8); + switch (len - i) { + case 7: data |= ((size_t) d[6] << 24) << 24; // fall through + case 6: data |= ((size_t) d[5] << 20) << 20; // fall through + case 5: data |= ((size_t) d[4] << 16) << 16; // fall through + case 4: data |= (d[3] << 24); // fall through + case 3: data |= (d[2] << 16); // fall through + case 2: data |= (d[1] << 8); // fall through + case 1: data |= d[0]; // fall through + case 0: break; + } + v3 ^= data; + for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j) + STBDS_SIPROUND(); + v0 ^= data; + v2 ^= 0xff; + for (j=0; j < STBDS_SIPHASH_D_ROUNDS; ++j) + STBDS_SIPROUND(); + +#ifdef STBDS_SIPHASH_2_4 + return v0^v1^v2^v3; +#else + return v1^v2^v3; // slightly stronger since v0^v3 in above cancels out final round operation? I tweeted at the authors of SipHash about this but they didn't reply +#endif +} + +size_t stbds_hash_bytes(void *p, size_t len, size_t seed) +{ +#ifdef STBDS_SIPHASH_2_4 + return stbds_siphash_bytes(p,len,seed); +#else + unsigned char *d = (unsigned char *) p; + + if (len == 4) { + unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); + #if 0 + // HASH32-A Bob Jenkin's hash function w/o large constants + hash ^= seed; + hash -= (hash<<6); + hash ^= (hash>>17); + hash -= (hash<<9); + hash ^= seed; + hash ^= (hash<<4); + hash -= (hash<<3); + hash ^= (hash<<10); + hash ^= (hash>>15); + #elif 1 + // HASH32-BB Bob Jenkin's presumably-accidental version of Thomas Wang hash with rotates turned into shifts. + // Note that converting these back to rotates makes it run a lot slower, presumably due to collisions, so I'm + // not really sure what's going on. + hash ^= seed; + hash = (hash ^ 61) ^ (hash >> 16); + hash = hash + (hash << 3); + hash = hash ^ (hash >> 4); + hash = hash * 0x27d4eb2d; + hash ^= seed; + hash = hash ^ (hash >> 15); + #else // HASH32-C - Murmur3 + hash ^= seed; + hash *= 0xcc9e2d51; + hash = (hash << 17) | (hash >> 15); + hash *= 0x1b873593; + hash ^= seed; + hash = (hash << 19) | (hash >> 13); + hash = hash*5 + 0xe6546b64; + hash ^= hash >> 16; + hash *= 0x85ebca6b; + hash ^= seed; + hash ^= hash >> 13; + hash *= 0xc2b2ae35; + hash ^= hash >> 16; + #endif + // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2 + // Note that the larger tables have high variance as they were run fewer times + // HASH32-A // HASH32-BB // HASH32-C + // 0.10ms // 0.10ms // 0.10ms : 2,000 inserts creating 2K table + // 0.96ms // 0.95ms // 0.99ms : 20,000 inserts creating 20K table + // 14.69ms // 14.43ms // 14.97ms : 200,000 inserts creating 200K table + // 199.99ms // 195.36ms // 202.05ms : 2,000,000 inserts creating 2M table + // 2234.84ms // 2187.74ms // 2240.38ms : 20,000,000 inserts creating 20M table + // 55.68ms // 53.72ms // 57.31ms : 500,000 inserts & deletes in 2K table + // 63.43ms // 61.99ms // 65.73ms : 500,000 inserts & deletes in 20K table + // 80.04ms // 77.96ms // 81.83ms : 500,000 inserts & deletes in 200K table + // 100.42ms // 97.40ms // 102.39ms : 500,000 inserts & deletes in 2M table + // 119.71ms // 120.59ms // 121.63ms : 500,000 inserts & deletes in 20M table + // 185.28ms // 195.15ms // 187.74ms : 500,000 inserts & deletes in 200M table + // 15.58ms // 14.79ms // 15.52ms : 200,000 inserts creating 200K table with varying key spacing + + return (((size_t) hash << 16 << 16) | hash) ^ seed; + } else if (len == 8 && sizeof(size_t) == 8) { + size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); + hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4 + hash ^= seed; + hash = (~hash) + (hash << 21); + hash ^= STBDS_ROTATE_RIGHT(hash,24); + hash *= 265; + hash ^= STBDS_ROTATE_RIGHT(hash,14); + hash ^= seed; + hash *= 21; + hash ^= STBDS_ROTATE_RIGHT(hash,28); + hash += (hash << 31); + hash = (~hash) + (hash << 18); + return hash; + } else { + return stbds_siphash_bytes(p,len,seed); + } +#endif +} +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + +static int stbds_is_key_equal(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode, size_t i) +{ + if (mode >= STBDS_HM_STRING) + return 0==strcmp((char *) key, * (char **) ((char *) a + elemsize*i + keyoffset)); + else + return 0==memcmp(key, (char *) a + elemsize*i + keyoffset, keysize); +} + +#define STBDS_HASH_TO_ARR(x,elemsize) ((char*) (x) - (elemsize)) +#define STBDS_ARR_TO_HASH(x,elemsize) ((char*) (x) + (elemsize)) + +#define stbds_hash_table(a) ((stbds_hash_index *) stbds_header(a)->hash_table) + +void stbds_hmfree_func(void *a, size_t elemsize) +{ + if (a == NULL) return; + if (stbds_hash_table(a) != NULL) { + if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) { + size_t i; + // skip 0th element, which is default + for (i=1; i < stbds_header(a)->length; ++i) + STBDS_FREE(NULL, *(char**) ((char *) a + elemsize*i)); + } + stbds_strreset(&stbds_hash_table(a)->string); + } + STBDS_FREE(NULL, stbds_header(a)->hash_table); + STBDS_FREE(NULL, stbds_header(a)); +} + +static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode) +{ + void *raw_a = STBDS_HASH_TO_ARR(a,elemsize); + stbds_hash_index *table = stbds_hash_table(raw_a); + size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed); + size_t step = STBDS_BUCKET_LENGTH; + size_t limit,i; + size_t pos; + stbds_hash_bucket *bucket; + + if (hash < 2) hash += 2; // stored hash values are forbidden from being 0, so we can detect empty slots + + pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2); + + for (;;) { + STBDS_STATS(++stbds_hash_probes); + bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT]; + + // start searching from pos to end of bucket, this should help performance on small hash tables that fit in cache + for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) { + if (bucket->hash[i] == hash) { + if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { + return (pos & ~STBDS_BUCKET_MASK)+i; + } + } else if (bucket->hash[i] == STBDS_HASH_EMPTY) { + return -1; + } + } + + // search from beginning of bucket to pos + limit = pos & STBDS_BUCKET_MASK; + for (i = 0; i < limit; ++i) { + if (bucket->hash[i] == hash) { + if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { + return (pos & ~STBDS_BUCKET_MASK)+i; + } + } else if (bucket->hash[i] == STBDS_HASH_EMPTY) { + return -1; + } + } + + // quadratic probing + pos += step; + step += STBDS_BUCKET_LENGTH; + pos &= (table->slot_count-1); + } + /* NOTREACHED */ +} + +void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode) +{ + size_t keyoffset = 0; + if (a == NULL) { + // make it non-empty so we can return a temp + a = stbds_arrgrowf(0, elemsize, 0, 1); + stbds_header(a)->length += 1; + memset(a, 0, elemsize); + *temp = STBDS_INDEX_EMPTY; + // adjust a to point after the default element + return STBDS_ARR_TO_HASH(a,elemsize); + } else { + stbds_hash_index *table; + void *raw_a = STBDS_HASH_TO_ARR(a,elemsize); + // adjust a to point to the default element + table = (stbds_hash_index *) stbds_header(raw_a)->hash_table; + if (table == 0) { + *temp = -1; + } else { + ptrdiff_t slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode); + if (slot < 0) { + *temp = STBDS_INDEX_EMPTY; + } else { + stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; + *temp = b->index[slot & STBDS_BUCKET_MASK]; + } + } + return a; + } +} + +void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode) +{ + ptrdiff_t temp; + void *p = stbds_hmget_key_ts(a, elemsize, key, keysize, &temp, mode); + stbds_temp(STBDS_HASH_TO_ARR(p,elemsize)) = temp; + return p; +} + +void * stbds_hmput_default(void *a, size_t elemsize) +{ + // three cases: + // a is NULL <- allocate + // a has a hash table but no entries, because of shmode <- grow + // a has entries <- do nothing + if (a == NULL || stbds_header(STBDS_HASH_TO_ARR(a,elemsize))->length == 0) { + a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a,elemsize) : NULL, elemsize, 0, 1); + stbds_header(a)->length += 1; + memset(a, 0, elemsize); + a=STBDS_ARR_TO_HASH(a,elemsize); + } + return a; +} + +static char *stbds_strdup(char *str); + +void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode) +{ + size_t keyoffset=0; + void *raw_a; + stbds_hash_index *table; + + if (a == NULL) { + a = stbds_arrgrowf(0, elemsize, 0, 1); + memset(a, 0, elemsize); + stbds_header(a)->length += 1; + // adjust a to point AFTER the default element + a = STBDS_ARR_TO_HASH(a,elemsize); + } + + // adjust a to point to the default element + raw_a = a; + a = STBDS_HASH_TO_ARR(a,elemsize); + + table = (stbds_hash_index *) stbds_header(a)->hash_table; + + if (table == NULL || table->used_count >= table->used_count_threshold) { + stbds_hash_index *nt; + size_t slot_count; + + slot_count = (table == NULL) ? STBDS_BUCKET_LENGTH : table->slot_count*2; + nt = stbds_make_hash_index(slot_count, table); + if (table) + STBDS_FREE(NULL, table); + else + nt->string.mode = mode >= STBDS_HM_STRING ? STBDS_SH_DEFAULT : 0; + stbds_header(a)->hash_table = table = nt; + STBDS_STATS(++stbds_hash_grow); + } + + // we iterate hash table explicitly because we want to track if we saw a tombstone + { + size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed); + size_t step = STBDS_BUCKET_LENGTH; + size_t pos; + ptrdiff_t tombstone = -1; + stbds_hash_bucket *bucket; + + // stored hash values are forbidden from being 0, so we can detect empty slots to early out quickly + if (hash < 2) hash += 2; + + pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2); + + for (;;) { + size_t limit, i; + STBDS_STATS(++stbds_hash_probes); + bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT]; + + // start searching from pos to end of bucket + for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) { + if (bucket->hash[i] == hash) { + if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { + stbds_temp(a) = bucket->index[i]; + if (mode >= STBDS_HM_STRING) + stbds_temp_key(a) = * (char **) ((char *) raw_a + elemsize*bucket->index[i] + keyoffset); + return STBDS_ARR_TO_HASH(a,elemsize); + } + } else if (bucket->hash[i] == 0) { + pos = (pos & ~STBDS_BUCKET_MASK) + i; + goto found_empty_slot; + } else if (tombstone < 0) { + if (bucket->index[i] == STBDS_INDEX_DELETED) + tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i); + } + } + + // search from beginning of bucket to pos + limit = pos & STBDS_BUCKET_MASK; + for (i = 0; i < limit; ++i) { + if (bucket->hash[i] == hash) { + if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { + stbds_temp(a) = bucket->index[i]; + return STBDS_ARR_TO_HASH(a,elemsize); + } + } else if (bucket->hash[i] == 0) { + pos = (pos & ~STBDS_BUCKET_MASK) + i; + goto found_empty_slot; + } else if (tombstone < 0) { + if (bucket->index[i] == STBDS_INDEX_DELETED) + tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i); + } + } + + // quadratic probing + pos += step; + step += STBDS_BUCKET_LENGTH; + pos &= (table->slot_count-1); + } + found_empty_slot: + if (tombstone >= 0) { + pos = tombstone; + --table->tombstone_count; + } + ++table->used_count; + + { + ptrdiff_t i = (ptrdiff_t) stbds_arrlen(a); + // we want to do stbds_arraddn(1), but we can't use the macros since we don't have something of the right type + if ((size_t) i+1 > stbds_arrcap(a)) + *(void **) &a = stbds_arrgrowf(a, elemsize, 1, 0); + raw_a = STBDS_ARR_TO_HASH(a,elemsize); + + STBDS_ASSERT((size_t) i+1 <= stbds_arrcap(a)); + stbds_header(a)->length = i+1; + bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT]; + bucket->hash[pos & STBDS_BUCKET_MASK] = hash; + bucket->index[pos & STBDS_BUCKET_MASK] = i-1; + stbds_temp(a) = i-1; + + switch (table->string.mode) { + case STBDS_SH_STRDUP: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_strdup((char*) key); break; + case STBDS_SH_ARENA: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_stralloc(&table->string, (char*)key); break; + case STBDS_SH_DEFAULT: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = (char *) key; break; + default: memcpy((char *) a + elemsize*i, key, keysize); break; + } + } + return STBDS_ARR_TO_HASH(a,elemsize); + } +} + +void * stbds_shmode_func(size_t elemsize, int mode) +{ + void *a = stbds_arrgrowf(0, elemsize, 0, 1); + stbds_hash_index *h; + memset(a, 0, elemsize); + stbds_header(a)->length = 1; + stbds_header(a)->hash_table = h = (stbds_hash_index *) stbds_make_hash_index(STBDS_BUCKET_LENGTH, NULL); + h->string.mode = (unsigned char) mode; + return STBDS_ARR_TO_HASH(a,elemsize); +} + +void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode) +{ + if (a == NULL) { + return 0; + } else { + stbds_hash_index *table; + void *raw_a = STBDS_HASH_TO_ARR(a,elemsize); + table = (stbds_hash_index *) stbds_header(raw_a)->hash_table; + stbds_temp(raw_a) = 0; + if (table == 0) { + return a; + } else { + ptrdiff_t slot; + slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode); + if (slot < 0) + return a; + else { + stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; + int i = slot & STBDS_BUCKET_MASK; + ptrdiff_t old_index = b->index[i]; + ptrdiff_t final_index = (ptrdiff_t) stbds_arrlen(raw_a)-1-1; // minus one for the raw_a vs a, and minus one for 'last' + STBDS_ASSERT(slot < (ptrdiff_t) table->slot_count); + --table->used_count; + ++table->tombstone_count; + stbds_temp(raw_a) = 1; + STBDS_ASSERT(table->used_count >= 0); + //STBDS_ASSERT(table->tombstone_count < table->slot_count/4); + b->hash[i] = STBDS_HASH_DELETED; + b->index[i] = STBDS_INDEX_DELETED; + + if (mode == STBDS_HM_STRING && table->string.mode == STBDS_SH_STRDUP) + STBDS_FREE(NULL, *(char**) ((char *) a+elemsize*old_index)); + + // if indices are the same, memcpy is a no-op, but back-pointer-fixup will fail, so skip + if (old_index != final_index) { + // swap delete + memmove((char*) a + elemsize*old_index, (char*) a + elemsize*final_index, elemsize); + + // now find the slot for the last element + if (mode == STBDS_HM_STRING) + slot = stbds_hm_find_slot(a, elemsize, *(char**) ((char *) a+elemsize*old_index + keyoffset), keysize, keyoffset, mode); + else + slot = stbds_hm_find_slot(a, elemsize, (char* ) a+elemsize*old_index + keyoffset, keysize, keyoffset, mode); + STBDS_ASSERT(slot >= 0); + b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; + i = slot & STBDS_BUCKET_MASK; + STBDS_ASSERT(b->index[i] == final_index); + b->index[i] = old_index; + } + stbds_header(raw_a)->length -= 1; + + if (table->used_count < table->used_count_shrink_threshold && table->slot_count > STBDS_BUCKET_LENGTH) { + stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count>>1, table); + STBDS_FREE(NULL, table); + STBDS_STATS(++stbds_hash_shrink); + } else if (table->tombstone_count > table->tombstone_count_threshold) { + stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count , table); + STBDS_FREE(NULL, table); + STBDS_STATS(++stbds_hash_rebuild); + } + + return a; + } + } + } + /* NOTREACHED */ +} + +static char *stbds_strdup(char *str) +{ + // to keep replaceable allocator simple, we don't want to use strdup. + // rolling our own also avoids problem of strdup vs _strdup + size_t len = strlen(str)+1; + char *p = (char*) STBDS_REALLOC(NULL, 0, len); + memmove(p, str, len); + return p; +} + +#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MIN +#define STBDS_STRING_ARENA_BLOCKSIZE_MIN 512u +#endif +#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MAX +#define STBDS_STRING_ARENA_BLOCKSIZE_MAX (1u<<20) +#endif + +char *stbds_stralloc(stbds_string_arena *a, char *str) +{ + char *p; + size_t len = strlen(str)+1; + if (len > a->remaining) { + // compute the next blocksize + size_t blocksize = a->block; + + // size is 512, 512, 1024, 1024, 2048, 2048, 4096, 4096, etc., so that + // there are log(SIZE) allocations to free when we destroy the table + blocksize = (size_t) (STBDS_STRING_ARENA_BLOCKSIZE_MIN) << (blocksize>>1); + + // if size is under 1M, advance to next blocktype + if (blocksize < (size_t)(STBDS_STRING_ARENA_BLOCKSIZE_MAX)) + ++a->block; + + if (len > blocksize) { + // if string is larger than blocksize, then just allocate the full size. + // note that we still advance string_block so block size will continue + // increasing, so e.g. if somebody only calls this with 1000-long strings, + // eventually the arena will start doubling and handling those as well + stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + len); + memmove(sb->storage, str, len); + if (a->storage) { + // insert it after the first element, so that we don't waste the space there + sb->next = a->storage->next; + a->storage->next = sb; + } else { + sb->next = 0; + a->storage = sb; + a->remaining = 0; // this is redundant, but good for clarity + } + return sb->storage; + } else { + stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + blocksize); + sb->next = a->storage; + a->storage = sb; + a->remaining = blocksize; + } + } + + STBDS_ASSERT(len <= a->remaining); + p = a->storage->storage + a->remaining - len; + a->remaining -= len; + memmove(p, str, len); + return p; +} + +void stbds_strreset(stbds_string_arena *a) +{ + stbds_string_block *x,*y; + x = a->storage; + while (x) { + y = x->next; + STBDS_FREE(NULL, x); + x = y; + } + memset(a, 0, sizeof(*a)); +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// UNIT TESTS +// + +#ifdef STBDS_UNIT_TESTS +#include +#ifdef STBDS_ASSERT_WAS_UNDEFINED +#undef STBDS_ASSERT +#endif +#ifndef STBDS_ASSERT +#define STBDS_ASSERT assert +#include +#endif + +typedef struct { int key,b,c,d; } stbds_struct; +typedef struct { int key[2],b,c,d; } stbds_struct2; + +static char buffer[256]; +char *strkey(int n) +{ +#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) + sprintf_s(buffer, sizeof(buffer), "test_%d", n); +#else + sprintf(buffer, "test_%d", n); +#endif + return buffer; +} + +void stbds_unit_tests(void) +{ +#if defined(_MSC_VER) && _MSC_VER <= 1200 && defined(__cplusplus) + // VC6 C++ doesn't like the template<> trick on unnamed structures, so do nothing! + STBDS_ASSERT(0); +#else + const int testsize = 100000; + const int testsize2 = testsize/20; + int *arr=NULL; + struct { int key; int value; } *intmap = NULL; + struct { char *key; int value; } *strmap = NULL, s; + struct { stbds_struct key; int value; } *map = NULL; + stbds_struct *map2 = NULL; + stbds_struct2 *map3 = NULL; + stbds_string_arena sa = { 0 }; + int key3[2] = { 1,2 }; + ptrdiff_t temp; + + int i,j; + + STBDS_ASSERT(arrlen(arr)==0); + for (i=0; i < 20000; i += 50) { + for (j=0; j < i; ++j) + arrpush(arr,j); + arrfree(arr); + } + + for (i=0; i < 4; ++i) { + arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4); + arrdel(arr,i); + arrfree(arr); + arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4); + arrdelswap(arr,i); + arrfree(arr); + } + + for (i=0; i < 5; ++i) { + arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4); + stbds_arrins(arr,i,5); + STBDS_ASSERT(arr[i] == 5); + if (i < 4) + STBDS_ASSERT(arr[4] == 4); + arrfree(arr); + } + + i = 1; + STBDS_ASSERT(hmgeti(intmap,i) == -1); + hmdefault(intmap, -2); + STBDS_ASSERT(hmgeti(intmap, i) == -1); + STBDS_ASSERT(hmget (intmap, i) == -2); + for (i=0; i < testsize; i+=2) + hmput(intmap, i, i*5); + for (i=0; i < testsize; i+=1) { + if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 ); + else STBDS_ASSERT(hmget(intmap, i) == i*5); + if (i & 1) STBDS_ASSERT(hmget_ts(intmap, i, temp) == -2 ); + else STBDS_ASSERT(hmget_ts(intmap, i, temp) == i*5); + } + for (i=0; i < testsize; i+=2) + hmput(intmap, i, i*3); + for (i=0; i < testsize; i+=1) + if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 ); + else STBDS_ASSERT(hmget(intmap, i) == i*3); + for (i=2; i < testsize; i+=4) + hmdel(intmap, i); // delete half the entries + for (i=0; i < testsize; i+=1) + if (i & 3) STBDS_ASSERT(hmget(intmap, i) == -2 ); + else STBDS_ASSERT(hmget(intmap, i) == i*3); + for (i=0; i < testsize; i+=1) + hmdel(intmap, i); // delete the rest of the entries + for (i=0; i < testsize; i+=1) + STBDS_ASSERT(hmget(intmap, i) == -2 ); + hmfree(intmap); + for (i=0; i < testsize; i+=2) + hmput(intmap, i, i*3); + hmfree(intmap); + + #if defined(__clang__) || defined(__GNUC__) + #ifndef __cplusplus + intmap = NULL; + hmput(intmap, 15, 7); + hmput(intmap, 11, 3); + hmput(intmap, 9, 5); + STBDS_ASSERT(hmget(intmap, 9) == 5); + STBDS_ASSERT(hmget(intmap, 11) == 3); + STBDS_ASSERT(hmget(intmap, 15) == 7); + #endif + #endif + + for (i=0; i < testsize; ++i) + stralloc(&sa, strkey(i)); + strreset(&sa); + + { + s.key = "a", s.value = 1; + shputs(strmap, s); + STBDS_ASSERT(*strmap[0].key == 'a'); + STBDS_ASSERT(strmap[0].key == s.key); + STBDS_ASSERT(strmap[0].value == s.value); + shfree(strmap); + } + + { + s.key = "a", s.value = 1; + sh_new_strdup(strmap); + shputs(strmap, s); + STBDS_ASSERT(*strmap[0].key == 'a'); + STBDS_ASSERT(strmap[0].key != s.key); + STBDS_ASSERT(strmap[0].value == s.value); + shfree(strmap); + } + + { + s.key = "a", s.value = 1; + sh_new_arena(strmap); + shputs(strmap, s); + STBDS_ASSERT(*strmap[0].key == 'a'); + STBDS_ASSERT(strmap[0].key != s.key); + STBDS_ASSERT(strmap[0].value == s.value); + shfree(strmap); + } + + for (j=0; j < 2; ++j) { + STBDS_ASSERT(shgeti(strmap,"foo") == -1); + if (j == 0) + sh_new_strdup(strmap); + else + sh_new_arena(strmap); + STBDS_ASSERT(shgeti(strmap,"foo") == -1); + shdefault(strmap, -2); + STBDS_ASSERT(shgeti(strmap,"foo") == -1); + for (i=0; i < testsize; i+=2) + shput(strmap, strkey(i), i*3); + for (i=0; i < testsize; i+=1) + if (i & 1) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 ); + else STBDS_ASSERT(shget(strmap, strkey(i)) == i*3); + for (i=2; i < testsize; i+=4) + shdel(strmap, strkey(i)); // delete half the entries + for (i=0; i < testsize; i+=1) + if (i & 3) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 ); + else STBDS_ASSERT(shget(strmap, strkey(i)) == i*3); + for (i=0; i < testsize; i+=1) + shdel(strmap, strkey(i)); // delete the rest of the entries + for (i=0; i < testsize; i+=1) + STBDS_ASSERT(shget(strmap, strkey(i)) == -2 ); + shfree(strmap); + } + + { + struct { char *key; char value; } *hash = NULL; + char name[4] = "jen"; + shput(hash, "bob" , 'h'); + shput(hash, "sally" , 'e'); + shput(hash, "fred" , 'l'); + shput(hash, "jen" , 'x'); + shput(hash, "doug" , 'o'); + + shput(hash, name , 'l'); + shfree(hash); + } + + for (i=0; i < testsize; i += 2) { + stbds_struct s = { i,i*2,i*3,i*4 }; + hmput(map, s, i*5); + } + + for (i=0; i < testsize; i += 1) { + stbds_struct s = { i,i*2,i*3 ,i*4 }; + stbds_struct t = { i,i*2,i*3+1,i*4 }; + if (i & 1) STBDS_ASSERT(hmget(map, s) == 0); + else STBDS_ASSERT(hmget(map, s) == i*5); + if (i & 1) STBDS_ASSERT(hmget_ts(map, s, temp) == 0); + else STBDS_ASSERT(hmget_ts(map, s, temp) == i*5); + //STBDS_ASSERT(hmget(map, t.key) == 0); + } + + for (i=0; i < testsize; i += 2) { + stbds_struct s = { i,i*2,i*3,i*4 }; + hmputs(map2, s); + } + hmfree(map); + + for (i=0; i < testsize; i += 1) { + stbds_struct s = { i,i*2,i*3,i*4 }; + stbds_struct t = { i,i*2,i*3+1,i*4 }; + if (i & 1) STBDS_ASSERT(hmgets(map2, s.key).d == 0); + else STBDS_ASSERT(hmgets(map2, s.key).d == i*4); + //STBDS_ASSERT(hmgetp(map2, t.key) == 0); + } + hmfree(map2); + + for (i=0; i < testsize; i += 2) { + stbds_struct2 s = { { i,i*2 }, i*3,i*4, i*5 }; + hmputs(map3, s); + } + for (i=0; i < testsize; i += 1) { + stbds_struct2 s = { { i,i*2}, i*3, i*4, i*5 }; + stbds_struct2 t = { { i,i*2}, i*3+1, i*4, i*5 }; + if (i & 1) STBDS_ASSERT(hmgets(map3, s.key).d == 0); + else STBDS_ASSERT(hmgets(map3, s.key).d == i*5); + //STBDS_ASSERT(hmgetp(map3, t.key) == 0); + } +#endif +} +#endif + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2019 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ From 84d136c256b1a46d33350522fa9cb9af743ec581 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Tue, 27 Jan 2026 18:32:00 +0100 Subject: [PATCH 16/85] fix issues with net drivers --- nbnet.h | 36 +++++++------------ net_drivers/udp.h | 3 -- net_drivers/webrtc_native.h | 71 +++++++++++++++++-------------------- soak/client.c | 26 +++++++------- soak/server.c | 22 ++++++------ 5 files changed, 69 insertions(+), 89 deletions(-) diff --git a/nbnet.h b/nbnet.h index 609367c..cca79da 100644 --- a/nbnet.h +++ b/nbnet.h @@ -466,38 +466,22 @@ bool NBN_EventQueue_IsEmpty(NBN_EventQueue *); #endif /* NBNET_WINDOWS */ #define NBN_GameClient_SetPing(v) \ - { \ - nbn_game_client.endpoint.packet_simulator.ping = v; \ - } + { nbn_game_client.endpoint.packet_simulator.ping = v; } #define NBN_GameClient_SetJitter(v) \ - { \ - nbn_game_client.endpoint.packet_simulator.jitter = v; \ - } + { nbn_game_client.endpoint.packet_simulator.jitter = v; } #define NBN_GameClient_SetPacketLoss(v) \ - { \ - nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; \ - } + { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } #define NBN_GameClient_SetPacketDuplication(v) \ - { \ - nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; \ - } + { nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; } #define NBN_GameServer_SetPing(v) \ - { \ - nbn_game_server.endpoint.packet_simulator.ping = v; \ - } + { nbn_game_server.endpoint.packet_simulator.ping = v; } #define NBN_GameServer_SetJitter(v) \ - { \ - nbn_game_server.endpoint.packet_simulator.jitter = v; \ - } + { nbn_game_server.endpoint.packet_simulator.jitter = v; } #define NBN_GameServer_SetPacketLoss(v) \ - { \ - nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; \ - } + { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } #define NBN_GameServer_SetPacketDuplication(v) \ - { \ - nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; \ - } + { nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; } typedef struct NBN_PacketSimulatorEntry NBN_PacketSimulatorEntry; @@ -1027,6 +1011,10 @@ int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data); #ifdef NBNET_IMPL +#define STB_DS_IMPLEMENTATION + +#include "stb_ds.h" + #pragma region NBN_ConnectionVector #define NBN_CONNECTION_VECTOR_INITIAL_CAPACITY 32 diff --git a/net_drivers/udp.h b/net_drivers/udp.h index 085f8bb..1ec0dec 100644 --- a/net_drivers/udp.h +++ b/net_drivers/udp.h @@ -48,9 +48,6 @@ void NBN_UDP_Register(void); #include "../nbnet.h" #endif -#define STB_DS_IMPLEMENTATION -#include "../stb_ds.h" - #define NBN_UDP_DRIVER_ID 0 #define NBN_UDP_DRIVER_NAME "UDP" diff --git a/net_drivers/webrtc_native.h b/net_drivers/webrtc_native.h index 7367b3a..d053bc5 100644 --- a/net_drivers/webrtc_native.h +++ b/net_drivers/webrtc_native.h @@ -38,7 +38,6 @@ freely, subject to the following restrictions: NBN_GameServer_Start */ -#include "../stb_ds.h" #include "json.h" #include #include @@ -51,7 +50,7 @@ freely, subject to the following restrictions: #endif #define NBN_WEBRTC_NATIVE_DRIVER_ID 2 -#define NBN_WEBRTC_NATIVE_DRIVER_NAME "WebRTC_C" +#define NBN_WEBRTC_NATIVE_DRIVER_NAME "WebRTC_Native" typedef struct NBN_WebRTC_Native_Config { bool enable_tls; @@ -322,10 +321,9 @@ typedef struct NBN_WebRTC_Native_Server { } *peers; uint16_t ws_port; uint32_t protocol_id; - char packet_buffer[NBN_PACKET_MAX_SIZE]; } NBN_WebRTC_Native_Server; -static NBN_WebRTC_Native_Server nbn_wrtc_c_serv = {0, NULL, false, 0, 0, {0}}; +static NBN_WebRTC_Native_Server nbn_wrtc_native_serv = {0, NULL, 0, 0}; static void NBN_WebRTC_Native_Serv_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { NBN_LogDebug("Processing local description of type '%s'", type); @@ -360,10 +358,8 @@ static void NBN_WebRTC_Native_Serv_OnWsOpen(int ws, void *user_ptr) { return; } - peer->conn = - NBN_GameServer_CreateClientConnection(NBN_WEBRTC_C_DRIVER_ID, peer, nbn_wrtc_c_serv.protocol_id, peer->id); - - hmput(nbn_wrtc_c_serv.peers, peer->id, peer); + peer->conn = NBN_GameServer_CreateClientConnection(NBN_WEBRTC_NATIVE_DRIVER_ID, peer, peer->id); + hmput(nbn_wrtc_native_serv.peers, peer->id, peer); } static void NBN_WebRTC_Native_Serv_OnWsClosed(int ws, void *user_ptr) { @@ -393,9 +389,9 @@ static void NBN_WebRTC_Native_Serv_OnWsConnection(int wsserver, int ws, void *us } static int NBN_WebRTC_Native_ServStart(uint32_t protocol_id, uint16_t port) { - nbn_wrtc_c_serv.ws_port = port; - nbn_wrtc_c_serv.protocol_id = protocol_id; - nbn_wrtc_c_serv.wsserver = -1; + nbn_wrtc_native_serv.ws_port = port; + nbn_wrtc_native_serv.protocol_id = protocol_id; + nbn_wrtc_native_serv.wsserver = -1; rtcInitLogger(nbn_wrtc_c_cfg.log_level, NBN_WebRTC_Native_Log); rtcPreload(); @@ -413,18 +409,18 @@ static int NBN_WebRTC_Native_ServStart(uint32_t protocol_id, uint16_t port) { return NBN_ERROR; } - nbn_wrtc_c_serv.wsserver = wsserver; + nbn_wrtc_native_serv.wsserver = wsserver; - hmdefault(nbn_wrtc_c_serv.peers, NULL); + hmdefault(nbn_wrtc_native_serv.peers, NULL); return 0; } static void NBN_WebRTC_Native_ServStop(void) { - hmfree(nbn_wrtc_c_serv.peers); + hmfree(nbn_wrtc_native_serv.peers); - if (nbn_wrtc_c_serv.wsserver >= 0) { - rtcDeleteWebSocketServer(nbn_wrtc_c_serv.wsserver); + if (nbn_wrtc_native_serv.wsserver >= 0) { + rtcDeleteWebSocketServer(nbn_wrtc_native_serv.wsserver); } rtcCleanup(); @@ -432,14 +428,14 @@ static void NBN_WebRTC_Native_ServStop(void) { static int NBN_WebRTC_Native_ServRecvPackets(void) { static NBN_Packet packet = {0}; - const int buffer_size = sizeof(nbn_wrtc_c_serv.packet_buffer); + const int buffer_size = sizeof(packet.buffer); int size = buffer_size; - for (unsigned int i = 0; i < hmlen(nbn_wrtc_c_serv.peers); i++) { - NBN_WebRTC_Native_Peer *peer = nbn_wrtc_c_serv.peers[i].value; + for (unsigned int i = 0; i < hmlen(nbn_wrtc_native_serv.peers); i++) { + NBN_WebRTC_Native_Peer *peer = nbn_wrtc_native_serv.peers[i].value; - while (rtcReceiveMessage(peer->channel_id, nbn_wrtc_c_serv.packet_buffer, &size) == RTC_ERR_SUCCESS) { - if (NBN_Packet_InitRead(&packet, peer->conn, (uint8_t *)nbn_wrtc_c_serv.packet_buffer, size) < 0) + while (rtcReceiveMessage(peer->channel_id, (char *)packet.buffer, &size) == RTC_ERR_SUCCESS) { + if (NBN_Packet_InitRead(&packet, nbn_wrtc_native_serv.protocol_id, size) < 0) continue; packet.sender = peer->conn; @@ -454,7 +450,7 @@ static int NBN_WebRTC_Native_ServRecvPackets(void) { static void NBN_WebRTC_Native_ServRemoveClientConnection(NBN_Connection *conn) { NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)conn->driver_data; - int ret = hmdel(nbn_wrtc_c_serv.peers, peer->id); + int ret = hmdel(nbn_wrtc_native_serv.peers, peer->id); NBN_Assert(ret == 1); NBN_WebRTC_Native_DestroyPeer(peer); @@ -481,10 +477,9 @@ typedef struct NBN_WebRTC_Native_Client { uint32_t protocol_id; bool is_connected; NBN_WebRTC_Native_Peer *peer; - char packet_buffer[NBN_PACKET_MAX_SIZE]; } NBN_WebRTC_Native_Client; -static NBN_WebRTC_Native_Client nbn_wrtc_c_cli = {0, false, false, NULL, {0}}; +static NBN_WebRTC_Native_Client nbn_wrtc_native_cli = {0, false, false}; static void NBN_WebRTC_Native_Cli_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { NBN_LogDebug("Processing local description of type '%s'", type); @@ -504,7 +499,7 @@ static void NBN_WebRTC_Native_Cli_OnPeerStateChanged(int pc, rtcState state, voi NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)user_ptr; NBN_LogDebug("Server peer is connected !", pc); - nbn_wrtc_c_cli.is_connected = true; + nbn_wrtc_native_cli.is_connected = true; } } @@ -521,8 +516,8 @@ static void NBN_WebRTC_Native_Cli_OnWsOpen(int ws, void *user_ptr) { NBN_LogDebug("Successfully created peer: %d", peer->id); - peer->conn = NBN_GameClient_CreateServerConnection(NBN_WEBRTC_C_DRIVER_ID, peer, nbn_wrtc_c_cli.protocol_id); - nbn_wrtc_c_cli.peer = peer; + peer->conn = NBN_GameClient_CreateServerConnection(NBN_WEBRTC_NATIVE_DRIVER_ID, peer); + nbn_wrtc_native_cli.peer = peer; } static void NBN_WebRTC_Native_Cli_OnWsClosed(int ws, void *user_ptr) { @@ -559,12 +554,12 @@ static int AttemptConnection(void) { return NBN_ERROR; } #endif - if (--retries <= 0 || nbn_wrtc_c_cli.is_connected) { + if (--retries <= 0 || nbn_wrtc_native_cli.is_connected) { break; } } - if (!nbn_wrtc_c_cli.is_connected) { + if (!nbn_wrtc_native_cli.is_connected) { NBN_LogError("Failed to connect"); return NBN_ERROR; @@ -588,7 +583,7 @@ static int NBN_WebRTC_Native_CliStart(uint32_t protocol_id, const char *host, ui return NBN_ERROR; } - nbn_wrtc_c_cli.protocol_id = protocol_id; + nbn_wrtc_native_cli.protocol_id = protocol_id; NBN_LogDebug("Successfully created client WS: %d", cli_ws); @@ -601,24 +596,24 @@ static int NBN_WebRTC_Native_CliStart(uint32_t protocol_id, const char *host, ui } static void NBN_WebRTC_Native_CliStop(void) { - NBN_WebRTC_Native_Peer *peer = nbn_wrtc_c_cli.peer; + NBN_WebRTC_Native_Peer *peer = nbn_wrtc_native_cli.peer; if (peer) { NBN_WebRTC_Native_DestroyPeer(peer); } - nbn_wrtc_c_cli.is_connected = false; + nbn_wrtc_native_cli.is_connected = false; rtcCleanup(); } static int NBN_WebRTC_Native_CliRecvPackets(void) { static NBN_Packet packet = {0}; - const int buffer_size = sizeof(nbn_wrtc_c_cli.packet_buffer); + const int buffer_size = sizeof(packet.buffer); int size = buffer_size; - NBN_WebRTC_Native_Peer *peer = nbn_wrtc_c_cli.peer; + NBN_WebRTC_Native_Peer *peer = nbn_wrtc_native_cli.peer; - while (rtcReceiveMessage(peer->channel_id, nbn_wrtc_c_cli.packet_buffer, &size) == RTC_ERR_SUCCESS) { - if (NBN_Packet_InitRead(&packet, peer->conn, (uint8_t *)nbn_wrtc_c_cli.packet_buffer, size) < 0) + while (rtcReceiveMessage(peer->channel_id, (char *)packet.buffer, &size) == RTC_ERR_SUCCESS) { + if (NBN_Packet_InitRead(&packet, nbn_wrtc_native_cli.protocol_id, size) < 0) continue; packet.sender = peer->conn; @@ -630,7 +625,7 @@ static int NBN_WebRTC_Native_CliRecvPackets(void) { } static int NBN_WebRTC_Native_CliSendPacket(NBN_Packet *packet) { - NBN_WebRTC_Native_Peer *peer = nbn_wrtc_c_cli.peer; + NBN_WebRTC_Native_Peer *peer = nbn_wrtc_native_cli.peer; if (rtcSendMessage(peer->channel_id, (char *)packet->buffer, packet->size) < 0) { NBN_LogError("rtcSendMessage failed for peer %d", peer->id); @@ -674,7 +669,7 @@ void NBN_WebRTC_Native_Register(NBN_WebRTC_Native_Config config) { NBN_WebRTC_Native_ServRecvPackets, NBN_WebRTC_Native_ServSendPacketTo, NBN_WebRTC_Native_ServRemoveClientConnection}; - NBN_Driver_Register(NBN_WEBRTC_C_DRIVER_ID, NBN_WEBRTC_C_DRIVER_NAME, driver_impl); + NBN_Driver_Register(NBN_WEBRTC_NATIVE_DRIVER_ID, NBN_WEBRTC_NATIVE_DRIVER_NAME, driver_impl); } void NBN_WebRTC_Native_Unregister(void) { diff --git a/soak/client.c b/soak/client.c index 300233f..24e70de 100644 --- a/soak/client.c +++ b/soak/client.c @@ -34,7 +34,7 @@ #include "../net_drivers/udp.h" #ifdef WEBRTC_NATIVE -#include "../net_drivers/webrtc_c.h" +#include "../net_drivers/webrtc_native.h" #endif #endif // __EMSCRIPTEN__ @@ -274,23 +274,23 @@ int main(int argc, char *argv[]) { SoakOptions options = Soak_GetOptions(); #ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register((NBN_WebRTC_Config){.enable_tls = false}); // Register the WebRTC driver + NBN_WebRTC_Register((NBN_WebRTC_Config){.enable_tls = false}); // Register the JS WebRTC driver #else #ifdef WEBRTC_NATIVE if (options.webrtc) { - // Register native WebRTC driver + // Register the native WebRTC driver const char *ice_servers[] = {"stun:stun01.sipphone.com"}; - NBN_WebRTC_C_Config cfg = {.ice_servers = ice_servers, - .ice_servers_count = 1, - .enable_tls = false, - .cert_path = NULL, - .key_path = NULL, - .passphrase = NULL, - .log_level = RTC_LOG_VERBOSE}; - - NBN_WebRTC_C_Register(cfg); + NBN_WebRTC_Native_Config cfg = {.ice_servers = ice_servers, + .ice_servers_count = 1, + .enable_tls = false, + .cert_path = NULL, + .key_path = NULL, + .passphrase = NULL, + .log_level = RTC_LOG_VERBOSE}; + + NBN_WebRTC_Native_Register(cfg); } else { NBN_UDP_Register(); } @@ -353,7 +353,7 @@ int main(int argc, char *argv[]) { #ifdef WEBRTC_NATIVE if (options.webrtc) { - NBN_WebRTC_C_Unregister(); + NBN_WebRTC_Native_Unregister(); } #endif // WEBRTC_NATIVE diff --git a/soak/server.c b/soak/server.c index 88d23e5..c86d213 100644 --- a/soak/server.c +++ b/soak/server.c @@ -35,7 +35,7 @@ #include "../net_drivers/udp.h" #ifdef WEBRTC_NATIVE -#include "../net_drivers/webrtc_c.h" +#include "../net_drivers/webrtc_native.h" #endif #endif // __EMSCRIPTEN__ @@ -306,15 +306,15 @@ int main(int argc, char *argv[]) { #ifdef WEBRTC_NATIVE // Register native WebRTC driver const char *ice_servers[] = {"stun:stun01.sipphone.com"}; - NBN_WebRTC_C_Config cfg = {.ice_servers = ice_servers, - .ice_servers_count = 1, - .enable_tls = false, - .cert_path = NULL, - .key_path = NULL, - .passphrase = NULL, - .log_level = RTC_LOG_VERBOSE}; - - NBN_WebRTC_C_Register(cfg); + NBN_WebRTC_Native_Config cfg = {.ice_servers = ice_servers, + .ice_servers_count = 1, + .enable_tls = false, + .cert_path = NULL, + .key_path = NULL, + .passphrase = NULL, + .log_level = RTC_LOG_VERBOSE}; + + NBN_WebRTC_Native_Register(cfg); #endif // WEBRTC_NATIVE SoakOptions options = Soak_GetOptions(); @@ -342,7 +342,7 @@ int main(int argc, char *argv[]) { Soak_Deinit(); #ifdef WEBRTC_NATIVE - NBN_WebRTC_C_Unregister(); + NBN_WebRTC_Native_Unregister(); #endif return ret; From d1cddebf1562e4a664d713458a7e71bd0d145ac0 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Tue, 27 Jan 2026 18:45:35 +0100 Subject: [PATCH 17/85] fix examples cmake file --- examples/CMakeLists.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index aa94e21..4725651 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -2,9 +2,3 @@ cmake_minimum_required(VERSION 3.5) project(nbnet_examples) add_subdirectory(echo) -add_subdirectory(echo_bytes) - -if (NOT DEFINED CPP_COMPILE) - # not supported in CPP - add_subdirectory(rpc) -endif (NOT DEFINED CPP_COMPILE) From d9c8252df8b826576bbf8dda0303ceb87c441e12 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Tue, 27 Jan 2026 18:47:12 +0100 Subject: [PATCH 18/85] fix unit tests --- tests/CMakeLists.txt | 7 - tests/message_chunks.c | 301 ----------------------------------------- tests/serialization.c | 121 ----------------- 3 files changed, 429 deletions(-) delete mode 100644 tests/message_chunks.c delete mode 100644 tests/serialization.c diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f1875b5..e324353 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,14 +11,7 @@ endif (CMAKE_COMPILER_IS_GNUCXX) # add_executable(message_chunks message_chunks.c CuTest.c) add_executable(string_tests string_tests.c CuTest.c) -add_executable(serialization_tests serialization.c CuTest.c) target_link_libraries(string_tests -lm) -target_link_libraries(serialization_tests -lm) -# FIXME: adapt to latest API -# add_test(message_chunks message_chunks) -add_test(serialization_tests serialization_tests) add_test(string_tests string_tests) - -target_compile_definitions(serialization_tests PUBLIC NBN_DEBUG) diff --git a/tests/message_chunks.c b/tests/message_chunks.c deleted file mode 100644 index 649c632..0000000 --- a/tests/message_chunks.c +++ /dev/null @@ -1,301 +0,0 @@ -#include -#include - -#include "CuTest.h" - -#define NBNET_IMPL - -#define NBN_LogInfo printf -#define NBN_LogTrace printf -#define NBN_LogDebug printf -#define NBN_LogError printf - -#define NBN_Allocator malloc -#define NBN_Deallocator free - -#include "../nbnet.h" - -typedef struct -{ - uint8_t data[4096]; -} BigMessage; - -#define BIG_MESSAGE_TYPE 0 - -BigMessage *BigMessage_Create() -{ - return malloc(sizeof(BigMessage)); -} - -int BigMessage_Serialize(BigMessage *msg, NBN_Stream *stream) -{ - SERIALIZE_BYTES(msg->data, 4096); - - return 0; -} - -void BigMessage_Destroy(BigMessage *msg) -{ - free(msg); -} - -static NBN_Endpoint endpoint; - -NBN_Connection *Begin(NBN_Endpoint *endpoint) -{ - NBN_Endpoint_Init(endpoint, (NBN_Config){ .protocol_name = "tests" }); - NBN_Endpoint_RegisterMessageBuilder(endpoint, (NBN_MessageBuilder)BigMessage_Create, BIG_MESSAGE_TYPE); - NBN_Endpoint_RegisterMessageSerializer(endpoint, (NBN_MessageSerializer)BigMessage_Serialize, BIG_MESSAGE_TYPE); - NBN_Endpoint_RegisterMessageDestructor(endpoint, (NBN_MessageDestructor)BigMessage_Destroy, BIG_MESSAGE_TYPE); - - return NBN_Endpoint_CreateConnection(endpoint, 0); -} - -static void End(NBN_Connection *conn, NBN_Endpoint *endpoint) -{ - NBN_ListNode *current_node = conn->send_queue->head; - - for (int i = 0; current_node != NULL; i++) - { - NBN_Message *m = current_node->data; - - current_node = current_node->next; - - NBN_List_Remove(conn->send_queue, m); - } - - NBN_Connection_Destroy(conn); - NBN_Endpoint_Deinit(endpoint); -} - -void Test_ChunksGeneration(CuTest *tc) -{ - NBN_Endpoint endpoint; - NBN_Connection *conn = Begin(&endpoint); - BigMessage *msg = NBN_Endpoint_CreateOutgoingMessage(&endpoint, BIG_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_RELIABLE); - - CuAssertPtrNotNull(tc, msg); - - /* Fill the "big message" with random bytes */ - for (int i = 0; i < sizeof(msg->data); i++) - msg->data[i] = rand() % 255 + 1; - - NBN_MeasureStream m_stream; - - NBN_MeasureStream_Init(&m_stream); - - NBN_Message message = { - .header = {.type = BIG_MESSAGE_TYPE, .channel_id = NBN_CHANNEL_RESERVED_RELIABLE}, - .serializer = (NBN_MessageSerializer)BigMessage_Serialize, - .data = msg}; - unsigned int message_size = (NBN_Message_Measure(&message, &m_stream) - 1) / 8 + 1; - uint8_t buffer[4096 * 8]; - NBN_WriteStream w_stream; - - NBN_WriteStream_Init(&w_stream, buffer, message_size); - - CuAssertIntEquals(tc, 0, NBN_Message_SerializeHeader( - &message.header, (NBN_Stream *)&w_stream)); - CuAssertIntEquals(tc, 0, BigMessage_Serialize(msg, (NBN_Stream *)&w_stream)); - - CuAssertIntEquals(tc, 0, NBN_Connection_EnqueueOutgoingMessage(conn)); - - /* Should have generated 5 chunks */ - CuAssertIntEquals(tc, 5, conn->send_queue->count); - - /* Merging the chunks together should reconstruct the initial message */ - uint8_t *r_buffer = malloc(message_size); /* used to merge chunks together */ - NBN_ListNode *current_node = conn->send_queue->head; - - for (int i = 0; current_node != NULL; i++) - { - NBN_Message *chunk_msg = current_node->data; - NBN_MessageChunk *chunk = ((NBN_OutgoingMessageInfo *)chunk_msg->data)->data; - - CuAssertIntEquals(tc, NBN_MESSAGE_CHUNK_TYPE, chunk_msg->header.type); - CuAssertIntEquals(tc, i, chunk->id); - CuAssertIntEquals(tc, 5, chunk->total); - - unsigned int cpy_size = MIN( - message_size - (i * NBN_MESSAGE_CHUNK_SIZE), - NBN_MESSAGE_CHUNK_SIZE); - - NBN_LogDebug("Read chunk %d (bytes: %d)", i, cpy_size); - - memcpy(r_buffer + (i * NBN_MESSAGE_CHUNK_SIZE), chunk->data, cpy_size); - - current_node = current_node->next; - } - - CuAssertIntEquals(tc, 0, memcmp(r_buffer, buffer, message_size)); - - free(r_buffer); - - End(conn, &endpoint); -} - -/* TODO: add more tests for cases like missing chunks etc. */ -void Test_NBN_Channel_AddChunk(CuTest *tc) -{ - NBN_Endpoint endpoint; - NBN_Connection *conn = Begin(&endpoint); - BigMessage *msg = NBN_Endpoint_CreateOutgoingMessage(&endpoint, BIG_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_RELIABLE); - - CuAssertPtrNotNull(tc, msg); - - /* Fill the "big message" with random bytes */ - for (int i = 0; i < sizeof(msg->data); i++) - msg->data[i] = rand() % 255 + 1; - - NBN_Connection_EnqueueOutgoingMessage(conn); - - BigMessage *msg2 = NBN_Endpoint_CreateOutgoingMessage(&endpoint, BIG_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_RELIABLE); - - CuAssertPtrNotNull(tc, msg); - - /* Fill the "big message" with random bytes */ - for (int i = 0; i < sizeof(msg2->data); i++) - msg2->data[i] = rand() % 255 + 1; - - NBN_Connection_EnqueueOutgoingMessage(conn); - - /* Should have generated 10 chunks */ - CuAssertIntEquals(tc, 10, conn->send_queue->count); - - NBN_Message *msg_chunks[10]; - - for (int i = 0; i < 10; i++) - { - NBN_Message *m = NBN_List_GetAt(conn->send_queue, i); - NBN_MessageChunk *chunk = ((NBN_OutgoingMessageInfo *)m->data)->data; - - msg_chunks[i] = NBN_Message_Create( - NBN_MESSAGE_CHUNK_TYPE, - NBN_CHANNEL_RESERVED_RELIABLE, - (NBN_MessageSerializer)NBN_MessageChunk_Serialize, - (NBN_MessageDestructor)NBN_MessageChunk_Destroy, - false, - chunk); - } - - NBN_Channel *channel = conn->channels[NBN_CHANNEL_RESERVED_RELIABLE]; - - /* First message chunks */ - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[0])); - CuAssertIntEquals(tc, 0, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 1, channel->chunk_count); - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[1])); - CuAssertIntEquals(tc, 1, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 2, channel->chunk_count); - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[2])); - CuAssertIntEquals(tc, 2, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 3, channel->chunk_count); - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[3])); - CuAssertIntEquals(tc, 3, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 4, channel->chunk_count); - - /* This is the last chunk of the first message so it should return true */ - CuAssertTrue(tc, NBN_Channel_AddChunk(channel, msg_chunks[4])); - CuAssertIntEquals(tc, -1, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 5, channel->chunk_count); - - /* Reconstruct the message so the chunks buffer gets cleared */ - NBN_Channel_ReconstructMessageFromChunks(channel, conn); - - /* Second message chunks */ - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[5])); - CuAssertIntEquals(tc, 0, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 1, channel->chunk_count); - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[6])); - CuAssertIntEquals(tc, 1, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 2, channel->chunk_count); - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[7])); - CuAssertIntEquals(tc, 2, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 3, channel->chunk_count); - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[8])); - CuAssertIntEquals(tc, 3, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 4, channel->chunk_count); - - /* This is the last chunk of the second message so it should return true */ - CuAssertTrue(tc, NBN_Channel_AddChunk(channel, msg_chunks[9])); - CuAssertIntEquals(tc, -1, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 5, channel->chunk_count); - - End(conn, &endpoint); -} - -void Test_NBN_Channel_ReconstructMessageFromChunks(CuTest *tc) -{ - NBN_Endpoint endpoint; - NBN_Connection *conn = Begin(&endpoint); - BigMessage *msg = NBN_Endpoint_CreateOutgoingMessage(&endpoint, BIG_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_RELIABLE); - - CuAssertPtrNotNull(tc, msg); - - /* Fill the "big message" with random bytes */ - for (int i = 0; i < sizeof(msg->data); i++) - msg->data[i] = rand() % 255 + 1; - - CuAssertIntEquals(tc, 0, NBN_Connection_EnqueueOutgoingMessage(conn)); - - /* Should have generated 5 chunks */ - CuAssertIntEquals(tc, 5, conn->send_queue->count); - - NBN_Message *msg_chunks[5]; - - for (int i = 0; i < 5; i++) - { - NBN_Message *m = NBN_List_GetAt(conn->send_queue, i); - NBN_MessageChunk *chunk = ((NBN_OutgoingMessageInfo *)m->data)->data; - - msg_chunks[i] = NBN_Message_Create( - NBN_MESSAGE_CHUNK_TYPE, - NBN_CHANNEL_RESERVED_RELIABLE, - (NBN_MessageSerializer)NBN_MessageChunk_Serialize, - (NBN_MessageDestructor)NBN_MessageChunk_Destroy, - false, - chunk); - } - - NBN_Channel *channel = conn->channels[NBN_CHANNEL_RESERVED_RELIABLE]; - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[0])); - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[1])); - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[2])); - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[3])); - CuAssertTrue(tc, NBN_Channel_AddChunk(channel, msg_chunks[4])); - - NBN_Message *r_msg = NBN_Channel_ReconstructMessageFromChunks(channel, conn); - - CuAssertIntEquals(tc, 0, r_msg->header.type); - CuAssertIntEquals(tc, NBN_CHANNEL_RESERVED_RELIABLE, r_msg->header.channel_id); - CuAssertIntEquals(tc, 0, memcmp(r_msg->data, msg->data, 4096)); - - End(conn, &endpoint); -} - -int main(int argc, char *argv[]) -{ - CuString *output = CuStringNew(); - CuSuite* suite = CuSuiteNew(); - - SUITE_ADD_TEST(suite, Test_ChunksGeneration); - SUITE_ADD_TEST(suite, Test_NBN_Channel_AddChunk); - SUITE_ADD_TEST(suite, Test_NBN_Channel_ReconstructMessageFromChunks); - - CuSuiteRun(suite); - CuSuiteSummary(suite, output); - CuSuiteDetails(suite, output); - - printf("%s\n", output->buffer); - - return suite->failCount; -} diff --git a/tests/serialization.c b/tests/serialization.c deleted file mode 100644 index c4375ea..0000000 --- a/tests/serialization.c +++ /dev/null @@ -1,121 +0,0 @@ -#include -#include - -#include "CuTest.h" - -#define NBNET_IMPL - -#define NBN_LogInfo printf -#define NBN_LogTrace printf -#define NBN_LogDebug printf -#define NBN_LogError printf -#define NBN_LogWarning printf - -#define NBN_Allocator malloc -#define NBN_Deallocator free - -#include "../nbnet.h" - -typedef struct -{ - float v1; - float v2; - float v3; -} BogusMessage; - -int BogusMessage_Serialize(BogusMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeFloat(stream, msg->v1, -100, 100, 1); - NBN_SerializeFloat(stream, msg->v2, -100, 100, 2); - NBN_SerializeFloat(stream, msg->v3, -100, 100, 3); - - return 0; -} - -typedef struct -{ - uint64_t v1; - uint64_t v2; - uint64_t v3; - uint64_t v4; -} BogusMessage2; - -int BogusMessage2_Serialize(BogusMessage2 *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt64(stream, msg->v1); - NBN_SerializeUInt64(stream, msg->v2); - NBN_SerializeUInt64(stream, msg->v3); - NBN_SerializeUInt64(stream, msg->v4); - - return 0; -} - -void Test_SerializeFloat(CuTest *tc) -{ - BogusMessage msg = { .v1 = 42.5, .v2 = -12.42, .v3 = -89.123 }; - NBN_WriteStream w_stream; - uint8_t buffer[32]; - - NBN_WriteStream_Init(&w_stream, buffer, sizeof(buffer)); - - CuAssertIntEquals(tc, 0, BogusMessage_Serialize(&msg, (NBN_Stream *)&w_stream)); - - NBN_WriteStream_Flush(&w_stream); - - BogusMessage r_msg; - NBN_ReadStream r_stream; - - NBN_ReadStream_Init(&r_stream, buffer, sizeof(buffer)); - - CuAssertIntEquals(tc, 0, BogusMessage_Serialize(&r_msg, (NBN_Stream *)&r_stream)); - CuAssertTrue(tc, r_msg.v1 == 42.5); - CuAssertIntEquals(tc, -1242, r_msg.v2 * 100); - CuAssertIntEquals(tc, -89123, r_msg.v3 * 1000); -} - -void Test_SerializeUInt64(CuTest *tc) -{ - BogusMessage2 msg = { - .v1 = 0xFFFFFFFFFFFFFFFF, - .v2 = 9223372036854775807, - .v3 = 4611686018427387903, - .v4 = 42000 - }; - NBN_WriteStream w_stream; - uint8_t buffer[32]; - - NBN_WriteStream_Init(&w_stream, buffer, sizeof(buffer)); - CuAssertIntEquals(tc, 0, BogusMessage2_Serialize(&msg, (NBN_Stream *)&w_stream)); - NBN_WriteStream_Flush(&w_stream); - - BogusMessage2 r_msg; - NBN_ReadStream r_stream; - - NBN_ReadStream_Init(&r_stream, buffer, sizeof(buffer)); - - CuAssertIntEquals(tc, 0, BogusMessage2_Serialize(&r_msg, (NBN_Stream *)&r_stream)); - CuAssertTrue(tc, r_msg.v1 == 0xFFFFFFFFFFFFFFFF); - CuAssertTrue(tc, r_msg.v2 == 9223372036854775807); - CuAssertTrue(tc, r_msg.v3 == 4611686018427387903); - CuAssertTrue(tc, r_msg.v4 == 42000); -} - -int main(int argc, char *argv[]) -{ - (void) argc; - (void) argv; - - CuString *output = CuStringNew(); - CuSuite* suite = CuSuiteNew(); - - SUITE_ADD_TEST(suite, Test_SerializeFloat); - SUITE_ADD_TEST(suite, Test_SerializeUInt64); - - CuSuiteRun(suite); - CuSuiteSummary(suite, output); - CuSuiteDetails(suite, output); - - printf("%s\n", output->buffer); - - return suite->failCount; -} From 58c5d7ace79a962441e31935bb6bae57e66e396f Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Tue, 27 Jan 2026 18:51:04 +0100 Subject: [PATCH 19/85] include arpa/inet.h --- nbnet.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nbnet.h b/nbnet.h index cca79da..fcf9b3c 100644 --- a/nbnet.h +++ b/nbnet.h @@ -39,6 +39,10 @@ #define NBNET_WINDOWS +#else + +#include + #endif #ifndef NBNET_WINDOWS From 8ad25c98ce0c18787965e26228f20ebc5c0f4389 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Tue, 27 Jan 2026 19:04:52 +0100 Subject: [PATCH 20/85] attempt to fix cpp compilation issues --- nbnet.h | 47 ++++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/nbnet.h b/nbnet.h index fcf9b3c..8542a91 100644 --- a/nbnet.h +++ b/nbnet.h @@ -215,9 +215,6 @@ typedef struct NBN_MessageInfo { NBN_Connection *sender; } NBN_MessageInfo; -typedef bool (*NBN_MessageAllocator)(NBN_MessageHeader, NBN_MessageType, uint8_t **); -typedef void (*NBN_MessageDeallocator)(NBN_MessageHeader, NBN_MessageType, uint8_t *); - #pragma endregion /* NBN_Message */ #pragma region NBN_Packet @@ -548,13 +545,6 @@ void NBN_PacketSimulator_Stop(NBN_PacketSimulator *); #pragma region NBN_Endpoint -typedef struct NBN_Endpoint_Config { - const char *protocol_name; - uint16_t port; - NBN_MessageAllocator msg_allocator; - NBN_MessageDeallocator msg_deallocator; -} NBN_Endpoint_Config; - struct NBN_Endpoint { NBN_EventQueue event_queue; uint32_t protocol_id; @@ -563,8 +553,6 @@ struct NBN_Endpoint { NBN_Message write_message; NBN_Writer message_writer; NBN_Reader message_reader; - NBN_MessageAllocator msg_allocator; - NBN_MessageDeallocator msg_deallocator; #ifdef NBN_DEBUG /* Debug callbacks */ @@ -592,8 +580,9 @@ enum { }; typedef struct NBN_GameClient_Config { - NBN_Endpoint_Config endpoint; + const char *protocol_name; const char *host; + uint16_t port; } NBN_GameClient_Config; typedef struct NBN_GameClient { @@ -741,7 +730,8 @@ typedef struct NBN_GameServerStats { } NBN_GameServerStats; typedef struct NBN_GameServer_Config { - NBN_Endpoint_Config endpoint; + const char *protocol_name; + uint16_t port; } NBN_GameServer_Config; typedef struct NBN_GameServer { @@ -2039,7 +2029,7 @@ bool NBN_EventQueue_IsEmpty(NBN_EventQueue *event_queue) { return event_queue->c #pragma region NBN_Endpoint -static void Endpoint_Init(NBN_Endpoint *, NBN_Endpoint_Config, uint32_t, bool); +static void Endpoint_Init(NBN_Endpoint *, uint32_t, bool); static void Endpoint_Deinit(NBN_Endpoint *); static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, uint32_t, int, void *); static uint32_t Endpoint_BuildProtocolId(const char *); @@ -2047,11 +2037,9 @@ static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *, NBN_Packet *, NBN_Conn static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN_Message *); static void Endpoint_UpdateTime(NBN_Endpoint *); -static void Endpoint_Init(NBN_Endpoint *endpoint, NBN_Endpoint_Config config, uint32_t protocol_id, bool is_server) { +static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_server) { endpoint->is_server = is_server; endpoint->protocol_id = protocol_id; - endpoint->msg_allocator = config.msg_allocator; - endpoint->msg_deallocator = config.msg_deallocator; NBN_EventQueue_Init(&endpoint->event_queue); @@ -2144,7 +2132,7 @@ static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *pa static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, uint8_t type, uint8_t channel_id) { NBN_Message *message = &endpoint->write_message; - message->header = (NBN_MessageHeader){-1, 0, type, channel_id}; + message->header = (NBN_MessageHeader){0, 0, type, channel_id}; message->sender = NULL; message->type = NBN_OUTGOING_MESSAGE; @@ -2239,9 +2227,7 @@ static int GameClient_HandleEvent(void); static int GameClient_HandleMessageReceivedEvent(void); void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port) { - nbn_game_client.config = (NBN_GameClient_Config){ - .host = host, - .endpoint = {.protocol_name = protocol_name, .port = port, .msg_allocator = NULL, .msg_deallocator = NULL}}; + nbn_game_client.config = (NBN_GameClient_Config){.host = host, .protocol_name = protocol_name, .port = port}; } NBN_Writer *NBN_GameClient_GetConnectionDataWriter(void) { @@ -2258,12 +2244,12 @@ int NBN_GameClient_Start(void) { } NBN_GameClient_Config config = nbn_game_client.config; - const char *protocol_name = config.endpoint.protocol_name; + const char *protocol_name = config.protocol_name; const char *host = config.host; - uint16_t port = config.endpoint.port; + uint16_t port = config.port; uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - Endpoint_Init(&nbn_game_client.endpoint, config.endpoint, protocol_id, false); + Endpoint_Init(&nbn_game_client.endpoint, protocol_id, false); nbn_game_client.server_connection = NULL; nbn_game_client.is_connected = false; @@ -2639,8 +2625,7 @@ static int GameServer_HandleEvent(void); static int GameServer_HandleMessageReceivedEvent(void); void NBN_GameServer_Init(const char *protocol_name, uint16_t port) { - nbn_game_server.config = (NBN_GameServer_Config){ - .endpoint = {.protocol_name = protocol_name, .port = port, .msg_allocator = NULL, .msg_deallocator = NULL}}; + nbn_game_server.config = (NBN_GameServer_Config){.protocol_name = protocol_name, .port = port}; } int NBN_GameServer_Start(void) { @@ -2650,11 +2635,11 @@ int NBN_GameServer_Start(void) { } NBN_GameServer_Config config = nbn_game_server.config; - const char *protocol_name = config.endpoint.protocol_name; - uint16_t port = config.endpoint.port; + const char *protocol_name = config.protocol_name; + uint16_t port = config.port; uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - Endpoint_Init(&nbn_game_server.endpoint, config.endpoint, protocol_id, true); + Endpoint_Init(&nbn_game_server.endpoint, protocol_id, true); if ((nbn_game_server.clients = NBN_ConnectionVector_Create()) == NULL) { NBN_LogError("Failed to create connections vector"); @@ -2865,7 +2850,7 @@ int NBN_GameServer_EnqueueBroadcastMessage(void) { if (GameServer_EnqueueMessageFor(conn, &endpoint->write_message) < 0) { NBN_LogError("Failed to send message to client %d when broadcasting", conn->id); - ret = -1; + ret = NBN_ERROR; break; } } From 0c5a3d2bab2cb16cf9d8edbcc0404defca40ab63 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Tue, 27 Jan 2026 19:07:20 +0100 Subject: [PATCH 21/85] fix more cpp issues --- nbnet.h | 2 +- soak/soak.c | 16 ---------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/nbnet.h b/nbnet.h index 8542a91..96c53f1 100644 --- a/nbnet.h +++ b/nbnet.h @@ -2227,7 +2227,7 @@ static int GameClient_HandleEvent(void); static int GameClient_HandleMessageReceivedEvent(void); void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port) { - nbn_game_client.config = (NBN_GameClient_Config){.host = host, .protocol_name = protocol_name, .port = port}; + nbn_game_client.config = (NBN_GameClient_Config){.protocol_name = protocol_name, .host = host, .port = port}; } NBN_Writer *NBN_GameClient_GetConnectionDataWriter(void) { diff --git a/soak/soak.c b/soak/soak.c index 17265ab..21e1c7f 100644 --- a/soak/soak.c +++ b/soak/soak.c @@ -231,19 +231,3 @@ int SoakMessage_Read(NBN_Reader *reader, unsigned int *msg_id, uint8_t *data, un return 0; } - -bool AllocateMessage(NBN_MessageHeader header, NBN_MessageType type, uint8_t **buffer) { - if (header.type == SOAK_MESSAGE_SMALL) { - *buffer = malloc(SOAK_MESSAGE_SMALL_MAX_DATA_LENGTH + 32); - - return true; - } - - return false; -} - -void DeallocateMessage(NBN_MessageHeader header, NBN_MessageType type, uint8_t *buffer) { - assert(header.type == SOAK_MESSAGE_SMALL); - - free(buffer); -} From e8d3bc163ee9b9e74344a4b23175d5aed2781cd5 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 28 Jan 2026 07:14:22 +0100 Subject: [PATCH 22/85] remive wincrypt.h, fix FreeBSD issue --- nbnet.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nbnet.h b/nbnet.h index 96c53f1..fe74114 100644 --- a/nbnet.h +++ b/nbnet.h @@ -33,23 +33,23 @@ #if defined(_WIN32) || defined(_WIN64) -#include #include #include #define NBNET_WINDOWS -#else - -#include - #endif #ifndef NBNET_WINDOWS +#include #include #include +#ifndef CLOCK_MONOTONIC_RAW +#define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC +#endif + #endif #pragma region Declarations From fd852c39844bbf3a20fcb7efeb5bbc58d43835f6 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 28 Jan 2026 07:21:13 +0100 Subject: [PATCH 23/85] include winsock2.h before windows.h --- nbnet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nbnet.h b/nbnet.h index fe74114..c1ea590 100644 --- a/nbnet.h +++ b/nbnet.h @@ -33,8 +33,8 @@ #if defined(_WIN32) || defined(_WIN64) -#include #include +#include #define NBNET_WINDOWS From 6a193c47a88a698402a7282138b54079c8f3c6be Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 28 Jan 2026 07:28:51 +0100 Subject: [PATCH 24/85] fix windows name collision --- examples/echo/client.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/echo/client.c b/examples/echo/client.c index 8ad4f33..2dcdaae 100644 --- a/examples/echo/client.c +++ b/examples/echo/client.c @@ -73,7 +73,7 @@ void OnMessageReceived(void) { Log(LOG_INFO, "Received echo: %s (length: %d, channel: %d)", msg_str, msg_info.length, msg_info.channel_id); } -int SendMessage(const char *msg) { +int SendEcho(const char *msg) { NBN_Writer *writer = NBN_GameClient_CreateReliableMessage(ECHO_MESSAGE_TYPE); unsigned int length = strlen(msg); @@ -96,7 +96,7 @@ int main(int argc, char *argv[]) { } const char *msg = argv[1]; - // reserve 4 bytes to write the message length in the message (see the SendMessage function) + // reserve 4 bytes to write the message length in the message (see the SendEcho function) unsigned int msg_max_len = ECHO_MESSAGE_MAX_LENGTH - 4; if (strlen(msg) > msg_max_len) { @@ -202,7 +202,7 @@ int main(int argc, char *argv[]) { break; if (connected) { - if (SendMessage(msg) < 0) { + if (SendEcho(msg) < 0) { Log(LOG_ERROR, "Failed to send message. Exit"); // Stop main loop From 0cd0eb9b58628a81e6850f29e25ec0b6cd33dbdd Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 28 Jan 2026 07:37:15 +0100 Subject: [PATCH 25/85] define WIN32_LEAN_AND_MEAN on windows --- nbnet.h | 1 + 1 file changed, 1 insertion(+) diff --git a/nbnet.h b/nbnet.h index c1ea590..7eec7da 100644 --- a/nbnet.h +++ b/nbnet.h @@ -34,6 +34,7 @@ #if defined(_WIN32) || defined(_WIN64) #include +#define WIN32_LEAN_AND_MEAN #include #define NBNET_WINDOWS From 10134d25eece6342eed5b14896fa8c40fd3750bb Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 28 Jan 2026 08:05:22 +0100 Subject: [PATCH 26/85] prevent winnt.h inclusion --- nbnet.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nbnet.h b/nbnet.h index 7eec7da..2bea94f 100644 --- a/nbnet.h +++ b/nbnet.h @@ -35,6 +35,8 @@ #include #define WIN32_LEAN_AND_MEAN +// prevent inclusion of winnt.h in windows.h +#define _WINNT_ #include #define NBNET_WINDOWS @@ -1275,7 +1277,9 @@ int NBN_Packet_Seal(NBN_Packet *packet) { } int NBN_Packet_InitRead(NBN_Packet *packet, uint32_t protocol_id, unsigned int size) { - NBN_Assert(size >= NBN_PACKET_HEADER_SIZE); + if (size < NBN_PACKET_HEADER_SIZE || size > NBN_PACKET_MAX_SIZE) { + return NBN_ERROR; + } packet->mode = NBN_PACKET_MODE_READ; packet->size = size; From 97bdb65c8d972235ecf9139f5913c2ae3ac09f00 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 28 Jan 2026 08:11:23 +0100 Subject: [PATCH 27/85] define WIN32_LEAN_AND_MEAN in the right place --- nbnet.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nbnet.h b/nbnet.h index 2bea94f..7982eca 100644 --- a/nbnet.h +++ b/nbnet.h @@ -33,10 +33,11 @@ #if defined(_WIN32) || defined(_WIN64) -#include #define WIN32_LEAN_AND_MEAN // prevent inclusion of winnt.h in windows.h #define _WINNT_ + +#include #include #define NBNET_WINDOWS From e351a296a18941d584791b45adfebb74204efafb Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 28 Jan 2026 08:11:39 +0100 Subject: [PATCH 28/85] fix webrtc js driver --- net_drivers/webrtc.h | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/net_drivers/webrtc.h b/net_drivers/webrtc.h index 694aee7..7ce7b54 100644 --- a/net_drivers/webrtc.h +++ b/net_drivers/webrtc.h @@ -33,6 +33,7 @@ freely, subject to the following restrictions: */ #include +#include #ifndef NBNET_H #include "../nbnet.h" @@ -84,11 +85,10 @@ typedef struct NBN_WebRTC_Server { int key; NBN_WebRTC_Peer *value; } *peers; - uint8_t packet_buffer[NBN_PACKET_MAX_SIZE]; uint32_t protocol_id; } NBN_WebRTC_Server; -static NBN_WebRTC_Server nbn_wrtc_serv = {NULL, {0}, 0}; +static NBN_WebRTC_Server nbn_wrtc_serv = {NULL, 0}; static NBN_WebRTC_Config nbn_wrtc_cfg; static int NBN_WebRTC_ServStart(uint32_t protocol_id, uint16_t port) { @@ -114,7 +114,7 @@ static int NBN_WebRTC_ServRecvPackets(void) { uint32_t peer_id; unsigned int len; - while ((len = __js_game_server_dequeue_packet(&peer_id, (uint8_t *)nbn_wrtc_serv.packet_buffer)) > 0) { + while ((len = __js_game_server_dequeue_packet(&peer_id, packet.buffer)) > 0) { NBN_WebRTC_Peer *peer = hmget(nbn_wrtc_serv.peers, peer_id); if (peer == NULL) { @@ -126,15 +126,14 @@ static int NBN_WebRTC_ServRecvPackets(void) { peer = (NBN_WebRTC_Peer *)malloc(sizeof(NBN_WebRTC_Peer)); peer->id = peer_id; - peer->conn = - NBN_GameServer_CreateClientConnection(NBN_WEBRTC_DRIVER_ID, peer, nbn_wrtc_serv.protocol_id, peer_id); + peer->conn = NBN_GameServer_CreateClientConnection(NBN_WEBRTC_DRIVER_ID, peer, peer_id); hmput(nbn_wrtc_serv.peers, peer_id, peer); NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, peer->conn); } - if (NBN_Packet_InitRead(&packet, peer->conn, nbn_wrtc_serv.packet_buffer, len) < 0) + if (NBN_Packet_InitRead(&packet, nbn_wrtc_serv.protocol_id, len) < 0) continue; packet.sender = peer->conn; @@ -150,8 +149,8 @@ static void NBN_WebRTC_ServRemoveClientConnection(NBN_Connection *conn) { __js_game_server_close_client_peer(conn->id); - NBN_WebRTC_Peer *peer = (NBN_WebRTC_Native_Peer *)conn->driver_data; - int ret = hmdel(nbn_wrtc_c_serv.peers, peer->id); + NBN_WebRTC_Peer *peer = (NBN_WebRTC_Peer *)conn->driver_data; + int ret = hmdel(nbn_wrtc_serv.peers, peer->id); if (ret == 1) { NBN_LogDebug("Destroyed peer %d", peer->id); @@ -179,15 +178,17 @@ NBN_EXTERN void __js_game_client_close(void); /* --- Driver implementation --- */ typedef struct NBN_WebRTC_Client { + uint32_t protocol_id; NBN_Connection *server_conn; } NBN_WebRTC_Client; -static NBN_WebRTC_Client nbn_wrtc_cli = {NULL}; +static NBN_WebRTC_Client nbn_wrtc_cli = {0, NULL}; static int NBN_WebRTC_CliStart(uint32_t protocol_id, const char *host, uint16_t port) { __js_game_client_init(protocol_id, nbn_wrtc_cfg.enable_tls); - nbn_wrtc_cli.server_conn = NBN_GameClient_CreateServerConnection(NBN_WEBRTC_DRIVER_ID, NULL, protocol_id); + nbn_wrtc_cli.protocol_id = protocol_id; + nbn_wrtc_cli.server_conn = NBN_GameClient_CreateServerConnection(NBN_WEBRTC_DRIVER_ID, NULL); int res; @@ -203,8 +204,8 @@ static int NBN_WebRTC_CliRecvPackets(void) { static NBN_Packet packet = {0}; unsigned int len; - while ((len = __js_game_client_dequeue_packet((uint8_t *)nbn_wrtc_serv.packet_buffer)) > 0) { - if (NBN_Packet_InitRead(&packet, nbn_wrtc_cli.server_conn, nbn_wrtc_serv.packet_buffer, len) < 0) + while ((len = __js_game_client_dequeue_packet(packet.buffer)) > 0) { + if (NBN_Packet_InitRead(&packet, nbn_wrtc_cli.protocol_id, len) < 0) continue; NBN_Driver_RaiseEvent(NBN_DRIVER_CLI_PACKET_RECEIVED, &packet); From 642fa463c14855b2a299455bc795dd4c3c5a8d01 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 28 Jan 2026 08:26:02 +0100 Subject: [PATCH 29/85] fix get time when compiling with emscripten --- nbnet.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/nbnet.h b/nbnet.h index 7982eca..58dde84 100644 --- a/nbnet.h +++ b/nbnet.h @@ -44,6 +44,12 @@ #endif +#ifdef __EMSCRIPTEN__ + +#include + +#endif + #ifndef NBNET_WINDOWS #include @@ -2166,8 +2172,10 @@ static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connectio } static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) { -#ifdef NBNET_WINDOWS +#if defined(NBNET_WINDOWS) endpoint->time = GetTickCount64() / 1000.0; +#elif defined(__EMSCRIPTEN__) + endpoint->time = emscripten_get_now() / 1000; #else static struct timespec tp; From 9144a2b9f20310e030ad95c84e4bcbbd690c6c04 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 28 Jan 2026 08:27:24 +0100 Subject: [PATCH 30/85] remove experimental arguments from node commands --- examples/raylib/package.json | 2 +- soak/package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/raylib/package.json b/examples/raylib/package.json index f6e1142..4476b29 100644 --- a/examples/raylib/package.json +++ b/examples/raylib/package.json @@ -1,6 +1,6 @@ { "scripts": { - "server": "node --experimental-wasm-threads --experimental-wasm-bulk-memory raylib_server.js" + "server": "node raylib_server.js" }, "dependencies": { "nbnet": "file:../../net_drivers/webrtc" diff --git a/soak/package.json b/soak/package.json index ed5a951..e3c88c3 100644 --- a/soak/package.json +++ b/soak/package.json @@ -1,7 +1,7 @@ { "scripts": { - "server": "node --experimental-wasm-threads build_web/server.js", - "client": "node --experimental-wasm-threads build_web/client.js" + "server": "node build_web/server.js", + "client": "node build_web/client.js" }, "dependencies": { "nbnet": "file:../net_drivers/webrtc" From 814bb35e3eb782eeb61e8fc8a8f7c01d7d48f243 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 28 Jan 2026 08:32:48 +0100 Subject: [PATCH 31/85] update npm dependencies --- examples/echo/package-lock.json | 1708 +----------------------- examples/raylib/package-lock.json | 1824 +------------------------- net_drivers/webrtc/package-lock.json | 697 +++------- soak/package-lock.json | 758 +---------- 4 files changed, 167 insertions(+), 4820 deletions(-) diff --git a/examples/echo/package-lock.json b/examples/echo/package-lock.json index a0b6e18..6b4d0aa 100644 --- a/examples/echo/package-lock.json +++ b/examples/echo/package-lock.json @@ -1,6 +1,6 @@ { "name": "echo", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -13,1716 +13,14 @@ "version": "1.0", "license": "MIT", "dependencies": { + "@roamhq/wrtc": "^0.8.0", "websocket": "^1.0.31", - "winston": "^3.2.1", - "wrtc": "^0.4.2" + "winston": "^3.2.1" } }, - "../../net_drivers/webrtc/node_modules/@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "../../net_drivers/webrtc/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "../../net_drivers/webrtc/node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "../../net_drivers/webrtc/node_modules/async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "../../net_drivers/webrtc/node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "../../net_drivers/webrtc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "dependencies": { - "node-gyp-build": "~3.7.0" - } - }, - "../../net_drivers/webrtc/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "../../net_drivers/webrtc/node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "../../net_drivers/webrtc/node_modules/color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "dependencies": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "../../net_drivers/webrtc/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "../../net_drivers/webrtc/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "../../net_drivers/webrtc/node_modules/color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "../../net_drivers/webrtc/node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "../../net_drivers/webrtc/node_modules/colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "dependencies": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "../../net_drivers/webrtc/node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "../../net_drivers/webrtc/node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "../../net_drivers/webrtc/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "../../net_drivers/webrtc/node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "../../net_drivers/webrtc/node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "../../net_drivers/webrtc/node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, - "../../net_drivers/webrtc/node_modules/domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "optional": true, - "dependencies": { - "webidl-conversions": "^4.0.2" - } - }, - "../../net_drivers/webrtc/node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "../../net_drivers/webrtc/node_modules/es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "dependencies": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "../../net_drivers/webrtc/node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "../../net_drivers/webrtc/node_modules/ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "dependencies": { - "type": "^2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/ext/node_modules/type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - }, - "../../net_drivers/webrtc/node_modules/fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "../../net_drivers/webrtc/node_modules/fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "../../net_drivers/webrtc/node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "../../net_drivers/webrtc/node_modules/fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "dependencies": { - "minipass": "^2.6.0" - } - }, - "../../net_drivers/webrtc/node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "../../net_drivers/webrtc/node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "../../net_drivers/webrtc/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "../../net_drivers/webrtc/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "../../net_drivers/webrtc/node_modules/ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "dependencies": { - "minimatch": "^3.0.4" - } - }, - "../../net_drivers/webrtc/node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "../../net_drivers/webrtc/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "../../net_drivers/webrtc/node_modules/ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "../../net_drivers/webrtc/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "../../net_drivers/webrtc/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dependencies": { - "number-is-nan": "^1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "../../net_drivers/webrtc/node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "../../net_drivers/webrtc/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "../../net_drivers/webrtc/node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "../../net_drivers/webrtc/node_modules/logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "dependencies": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - } - }, - "../../net_drivers/webrtc/node_modules/logform/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "../../net_drivers/webrtc/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dependencies": { - "brace-expansion": "^1.1.7" - } - }, - "../../net_drivers/webrtc/node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "../../net_drivers/webrtc/node_modules/minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dependencies": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "dependencies": { - "minipass": "^2.9.0" - } - }, - "../../net_drivers/webrtc/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dependencies": { - "minimist": "^1.2.5" - } - }, - "../../net_drivers/webrtc/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "../../net_drivers/webrtc/node_modules/needle": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", - "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", - "dependencies": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "../../net_drivers/webrtc/node_modules/needle/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "../../net_drivers/webrtc/node_modules/needle/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "../../net_drivers/webrtc/node_modules/next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "../../net_drivers/webrtc/node_modules/node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==" - }, - "../../net_drivers/webrtc/node_modules/node-pre-gyp": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", - "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", - "dependencies": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "../../net_drivers/webrtc/node_modules/nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "dependencies": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "../../net_drivers/webrtc/node_modules/npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "dependencies": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "../../net_drivers/webrtc/node_modules/npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "dependencies": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "../../net_drivers/webrtc/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "../../net_drivers/webrtc/node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "../../net_drivers/webrtc/node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "dependencies": { - "fn.name": "1.x.x" - } - }, - "../../net_drivers/webrtc/node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "../../net_drivers/webrtc/node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "../../net_drivers/webrtc/node_modules/osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dependencies": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "../../net_drivers/webrtc/node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "../../net_drivers/webrtc/node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dependencies": { - "glob": "^7.1.3" - } - }, - "../../net_drivers/webrtc/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "../../net_drivers/webrtc/node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "../../net_drivers/webrtc/node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "../../net_drivers/webrtc/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "../../net_drivers/webrtc/node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "../../net_drivers/webrtc/node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "../../net_drivers/webrtc/node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "../../net_drivers/webrtc/node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "../../net_drivers/webrtc/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "../../net_drivers/webrtc/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dependencies": { - "ansi-regex": "^2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "../../net_drivers/webrtc/node_modules/tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "dependencies": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "../../net_drivers/webrtc/node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "../../net_drivers/webrtc/node_modules/triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "../../net_drivers/webrtc/node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "../../net_drivers/webrtc/node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "dependencies": { - "node-gyp-build": "~3.7.0" - } - }, - "../../net_drivers/webrtc/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "../../net_drivers/webrtc/node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "optional": true - }, - "../../net_drivers/webrtc/node_modules/websocket": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.32.tgz", - "integrity": "sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q==", - "dependencies": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - } - }, - "../../net_drivers/webrtc/node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "../../net_drivers/webrtc/node_modules/winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "dependencies": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - } - }, - "../../net_drivers/webrtc/node_modules/winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "dependencies": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - } - }, - "../../net_drivers/webrtc/node_modules/winston-transport/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/winston-transport/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "../../net_drivers/webrtc/node_modules/winston-transport/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "../../net_drivers/webrtc/node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "../../net_drivers/webrtc/node_modules/wrtc": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.6.tgz", - "integrity": "sha512-4uD+oFoY2yuo3AV9fum3cXUXR6v8YQHZlqBrKkCRGjW1BvKrVHtLNH4UaNLBLiJu9DL89WqUWmbzsQ9RxMzANw==", - "dependencies": { - "node-pre-gyp": "^0.13.0" - }, - "optionalDependencies": { - "domexception": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - }, - "../../net_drivers/webrtc/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, "node_modules/nbnet": { "resolved": "../../net_drivers/webrtc", "link": true } - }, - "dependencies": { - "nbnet": { - "version": "file:../../net_drivers/webrtc", - "requires": { - "websocket": "^1.0.31", - "winston": "^3.2.1", - "wrtc": "^0.4.2" - }, - "dependencies": { - "@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "requires": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "optional": true, - "requires": { - "webidl-conversions": "^4.0.2" - } - }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "requires": { - "type": "^2.0.0" - }, - "dependencies": { - "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - } - } - }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "requires": { - "minipass": "^2.6.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "requires": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "needle": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", - "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==" - }, - "node-pre-gyp": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", - "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "requires": { - "fn.name": "1.x.x" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "optional": true - }, - "websocket": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.32.tgz", - "integrity": "sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q==", - "requires": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - } - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "requires": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - } - }, - "winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "requires": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "wrtc": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.6.tgz", - "integrity": "sha512-4uD+oFoY2yuo3AV9fum3cXUXR6v8YQHZlqBrKkCRGjW1BvKrVHtLNH4UaNLBLiJu9DL89WqUWmbzsQ9RxMzANw==", - "requires": { - "domexception": "^1.0.1", - "node-pre-gyp": "^0.13.0" - } - }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - } } } diff --git a/examples/raylib/package-lock.json b/examples/raylib/package-lock.json index 7c0a7f6..55a8370 100644 --- a/examples/raylib/package-lock.json +++ b/examples/raylib/package-lock.json @@ -1,6 +1,6 @@ { "name": "raylib", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -10,1833 +10,17 @@ }, "../../net_drivers/webrtc": { "name": "nbnet", - "version": "0.0.1", + "version": "1.0", "license": "MIT", "dependencies": { + "@roamhq/wrtc": "^0.8.0", "websocket": "^1.0.31", - "winston": "^3.2.1", - "wrtc": "^0.4.2" + "winston": "^3.2.1" } }, - "../../net_drivers/webrtc/node_modules/@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "../../net_drivers/webrtc/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "../../net_drivers/webrtc/node_modules/async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "../../net_drivers/webrtc/node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "../../net_drivers/webrtc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "dependencies": { - "node-gyp-build": "~3.7.0" - } - }, - "../../net_drivers/webrtc/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "../../net_drivers/webrtc/node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "dependencies": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "../../net_drivers/webrtc/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "../../net_drivers/webrtc/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "../../net_drivers/webrtc/node_modules/color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "../../net_drivers/webrtc/node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "engines": { - "node": ">=0.1.90" - } - }, - "../../net_drivers/webrtc/node_modules/colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "dependencies": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "../../net_drivers/webrtc/node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "../../net_drivers/webrtc/node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "../../net_drivers/webrtc/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "../../net_drivers/webrtc/node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "../../net_drivers/webrtc/node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "../../net_drivers/webrtc/node_modules/domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "optional": true, - "dependencies": { - "webidl-conversions": "^4.0.2" - } - }, - "../../net_drivers/webrtc/node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "../../net_drivers/webrtc/node_modules/es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "dependencies": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "../../net_drivers/webrtc/node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "../../net_drivers/webrtc/node_modules/ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "dependencies": { - "type": "^2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/ext/node_modules/type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - }, - "../../net_drivers/webrtc/node_modules/fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "../../net_drivers/webrtc/node_modules/fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "../../net_drivers/webrtc/node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "../../net_drivers/webrtc/node_modules/fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "dependencies": { - "minipass": "^2.6.0" - } - }, - "../../net_drivers/webrtc/node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "../../net_drivers/webrtc/node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "../../net_drivers/webrtc/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "../../net_drivers/webrtc/node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "../../net_drivers/webrtc/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "dependencies": { - "minimatch": "^3.0.4" - } - }, - "../../net_drivers/webrtc/node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "../../net_drivers/webrtc/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "../../net_drivers/webrtc/node_modules/ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "engines": { - "node": "*" - } - }, - "../../net_drivers/webrtc/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "../../net_drivers/webrtc/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "engines": { - "node": ">=8" - } - }, - "../../net_drivers/webrtc/node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "../../net_drivers/webrtc/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "../../net_drivers/webrtc/node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "../../net_drivers/webrtc/node_modules/logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "dependencies": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - } - }, - "../../net_drivers/webrtc/node_modules/logform/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "../../net_drivers/webrtc/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "../../net_drivers/webrtc/node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "../../net_drivers/webrtc/node_modules/minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dependencies": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "dependencies": { - "minipass": "^2.9.0" - } - }, - "../../net_drivers/webrtc/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "../../net_drivers/webrtc/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "../../net_drivers/webrtc/node_modules/needle": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", - "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", - "dependencies": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "bin": { - "needle": "bin/needle" - }, - "engines": { - "node": ">= 4.4.x" - } - }, - "../../net_drivers/webrtc/node_modules/needle/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "../../net_drivers/webrtc/node_modules/needle/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "../../net_drivers/webrtc/node_modules/next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "../../net_drivers/webrtc/node_modules/node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "../../net_drivers/webrtc/node_modules/node-pre-gyp": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", - "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", - "dependencies": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "../../net_drivers/webrtc/node_modules/nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "dependencies": { - "abbrev": "1", - "osenv": "^0.1.4" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, - "../../net_drivers/webrtc/node_modules/npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "dependencies": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "../../net_drivers/webrtc/node_modules/npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "dependencies": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "../../net_drivers/webrtc/node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "dependencies": { - "fn.name": "1.x.x" - } - }, - "../../net_drivers/webrtc/node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dependencies": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "../../net_drivers/webrtc/node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "../../net_drivers/webrtc/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "../../net_drivers/webrtc/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "../../net_drivers/webrtc/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "../../net_drivers/webrtc/node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "../../net_drivers/webrtc/node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "../../net_drivers/webrtc/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "../../net_drivers/webrtc/node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "../../net_drivers/webrtc/node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "../../net_drivers/webrtc/node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "../../net_drivers/webrtc/node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", - "engines": { - "node": "*" - } - }, - "../../net_drivers/webrtc/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "../../net_drivers/webrtc/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "dependencies": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - }, - "engines": { - "node": ">=4.5" - } - }, - "../../net_drivers/webrtc/node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "../../net_drivers/webrtc/node_modules/triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "../../net_drivers/webrtc/node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "../../net_drivers/webrtc/node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "dependencies": { - "node-gyp-build": "~3.7.0" - } - }, - "../../net_drivers/webrtc/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "../../net_drivers/webrtc/node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "optional": true - }, - "../../net_drivers/webrtc/node_modules/websocket": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.32.tgz", - "integrity": "sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q==", - "dependencies": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "../../net_drivers/webrtc/node_modules/winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "dependencies": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - }, - "engines": { - "node": ">= 6.4.0" - } - }, - "../../net_drivers/webrtc/node_modules/winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "dependencies": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - }, - "engines": { - "node": ">= 6.4.0" - } - }, - "../../net_drivers/webrtc/node_modules/winston-transport/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/winston-transport/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "../../net_drivers/webrtc/node_modules/winston-transport/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "../../net_drivers/webrtc/node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "../../net_drivers/webrtc/node_modules/wrtc": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.6.tgz", - "integrity": "sha512-4uD+oFoY2yuo3AV9fum3cXUXR6v8YQHZlqBrKkCRGjW1BvKrVHtLNH4UaNLBLiJu9DL89WqUWmbzsQ9RxMzANw==", - "dependencies": { - "domexception": "^1.0.1", - "node-pre-gyp": "^0.13.0" - }, - "engines": { - "node": "^8.11.2 || ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0" - }, - "optionalDependencies": { - "domexception": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=", - "engines": { - "node": ">=0.10.32" - } - }, - "../../net_drivers/webrtc/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, "node_modules/nbnet": { "resolved": "../../net_drivers/webrtc", "link": true } - }, - "dependencies": { - "nbnet": { - "version": "file:../../net_drivers/webrtc", - "requires": { - "websocket": "^1.0.31", - "winston": "^3.2.1", - "wrtc": "^0.4.2" - }, - "dependencies": { - "@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "requires": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "optional": true, - "requires": { - "webidl-conversions": "^4.0.2" - } - }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "requires": { - "type": "^2.0.0" - }, - "dependencies": { - "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - } - } - }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "requires": { - "minipass": "^2.6.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "requires": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "needle": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", - "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==" - }, - "node-pre-gyp": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", - "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "requires": { - "fn.name": "1.x.x" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "optional": true - }, - "websocket": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.32.tgz", - "integrity": "sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q==", - "requires": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - } - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "requires": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - } - }, - "winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "requires": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "wrtc": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.6.tgz", - "integrity": "sha512-4uD+oFoY2yuo3AV9fum3cXUXR6v8YQHZlqBrKkCRGjW1BvKrVHtLNH4UaNLBLiJu9DL89WqUWmbzsQ9RxMzANw==", - "requires": { - "domexception": "^1.0.1", - "node-pre-gyp": "^0.13.0" - } - }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - } } } diff --git a/net_drivers/webrtc/package-lock.json b/net_drivers/webrtc/package-lock.json index 5bc5b8e..e925308 100644 --- a/net_drivers/webrtc/package-lock.json +++ b/net_drivers/webrtc/package-lock.json @@ -1,7 +1,7 @@ { "name": "nbnet", "version": "1.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -18,16 +18,18 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", "engines": { "node": ">=0.1.90" } }, "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "license": "MIT", "dependencies": { - "colorspace": "1.1.x", + "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", "kuler": "^2.0.0" } @@ -36,6 +38,7 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/@roamhq/wrtc/-/wrtc-0.8.0.tgz", "integrity": "sha512-C0V/nqc4/2xzORI5qa4mIeN/8UO3ywN1kInrJ9u6GljFx0D18JMUJEqe8yYHa61RrEeoWN3PKdW++k8TocSx/A==", + "license": "BSD-2-Clause", "optionalDependencies": { "@roamhq/wrtc-darwin-arm64": "0.8.0", "@roamhq/wrtc-darwin-x64": "0.8.0", @@ -52,6 +55,7 @@ "cpu": [ "arm64" ], + "license": "BSD-2-Clause", "optional": true, "os": [ "darwin" @@ -64,6 +68,7 @@ "cpu": [ "x64" ], + "license": "BSD-2-Clause", "optional": true, "os": [ "darwin" @@ -76,6 +81,7 @@ "cpu": [ "arm64" ], + "license": "BSD-2-Clause", "optional": true, "os": [ "linux" @@ -88,6 +94,7 @@ "cpu": [ "x64" ], + "license": "BSD-2-Clause", "optional": true, "os": [ "linux" @@ -100,26 +107,40 @@ "cpu": [ "x64" ], + "license": "BSD-2-Clause", "optional": true, "os": [ "win32" ] }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, "node_modules/@types/triple-beam": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" }, "node_modules/bufferutil": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", - "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.1.0.tgz", + "integrity": "sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==", "hasInstallScript": true, + "license": "MIT", "dependencies": { "node-gyp-build": "^4.3.0" }, @@ -128,58 +149,69 @@ } }, "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", + "license": "MIT", "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "engines": { + "node": ">=18" } }, "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", + "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" } }, "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" } }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "license": "MIT", "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "license": "ISC", "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" } }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -189,6 +221,7 @@ "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", "deprecated": "Use your platform's native DOMException instead", + "license": "MIT", "optional": true, "dependencies": { "webidl-conversions": "^7.0.0" @@ -200,13 +233,15 @@ "node_modules/enabled": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" }, "node_modules/es5-ext": { "version": "0.10.64", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "hasInstallScript": true, + "license": "ISC", "dependencies": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", @@ -220,7 +255,8 @@ "node_modules/es6-iterator": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "license": "MIT", "dependencies": { "d": "1", "es5-ext": "^0.10.35", @@ -228,18 +264,23 @@ } }, "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "license": "ISC", "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" } }, "node_modules/esniff": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "license": "ISC", "dependencies": { "d": "^1.0.1", "es5-ext": "^0.10.62", @@ -250,57 +291,48 @@ "node": ">=0.10" } }, - "node_modules/esniff/node_modules/type": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", - "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==" - }, "node_modules/event-emitter": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "license": "MIT", "dependencies": { "d": "1", "es5-ext": "~0.10.14" } }, "node_modules/ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "license": "ISC", "dependencies": { - "type": "^2.0.0" + "type": "^2.7.2" } }, - "node_modules/ext/node_modules/type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - }, "node_modules/fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" }, "node_modules/fn.name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -311,17 +343,20 @@ "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" }, "node_modules/kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" }, "node_modules/logform": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", - "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", @@ -337,22 +372,26 @@ "node_modules/logform/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/next-tick": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "license": "ISC" }, "node_modules/node-gyp-build": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", - "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -363,6 +402,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", "dependencies": { "fn.name": "1.x.x" } @@ -371,6 +411,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -397,28 +438,23 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", "engines": { "node": ">=10" } }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", "engines": { "node": "*" } @@ -427,6 +463,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" } @@ -434,25 +471,29 @@ "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" }, "node_modules/triple-beam": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", "engines": { "node": ">= 14.0.0" } }, "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "license": "ISC" }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "license": "MIT", "dependencies": { "is-typedarray": "^1.0.0" } @@ -462,6 +503,7 @@ "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", "hasInstallScript": true, + "license": "MIT", "dependencies": { "node-gyp-build": "^4.3.0" }, @@ -472,12 +514,14 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", "optional": true, "engines": { "node": ">=12" @@ -487,6 +531,7 @@ "version": "1.0.35", "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", + "license": "Apache-2.0", "dependencies": { "bufferutil": "^4.0.1", "debug": "^2.2.0", @@ -500,32 +545,34 @@ } }, "node_modules/winston": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.1.tgz", - "integrity": "sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==", + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", + "license": "MIT", "dependencies": { "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", + "@dabh/diagnostics": "^2.0.8", "async": "^3.2.3", "is-stream": "^2.0.0", - "logform": "^2.6.0", + "logform": "^2.7.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", - "winston-transport": "^4.7.0" + "winston-transport": "^4.9.0" }, "engines": { "node": ">= 12.0.0" } }, "node_modules/winston-transport": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.1.tgz", - "integrity": "sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", "dependencies": { - "logform": "^2.6.1", + "logform": "^2.7.0", "readable-stream": "^3.6.2", "triple-beam": "^1.3.0" }, @@ -537,437 +584,11 @@ "version": "0.0.6", "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", "engines": { "node": ">=0.10.32" } } - }, - "dependencies": { - "@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==" - }, - "@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "@roamhq/wrtc": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc/-/wrtc-0.8.0.tgz", - "integrity": "sha512-C0V/nqc4/2xzORI5qa4mIeN/8UO3ywN1kInrJ9u6GljFx0D18JMUJEqe8yYHa61RrEeoWN3PKdW++k8TocSx/A==", - "requires": { - "@roamhq/wrtc-darwin-arm64": "0.8.0", - "@roamhq/wrtc-darwin-x64": "0.8.0", - "@roamhq/wrtc-linux-arm64": "0.8.1", - "@roamhq/wrtc-linux-x64": "0.8.1", - "@roamhq/wrtc-win32-x64": "0.8.0", - "domexception": "^4.0.0" - } - }, - "@roamhq/wrtc-darwin-arm64": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-darwin-arm64/-/wrtc-darwin-arm64-0.8.0.tgz", - "integrity": "sha512-OtV2KWO7zOG3L8TF3KCt9aucynVCD/ww2xeXXgg+FLkya3ca0uzehN8EQJ3BL4tkInksbFJ2ssyu9cehfJ3ZuA==", - "optional": true - }, - "@roamhq/wrtc-darwin-x64": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-darwin-x64/-/wrtc-darwin-x64-0.8.0.tgz", - "integrity": "sha512-VY7Vzt/SDDDCpW//h8GW9bOZrOr8gWXPZVD9473ypl4jyBIoO57yyLbHzd1G0vBUkS6szsHlQCz1WwpI30YL+g==", - "optional": true - }, - "@roamhq/wrtc-linux-arm64": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-linux-arm64/-/wrtc-linux-arm64-0.8.1.tgz", - "integrity": "sha512-FBJLLazlWkGQUXaokC/rTbrUQbb0CNFYry52fZGstufrGLTWu+g4HcwXdVvxh1tnVtVMvkQGk+mlOL52sCxw0A==", - "optional": true - }, - "@roamhq/wrtc-linux-x64": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-linux-x64/-/wrtc-linux-x64-0.8.1.tgz", - "integrity": "sha512-I9oWG7b4uvWO1IOR/aF34n+ID6TKVuSs0jd19h5KdhfRtw7FFh9xxuwN9rONPxLVa6fS0q+MCZgAf8Scz89L8Q==", - "optional": true - }, - "@roamhq/wrtc-win32-x64": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-win32-x64/-/wrtc-win32-x64-0.8.0.tgz", - "integrity": "sha512-R2fxl41BLWPiP4eaTHGLzbbVvRjx1mV/OsgINCvawO7Hwz5Zx9I45+Fhrw3hd4n5amIeSG9VIF7Kz8eeTFXTGQ==", - "optional": true - }, - "@types/triple-beam": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" - }, - "async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" - }, - "bufferutil": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", - "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", - "requires": { - "node-gyp-build": "^4.3.0" - } - }, - "color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "requires": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "requires": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "optional": true, - "requires": { - "webidl-conversions": "^7.0.0" - } - }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "dependencies": { - "type": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", - "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==" - } - } - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "requires": { - "type": "^2.0.0" - }, - "dependencies": { - "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - } - } - }, - "fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" - }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "logform": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", - "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", - "requires": { - "@colors/colors": "1.6.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, - "node-gyp-build": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", - "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==" - }, - "one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "requires": { - "fn.name": "1.x.x" - } - }, - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==" - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "requires": { - "is-arrayish": "^0.3.1" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==" - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "requires": { - "node-gyp-build": "^4.3.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "optional": true - }, - "websocket": { - "version": "1.0.35", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", - "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", - "requires": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.63", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - } - }, - "winston": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.1.tgz", - "integrity": "sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==", - "requires": { - "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.6.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.7.0" - } - }, - "winston-transport": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.1.tgz", - "integrity": "sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==", - "requires": { - "logform": "^2.6.1", - "readable-stream": "^3.6.2", - "triple-beam": "^1.3.0" - } - }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==" - } } } diff --git a/soak/package-lock.json b/soak/package-lock.json index 2aa2bdc..ddf191e 100644 --- a/soak/package-lock.json +++ b/soak/package-lock.json @@ -1,6 +1,6 @@ { "name": "soak", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -18,765 +18,9 @@ "winston": "^3.2.1" } }, - "../net_drivers/webrtc/node_modules/@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "../net_drivers/webrtc/node_modules/async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "../net_drivers/webrtc/node_modules/bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "dependencies": { - "node-gyp-build": "~3.7.0" - } - }, - "../net_drivers/webrtc/node_modules/color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "dependencies": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "../net_drivers/webrtc/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "../net_drivers/webrtc/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "../net_drivers/webrtc/node_modules/color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "../net_drivers/webrtc/node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "../net_drivers/webrtc/node_modules/colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "dependencies": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "../net_drivers/webrtc/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "../net_drivers/webrtc/node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "../net_drivers/webrtc/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "../net_drivers/webrtc/node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "../net_drivers/webrtc/node_modules/es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "dependencies": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "../net_drivers/webrtc/node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "../net_drivers/webrtc/node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "../net_drivers/webrtc/node_modules/ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "dependencies": { - "type": "^2.0.0" - } - }, - "../net_drivers/webrtc/node_modules/ext/node_modules/type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - }, - "../net_drivers/webrtc/node_modules/fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "../net_drivers/webrtc/node_modules/fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "../net_drivers/webrtc/node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "../net_drivers/webrtc/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "../net_drivers/webrtc/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "../net_drivers/webrtc/node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "../net_drivers/webrtc/node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "../net_drivers/webrtc/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "../net_drivers/webrtc/node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "../net_drivers/webrtc/node_modules/logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "dependencies": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - } - }, - "../net_drivers/webrtc/node_modules/logform/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "../net_drivers/webrtc/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "../net_drivers/webrtc/node_modules/next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "../net_drivers/webrtc/node_modules/node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==" - }, - "../net_drivers/webrtc/node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "dependencies": { - "fn.name": "1.x.x" - } - }, - "../net_drivers/webrtc/node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "../net_drivers/webrtc/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "../net_drivers/webrtc/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "../net_drivers/webrtc/node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "../net_drivers/webrtc/node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "../net_drivers/webrtc/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "../net_drivers/webrtc/node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "../net_drivers/webrtc/node_modules/triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "../net_drivers/webrtc/node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "../net_drivers/webrtc/node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "../net_drivers/webrtc/node_modules/utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "dependencies": { - "node-gyp-build": "~3.7.0" - } - }, - "../net_drivers/webrtc/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "../net_drivers/webrtc/node_modules/websocket": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.32.tgz", - "integrity": "sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q==", - "dependencies": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - } - }, - "../net_drivers/webrtc/node_modules/winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "dependencies": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - } - }, - "../net_drivers/webrtc/node_modules/winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "dependencies": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - } - }, - "../net_drivers/webrtc/node_modules/winston-transport/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "../net_drivers/webrtc/node_modules/winston-transport/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "../net_drivers/webrtc/node_modules/winston-transport/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "../net_drivers/webrtc/node_modules/yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - }, "node_modules/nbnet": { "resolved": "../net_drivers/webrtc", "link": true } - }, - "dependencies": { - "nbnet": { - "version": "file:../net_drivers/webrtc", - "requires": { - "@roamhq/wrtc": "^0.8.0", - "websocket": "^1.0.31", - "winston": "^3.2.1" - }, - "dependencies": { - "@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "requires": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "requires": { - "type": "^2.0.0" - }, - "dependencies": { - "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - } - } - }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "requires": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==" - }, - "one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "requires": { - "fn.name": "1.x.x" - } - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "websocket": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.32.tgz", - "integrity": "sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q==", - "requires": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - } - }, - "winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "requires": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - } - }, - "winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "requires": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - } - } - } } } From b91aaef9a39e86f93f556d0909f1408c838b08a2 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 28 Jan 2026 21:04:53 +0100 Subject: [PATCH 32/85] fix issue in webrtc driver js code --- net_drivers/webrtc/js/api.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net_drivers/webrtc/js/api.js b/net_drivers/webrtc/js/api.js index 207451d..510c318 100644 --- a/net_drivers/webrtc/js/api.js +++ b/net_drivers/webrtc/js/api.js @@ -66,7 +66,7 @@ mergeInto(LibraryManager.library, { }, __js_game_server_send_packet_to: function (packetPtr, packetSize, peerId) { - const data = new Uint8Array(Module.HEAPU8.subarray(packetPtr, packetPtr + packetSize)) + const data = new Uint8Array(HEAPU8.subarray(packetPtr, packetPtr + packetSize)) this.gameServer.send(data, peerId) }, @@ -128,7 +128,7 @@ mergeInto(LibraryManager.library, { }, __js_game_client_send_packet: function (packetPtr, packetSize) { - const data = new Uint8Array(Module.HEAPU8.subarray(packetPtr, packetPtr + packetSize)) + const data = new Uint8Array(HEAPU8.subarray(packetPtr, packetPtr + packetSize)) this.gameClient.send(data) }, From 989e04dcf0caf9b28c9d5731c5bc5a3a8d2c2361 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 28 Jan 2026 21:05:44 +0100 Subject: [PATCH 33/85] fix run soak pipeline script --- bin/github-actions/run_soak.sh | 75 ++++++++++++++++------------------ 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/bin/github-actions/run_soak.sh b/bin/github-actions/run_soak.sh index 0b5d143..b7efda2 100755 --- a/bin/github-actions/run_soak.sh +++ b/bin/github-actions/run_soak.sh @@ -8,21 +8,22 @@ CHANNEL_COUNT=3 MESSAGE_COUNT=500 NODE_CMD="$EMSDK_NODE" -run_client () { - node_client=$1 - echo "Running soak client (run in mode: $node_client)..." +run_client() { + client_mode=$1 + echo "Running soak client (run in mode: $client_mode)..." - if [ $node_client -eq 1 ] - then + if [ "$client_mode" = "WEBRTC_EMSCRIPTEN" ]; then # WASM WebRTC client - $NODE_CMD build_web/client.js --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &> soak_cli_out - elif [ $node_client -eq 2 ] - then + $NODE_CMD build_web/client.js --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_cli_out + elif [ "$client_mode" = "WEBRTC_NATIVE" ]; then # native WebRTC client - ./build/client --webrtc --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &> soak_cli_out - else + ./build/client --webrtc --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_cli_out + elif [ "$client_mode" = "UDP" ]; then # UDP client - ./build/client --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &> soak_cli_out + ./build/client --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_cli_out + else + echo "Unknown client mode" + return 1 fi RESULT=$? @@ -30,15 +31,11 @@ run_client () { # when running the soak test in the latest version of emscripten with node 16 # the client aborts at the end when calling emscripten_force_exit # I could not figure out why, hence the condition - [[ $node_client -eq 1 ]] && EXPECTED_RESULT=7 || EXPECTED_RESULT=0 - - if [ $RESULT -eq $EXPECTED_RESULT ] - then + [[ "$client_mode" = "WEBRTC_EMSCRIPTEN" ]] && EXPECTED_RESULT=7 || EXPECTED_RESULT=0 + + if [ $RESULT -eq $EXPECTED_RESULT ]; then echo "Soak test completed with success!" - echo "Printing the end of client logs..." - - tail -n 150 soak_cli_out - + return 0 else echo "Soak test failed! (code: $RESULT)" @@ -51,8 +48,8 @@ run_client () { fi } -exit_soak () { - kill -SIGINT $SERV_PID 2> /dev/null +exit_soak() { + kill -SIGINT $SERV_PID 2>/dev/null exit $1 } @@ -60,15 +57,13 @@ exit_soak () { cd soak echo "Starting soak server..." -if [ -n "$WEBRTC" ] -then - $NODE_CMD build_web/server.js --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &> soak_serv_out & +if [ -n "$WEBRTC" ]; then + $NODE_CMD build_web/server.js --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_serv_out & else - ./build/server --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &> soak_serv_out & + ./build/server --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_serv_out & fi -if [ $? -eq 0 ] -then +if [ $? -eq 0 ]; then SERV_PID=$! echo "Server started (PID: $SERV_PID)" @@ -80,20 +75,20 @@ fi sleep 3 -if [ -n "$WEBRTC" ] -then - run_client 1 +if [ -n "$WEBRTC" ]; then + run_client "WEBRTC_EMSCRIPTEN" exit_soak $? -else - if [ -n "$WEBRTC_NATIVE" ] - then - # run a UDP client, a webrtc WASM client (emscripten) and a native webrtc client (all connecting to the same server) - - if run_client 0 && run_client 1 && run_client 2; then - exit_soak 0 - else - exit_soak 1 - fi +elif [ -n "$WEBRTC_NATIVE" ]; then + # run a UDP client, a webrtc WASM client (emscripten) and a native webrtc client (all connecting to the same server) + + if run_client "UDP" && run_client "WEBRTC_EMSCRIPTEN" && run_client "WEBRTC_NATIVE"; then + exit_soak 0 + else + exit_soak 1 fi +else + run_client "UDP" + + exit_soak $? fi From 815f2c9b6f9b6f58ef7ef18ce8c027003adb3447 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Thu, 29 Jan 2026 22:32:43 +0100 Subject: [PATCH 34/85] WIP: rework driver architecture --- examples/raylib/client.c | 9 +- examples/raylib/server.c | 7 + examples/raylib/shared.h | 67 ++- nbnet.h | 958 +++++++++++++++++++++++++-------------- net_drivers/udp.h | 475 ------------------- soak/CMakeLists.txt | 3 + soak/client.c | 19 +- soak/server.c | 88 ++-- soak/soak.h | 3 +- 9 files changed, 693 insertions(+), 936 deletions(-) delete mode 100644 net_drivers/udp.h diff --git a/examples/raylib/client.c b/examples/raylib/client.c index 4d66f25..c86fb97 100644 --- a/examples/raylib/client.c +++ b/examples/raylib/client.c @@ -59,6 +59,12 @@ Color client_colors_to_raylib_colors[] = { PINK // CLI_PINK }; +static void WriteConnectionRequestData(const char *name) { + NBN_Writer *writer = NBN_GameClient_GetConnectionRequestDataWriter(); + + NBN_Writer_WriteString(writer, name, CLIENT_NAME_MAX_LEN); +} + static void SpawnLocalClient(int x, int y, uint32_t client_id) { TraceLog(LOG_INFO, "Spawning at (%d, %d), client id: %d", x, y, client_id); @@ -480,13 +486,14 @@ int main(int argc, char *argv[]) { #ifdef __EMSCRIPTEN__ NBN_WebRTC_Register(); // Register the WebRTC driver #else - NBN_UDP_Register(); // Register the UDP driver #endif // __EMSCRIPTEN__ // Initialize the client with a protocol name, the server host and the server port // protocol name has to be the same as the one used by the server NBN_GameClient_Init(RAYLIB_EXAMPLE_PROTOCOL_NAME, "127.0.0.1", RAYLIB_EXAMPLE_PORT); + // WriteConnectionRequestData("FOO"); + // Start the client with the configuration if (NBN_GameClient_Start() < 0) { TraceLog(LOG_WARNING, "Game client failed to start. Exit"); diff --git a/examples/raylib/server.c b/examples/raylib/server.c index 1c3dbef..2981f4c 100644 --- a/examples/raylib/server.c +++ b/examples/raylib/server.c @@ -21,6 +21,7 @@ */ #include +#include #include // For Sleep function @@ -85,6 +86,12 @@ static int HandleNewConnection(void) { conn = NBN_GameServer_GetIncomingConnection(); + // Read the connection request data transmitted by the client + // NBN_Reader *reader = NBN_GameServer_GetConnectionRequestDataReader(); + // char name[CLIENT_NAME_MAX_LEN]; + // + // NBN_Reader_ReadString(reader, name, sizeof(name)); + // Get a spawning position for the client Vector2 spawn = spawns[conn->id % MAX_CLIENTS]; diff --git a/examples/raylib/shared.h b/examples/raylib/shared.h index d85e6d3..05ea6bb 100644 --- a/examples/raylib/shared.h +++ b/examples/raylib/shared.h @@ -54,24 +54,24 @@ #define NOKERNEL // All KERNEL defines and routines #define NOUSER // All USER defines and routines /*#define NONLS // All NLS defines and routines*/ -#define NOMB // MB_* and MessageBox() -#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines -#define NOMETAFILE // typedef METAFILEPICT -#define NOMINMAX // Macros min(a,b) and max(a,b) -#define NOMSG // typedef MSG and associated routines -#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_* -#define NOSCROLL // SB_* and scrolling routines -#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc. -#define NOSOUND // Sound driver routines -#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines -#define NOWH // SetWindowsHook and WH_* -#define NOWINOFFSETS // GWL_*, GCL_*, associated routines -#define NOCOMM // COMM driver routines -#define NOKANJI // Kanji support stuff. -#define NOHELP // Help engine interface. -#define NOPROFILER // Profiler interface. -#define NODEFERWINDOWPOS // DeferWindowPos routines -#define NOMCX // Modem Configuration Extensions +#define NOMB // MB_* and MessageBox() +#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines +#define NOMETAFILE // typedef METAFILEPICT +#define NOMINMAX // Macros min(a,b) and max(a,b) +#define NOMSG // typedef MSG and associated routines +#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_* +#define NOSCROLL // SB_* and scrolling routines +#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc. +#define NOSOUND // Sound driver routines +#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines +#define NOWH // SetWindowsHook and WH_* +#define NOWINOFFSETS // GWL_*, GCL_*, associated routines +#define NOCOMM // COMM driver routines +#define NOKANJI // Kanji support stuff. +#define NOHELP // Help engine interface. +#define NOPROFILER // Profiler interface. +#define NODEFERWINDOWPOS // DeferWindowPos routines +#define NOMCX // Modem Configuration Extensions // Type required before windows.h inclusion typedef struct tagMSG *LPMSG; @@ -107,8 +107,10 @@ typedef struct tagMSG *LPMSG; #define GAME_WIDTH 800 #define GAME_HEIGHT 600 +#define CLIENT_NAME_MAX_LEN 16 // Client name maximum length + #define MIN_FLOAT_VAL -5 // Minimum value of networked client float value -#define MAX_FLOAT_VAL 5 // Maximum value of networked client float value +#define MAX_FLOAT_VAL 5 // Maximum value of networked client float value // Maximum number of connected clients at a time #define MAX_CLIENTS 4 @@ -124,28 +126,13 @@ typedef struct tagMSG *LPMSG; #define GAME_STATE_MESSAGE_MAX_LENGTH ((20 * MAX_CLIENTS) + 4) // Message ids -enum -{ - CHANGE_COLOR_MESSAGE, - UPDATE_STATE_MESSAGE, - GAME_STATE_MESSAGE -}; +enum { CHANGE_COLOR_MESSAGE, UPDATE_STATE_MESSAGE, GAME_STATE_MESSAGE }; // Client colors used for ChangeColorMessage and GameStateMessage messages -typedef enum -{ - CLI_RED, - CLI_GREEN, - CLI_BLUE, - CLI_YELLOW, - CLI_ORANGE, - CLI_PURPLE, - CLI_PINK -} ClientColor; +typedef enum { CLI_RED, CLI_GREEN, CLI_BLUE, CLI_YELLOW, CLI_ORANGE, CLI_PURPLE, CLI_PINK } ClientColor; // Client state, represents a client over the network -typedef struct -{ +typedef struct { uint32_t client_id; int x; int y; @@ -154,15 +141,13 @@ typedef struct } ClientState; // Represents the state of all clients -typedef struct -{ +typedef struct { unsigned int client_count; ClientState client_states[MAX_CLIENTS]; } GameState; // Store all options from the command line -typedef struct -{ +typedef struct { float packet_loss; float packet_duplication; float ping; diff --git a/nbnet.h b/nbnet.h index 58dde84..6cf91b9 100644 --- a/nbnet.h +++ b/nbnet.h @@ -30,9 +30,24 @@ #include #include #include +#include #if defined(_WIN32) || defined(_WIN64) +#define NBN_PLATFORM_WINDOWS + +#elif (defined(__APPLE__) && defined(__MACH__)) + +#define NBN_PLATFORM_MAC + +#else + +#define NBN_PLATFORM_UNIX + +#endif + +#if defined(NBN_PLATFORM_WINDOWS) + #define WIN32_LEAN_AND_MEAN // prevent inclusion of winnt.h in windows.h #define _WINNT_ @@ -40,8 +55,6 @@ #include #include -#define NBNET_WINDOWS - #endif #ifdef __EMSCRIPTEN__ @@ -50,7 +63,7 @@ #endif -#ifndef NBNET_WINDOWS +#ifndef NBN_PLATFORM_WINDOWS #include #include @@ -114,16 +127,7 @@ typedef struct NBN_Endpoint NBN_Endpoint; typedef struct NBN_Connection NBN_Connection; typedef struct NBN_Channel NBN_Channel; typedef struct NBN_Driver NBN_Driver; - -#pragma region NBN_ConnectionVector - -typedef struct NBN_ConnectionVector { - NBN_Connection **connections; - unsigned int count; - unsigned int capacity; -} NBN_ConnectionVector; - -#pragma endregion // NBN_ConnectionVector +typedef enum NBN_Driver_ID NBN_Driver_ID; #pragma region Serialization @@ -152,6 +156,7 @@ void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value); void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value); void NBN_Writer_WriteFloat(NBN_Writer *writer, float value); void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int length); +void NBN_Writer_WriteString(NBN_Writer *writer, const char *str, unsigned int max_len); void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length); int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value); @@ -160,6 +165,7 @@ int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value); int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value); int NBN_Reader_ReadFloat(NBN_Reader *reader, float *value); int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length); +int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len); #pragma endregion /* Serialization */ @@ -170,7 +176,7 @@ int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length #define NBN_MESSAGE_RESEND_DELAY 0.1 /* Number of seconds before a message is resent (reliable messages redundancy) */ #define NBN_MESSAGE_HEADER_SIZE 6 /* See NBN_MessageHeader struct */ #define NBN_RESERVED_MESSAGE_BUFFER_LEN 32 /* Fixed size used for all library reserved messages' buffer */ -#define NBN_MESSAGE_MAX_SIZE 512 +#define NBN_MESSAGE_MAX_SIZE 256 // IMPORTANT: make sure you update NBN_MESSAGE_HEADER_SIZE if you modify NBN_MessageHeader struct typedef struct NBN_MessageHeader { @@ -285,8 +291,8 @@ int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); #define NBN_DISCONNECTION_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 2) #define NBN_CONNECTION_REQUEST_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 3) -#define NBN_SERVER_DATA_MAX_SIZE 256 -#define NBN_CONNECTION_DATA_MAX_SIZE 256 +#define NBN_SERVER_INITIAL_DATA_MAX_SIZE 256 +#define NBN_CONNECTION_REQUEST_DATA_MAX_SIZE 256 #pragma endregion /* Library reserved messages */ @@ -299,7 +305,7 @@ int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); #define NBN_CHANNEL_COUNT 2 #endif -#define NBN_CHANNEL_BUFFER_SIZE 1024 +#define NBN_CHANNEL_BUFFER_SIZE 256 /* Library reserved unreliable ordered channel */ #define NBN_CHANNEL_RESERVED_UNRELIABLE 0 @@ -374,14 +380,20 @@ typedef enum NBN_ConnectionDebugCallback { NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE #endif /* NBN_DEBUG */ +typedef struct NBN_IPAddress { + uint32_t host; + uint16_t port; +} NBN_IPAddress; + +typedef uint64_t NBN_Connection_ID; + struct NBN_Connection { - uint32_t id; + NBN_Connection_ID id; double last_recv_packet_time; /* Used to detect stale connections */ double last_flush_time; /* Last time the send queue was flushed */ double last_read_packets_time; /* Last time packets were read from the network driver */ - unsigned int - downloaded_bytes; /* Keep track of bytes read from the socket (used for download bandwith calculation) */ - int vector_pos; /* Position of the connection in the connections vector */ + /* Keep track of bytes read from the socket (used for download bandwith calculation) */ + unsigned int downloaded_bytes; uint8_t is_accepted : 1; uint8_t is_stale : 1; uint8_t is_closed : 1; @@ -389,8 +401,14 @@ struct NBN_Connection { NBN_Driver *driver; /* Network driver used for that connection */ NBN_Channel channels[NBN_CHANNEL_COUNT]; /* Message channels (sending & receiving) */ NBN_ConnectionStats stats; - void *driver_data; /* Data attached to the connection by the underlying driver */ - void *user_data; /* Pointer to user-defined data */ + void *user_data; /* Pointer to user-defined data */ + + /* Driver-related data attached to the connection */ + union { + struct { + NBN_IPAddress ip_address; + } udp; + } driver_data; #ifdef NBN_DEBUG /* Debug callbacks */ @@ -472,9 +490,9 @@ bool NBN_EventQueue_IsEmpty(NBN_EventQueue *); * Windows headers need to be included by the user of the library before * the nbnet header because of some winsock2.h / windows.h dependencies. */ -#ifndef NBNET_WINDOWS +#ifndef NBN_PLATFORM_WINDOWS #include // Threading -#endif /* NBNET_WINDOWS */ +#endif /* NBN_PLATFORM_WINDOWS */ #define NBN_GameClient_SetPing(v) \ { nbn_game_client.endpoint.packet_simulator.ping = v; } @@ -511,7 +529,7 @@ typedef struct NBN_PacketSimulator { NBN_PacketSimulatorEntry *tail_packet; unsigned int packet_count; -#ifdef NBNET_WINDOWS +#ifdef NBN_PLATFORM_WINDOWS HANDLE queue_mutex; HANDLE thread; #else @@ -563,6 +581,10 @@ struct NBN_Endpoint { NBN_Message write_message; NBN_Writer message_writer; NBN_Reader message_reader; + uint8_t server_initial_data_buffer[NBN_SERVER_INITIAL_DATA_MAX_SIZE]; + uint8_t connection_request_data_buffer[NBN_CONNECTION_REQUEST_DATA_MAX_SIZE]; + unsigned int client_connection_request_data_len; + unsigned int server_initial_data_len; #ifdef NBN_DEBUG /* Debug callbacks */ @@ -600,9 +622,6 @@ typedef struct NBN_GameClient { NBN_GameClient_Config config; NBN_Connection *server_connection; bool is_connected; - uint8_t server_data_buffer[NBN_SERVER_DATA_MAX_SIZE]; - uint8_t client_data_buffer[NBN_CONNECTION_DATA_MAX_SIZE]; - unsigned int server_data_len; NBN_Event last_event; int closed_code; NBN_Writer client_data_writer; @@ -622,7 +641,7 @@ extern NBN_GameClient nbn_game_client; void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port); // TODO: doc -NBN_Writer *NBN_GameClient_GetConnectionDataWriter(void); +NBN_Writer *NBN_GameClient_GetConnectionRequestDataWriter(void); /** * Start the game client. @@ -678,7 +697,7 @@ NBN_Reader *NBN_GameClient_GetMessageReader(void); /** * For drivers only! NOT MEANT TO BE USED BY USER CODE. */ -NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *driver_data); +NBN_Connection *NBN_GameClient_CreateServerConnection(NBN_Driver_ID driver_id); /** * Retrieve the info about the last received message. @@ -721,8 +740,6 @@ void NBN_GameClient_Debug_RegisterCallback(NBN_ConnectionDebugCallback, void *); #pragma region NBN_GameServer -#define NBN_MAX_CLIENTS 1024 - enum { /* A new client has connected */ NBN_NEW_CONNECTION = 2, @@ -747,13 +764,13 @@ typedef struct NBN_GameServer_Config { typedef struct NBN_GameServer { NBN_Endpoint endpoint; NBN_GameServer_Config config; - NBN_ConnectionVector *clients; /* Vector of clients connections */ + struct { + NBN_Connection_ID key; + NBN_Connection *value; + } *clients; NBN_ConnectionListNode *closed_clients_head; NBN_GameServerStats stats; NBN_Event last_event; - uint8_t server_data_buffer[NBN_SERVER_DATA_MAX_SIZE]; - uint8_t client_data_buffer[NBN_CONNECTION_DATA_MAX_SIZE]; - unsigned int client_data_len; NBN_Writer server_data_writer; NBN_Reader client_data_reader; } NBN_GameServer; @@ -781,6 +798,12 @@ int NBN_GameServer_Start(void); */ void NBN_GameServer_Stop(void); +// TODO: doc +NBN_Connection *NBN_GameServer_FindConnection(NBN_Connection_ID); + +// TODO: doc +unsigned int NBN_GameServer_GetClientCount(void); + /** * Poll game server events. * @@ -803,7 +826,7 @@ int NBN_GameServer_Flush(void); /** * For drivers only! NOT MEANT TO BE USED BY USER CODE. */ -NBN_Connection *NBN_GameServer_CreateClientConnection(int, void *, uint32_t); +NBN_Connection *NBN_GameServer_CreateClientConnection(NBN_Driver_ID, NBN_Connection_ID); /** * Close a client's connection without a specific code (default code is -1) @@ -877,7 +900,7 @@ int NBN_GameServer_RejectIncomingConnection(void); NBN_Connection *NBN_GameServer_GetIncomingConnection(void); // TODO: doc -NBN_Reader *NBN_GameServer_GetConnectionDataReader(void); +NBN_Reader *NBN_GameServer_GetConnectionRequestDataReader(void); /** * Return the information about the last disconnected client. @@ -916,8 +939,6 @@ void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback, void *); #pragma region Network driver -#define NBN_MAX_DRIVERS 4 - typedef enum NBN_DriverEvent { // Client events NBN_DRIVER_CLI_PACKET_RECEIVED, @@ -927,53 +948,40 @@ typedef enum NBN_DriverEvent { NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, } NBN_DriverEvent; -typedef void (*NBN_Driver_StopFunc)(void); -typedef int (*NBN_Driver_RecvPacketsFunc)(void); +typedef int (*NBN_Driver_Func_ClientStart)(NBN_GameClient *, const char *, uint16_t); +typedef void (*NBN_Driver_Func_ClientStop)(NBN_GameClient *); +typedef int (*NBN_Driver_Func_ClientSendPacket)(NBN_GameClient *, NBN_Packet *); +typedef int (*NBN_Driver_Func_ClientRecvPackets)(NBN_GameClient *); -typedef int (*NBN_Driver_ClientStartFunc)(uint32_t, const char *, uint16_t); -typedef int (*NBN_Driver_ClientSendPacketFunc)(NBN_Packet *); +typedef int (*NBN_Driver_Func_ServerStart)(NBN_GameServer *, uint16_t); +typedef void (*NBN_Driver_Func_ServerStop)(NBN_GameServer *); +typedef int (*NBN_Driver_Func_ServerSendPacketTo)(NBN_GameServer *, NBN_Packet *, NBN_Connection *); +typedef void (*NBN_Driver_Func_ServerCleanupConnection)(NBN_GameServer *, NBN_Connection *); +typedef int (*NBN_Driver_Func_ServerRecvPackets)(NBN_GameServer *); -typedef int (*NBN_Driver_ServerStartFunc)(uint32_t, uint16_t); -typedef int (*NBN_Driver_ServerSendPacketToFunc)(NBN_Packet *, NBN_Connection *); -typedef void (*NBN_Driver_ServerRemoveConnection)(NBN_Connection *); - -typedef struct NBN_DriverImplementation { +typedef struct NBN_Driver_Implementation { /* Client functions */ - NBN_Driver_ClientStartFunc cli_start; - NBN_Driver_StopFunc cli_stop; - NBN_Driver_RecvPacketsFunc cli_recv_packets; - NBN_Driver_ClientSendPacketFunc cli_send_packet; + NBN_Driver_Func_ClientStart cli_start; + NBN_Driver_Func_ClientStop cli_stop; + NBN_Driver_Func_ClientRecvPackets cli_recv_packets; + NBN_Driver_Func_ClientSendPacket cli_send_packet; /* Server functions */ - NBN_Driver_ServerStartFunc serv_start; - NBN_Driver_StopFunc serv_stop; - NBN_Driver_RecvPacketsFunc serv_recv_packets; - NBN_Driver_ServerSendPacketToFunc serv_send_packet_to; - NBN_Driver_ServerRemoveConnection serv_remove_connection; -} NBN_DriverImplementation; + NBN_Driver_Func_ServerStart serv_start; + NBN_Driver_Func_ServerStop serv_stop; + NBN_Driver_Func_ServerRecvPackets serv_recv_packets; + NBN_Driver_Func_ServerSendPacketTo serv_send_packet_to; + NBN_Driver_Func_ServerCleanupConnection serv_cleanup_connection; +} NBN_Driver_Implementation; + +enum NBN_Driver_ID { NBN_DRIVER_UDP = 0x01 }; struct NBN_Driver { int id; const char *name; - NBN_DriverImplementation impl; + NBN_Driver_Implementation impl; }; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmissing-field-initializers" -static NBN_Driver nbn_drivers[NBN_MAX_DRIVERS] = {{-1, NULL}, {-1, NULL}, {-1, NULL}, {-1, NULL}}; -#pragma clang diagnostic pop - -static unsigned int nbn_driver_count = 0; - -/** - * Register a new network driver, at least one network driver has to be registered. - * - * @param id ID of the driver, must be unique and within 0 and NBN_MAX_DRIVERS - * @param name Name of the driver - * @param signature Driver implementation (structure containing all driver implementation function pointers) - */ -void NBN_Driver_Register(int id, const char *name, NBN_DriverImplementation implementation); - /** * Let nbnet know about specific network events happening within a network driver. * @@ -1007,92 +1015,74 @@ int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data); #pragma endregion /* Utils */ -#pragma endregion /* Declarations */ - -#endif /* NBNET_H */ - -#pragma region Implementations - -#ifdef NBNET_IMPL - -#define STB_DS_IMPLEMENTATION - -#include "stb_ds.h" +#pragma region UDP driver -#pragma region NBN_ConnectionVector +#if defined(NBN_PLATFORM_WINDOWS) -#define NBN_CONNECTION_VECTOR_INITIAL_CAPACITY 32 - -static void NBN_ConnectionVector_Grow(NBN_ConnectionVector *vector, unsigned int new_capacity); - -static NBN_ConnectionVector *NBN_ConnectionVector_Create(void) { - NBN_ConnectionVector *vector = (NBN_ConnectionVector *)malloc(sizeof(NBN_ConnectionVector)); - - vector->connections = NULL; - vector->capacity = 0; - vector->count = 0; - - NBN_ConnectionVector_Grow(vector, NBN_CONNECTION_VECTOR_INITIAL_CAPACITY); - return vector; -} - -static void NBN_ConnectionVector_Destroy(NBN_ConnectionVector *vector) { - free(vector->connections); - free(vector); -} - -static void NBN_ConnectionVector_Add(NBN_ConnectionVector *vector, NBN_Connection *conn) { - NBN_Assert(conn->vector_pos == -1); - - if (vector->count >= vector->capacity) { - NBN_ConnectionVector_Grow(vector, vector->capacity * 2); - } - - unsigned int position = vector->count; - - if (vector->connections[position]) { - NBN_LogError("Failed to add connection (id: %d) to vector: position %d is not empty", conn->id, position); - NBN_Abort(); - } - - conn->vector_pos = position; - vector->connections[position] = conn; - vector->count++; -} - -static uint32_t NBN_ConnectionVector_RemoveAt(NBN_ConnectionVector *vector, unsigned int position) { - NBN_Connection *conn = vector->connections[position]; - - if (conn == NULL) - return 0; +#include - // Make sure that connections are stored contiguously in memory +typedef int socklen_t; - NBN_Connection *last_conn = vector->connections[vector->count - 1]; +#elif defined(NBN_PLATFORM_UNIX) || defined(NBN_PLATFORM_MAC) - vector->connections[position] = last_conn; - vector->connections[vector->count - 1] = NULL; - last_conn->vector_pos = position; // last connection in the vector is moved to the position of the removed one - vector->count--; +#include +#include +#include +#include +#include +#include + +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#define closesocket(s) close(s) + +typedef int SOCKET; +typedef struct sockaddr_in SOCKADDR_IN; +typedef struct sockaddr SOCKADDR; +typedef struct in_addr IN_ADDR; + +#endif // NBN_PLATFORM_WINDOWS + +int NBN_UDP_Client_Start(NBN_GameClient *client, const char *host, uint16_t port); +void NBN_UDP_Client_Stop(NBN_GameClient *client); +int NBN_UDP_Client_RecvPackets(NBN_GameClient *client); +int NBN_UDP_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet); + +int NBN_UDP_Server_Start(NBN_GameServer *server, uint16_t port); +void NBN_UDP_Server_Stop(NBN_GameServer *server); +int NBN_UDP_Server_RecvPackets(NBN_GameServer *server); +int NBN_UDP_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *connection); +void NBN_UDP_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *connection); + +static NBN_Driver nbn_udp_driver = {.name = "UDP", + .impl = {// Client implementation + .cli_start = NBN_UDP_Client_Start, + .cli_stop = NBN_UDP_Client_Stop, + .cli_recv_packets = NBN_UDP_Client_RecvPackets, + .cli_send_packet = NBN_UDP_Client_SendPacket, + + // Server implementation + .serv_start = NBN_UDP_Server_Start, + .serv_stop = NBN_UDP_Server_Stop, + .serv_recv_packets = NBN_UDP_Server_RecvPackets, + .serv_send_packet_to = NBN_UDP_Server_SendPacketTo, + .serv_cleanup_connection = NBN_UDP_Server_CleanupConnection}}; +// TODO: remove global state +static SOCKET nbn_udp_sock; + +#pragma endregion /* UDP driver */ - return conn->id; -} +#pragma endregion /* Declarations */ -static void NBN_ConnectionVector_Grow(NBN_ConnectionVector *vector, unsigned int new_capacity) { - vector->connections = (NBN_Connection **)realloc(vector->connections, sizeof(NBN_Connection *) * new_capacity); +#endif /* NBNET_H */ - if (vector->connections == NULL) { - NBN_LogError("Failed to allocate memory to grow the connection vector"); - NBN_Abort(); - } +#pragma region Implementations - for (unsigned int i = 0; i < new_capacity - vector->capacity; i++) - vector->connections[vector->capacity + i] = NULL; +#ifdef NBN_IMPLEMENTATION - vector->capacity = new_capacity; -} +#define STB_DS_IMPLEMENTATION -#pragma endregion // NBN_ConnectionVector +#include "stb_ds.h" #pragma region Serialization @@ -1139,6 +1129,13 @@ void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int leng writer->position += length; } +void NBN_Writer_WriteString(NBN_Writer *writer, const char *str, unsigned int max_len) { + unsigned int len = strnlen(str, max_len); + + NBN_Writer_WriteUInt32(writer, max_len); + NBN_Writer_WriteBytes(writer, (uint8_t *)str, len); +} + void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length) { reader->buffer = buffer; reader->length = length; @@ -1203,6 +1200,23 @@ int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length return 0; } +int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len) { + unsigned int len; + + if (NBN_Reader_ReadUInt32(reader, &len) < 0) { + return NBN_ERROR; + } + + if (len > max_len - 1) { + return NBN_ERROR; + } + + NBN_Reader_ReadBytes(reader, (uint8_t *)str, len); + str[len] = 0; + + return 0; +} + #pragma endregion /* Serialization */ #pragma region NBN_Packet @@ -1897,7 +1911,7 @@ static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, if (connection->is_stale) return 0; - return connection->driver->impl.serv_send_packet_to(packet, connection); + return connection->driver->impl.serv_send_packet_to(&nbn_game_server, packet, connection); #endif } else { #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) @@ -1905,7 +1919,7 @@ static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, #else NBN_Driver *driver = nbn_game_client.server_connection->driver; - return driver->impl.cli_send_packet(packet); + return driver->impl.cli_send_packet(&nbn_game_client, packet); #endif } } @@ -2043,7 +2057,7 @@ bool NBN_EventQueue_IsEmpty(NBN_EventQueue *event_queue) { return event_queue->c static void Endpoint_Init(NBN_Endpoint *, uint32_t, bool); static void Endpoint_Deinit(NBN_Endpoint *); -static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, uint32_t, int, void *); +static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, NBN_Connection_ID, NBN_Driver_ID); static uint32_t Endpoint_BuildProtocolId(const char *); static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *, NBN_Packet *, NBN_Connection *); static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN_Message *); @@ -2075,12 +2089,8 @@ static void Endpoint_Deinit(NBN_Endpoint *endpoint) { #endif } -static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_t id, int driver_id, - void *driver_data) { - NBN_Driver *driver = &nbn_drivers[driver_id]; - - NBN_Assert(driver->id >= 0); - +static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, NBN_Connection_ID id, + NBN_Driver_ID driver_id) { NBN_Connection *connection = (NBN_Connection *)malloc(sizeof(NBN_Connection)); connection->id = id; @@ -2094,7 +2104,6 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_ connection->is_accepted = false; connection->is_stale = false; connection->is_closed = false; - connection->vector_pos = -1; connection->user_data = NULL; for (int i = 0; i < NBN_MAX_PACKET_ENTRIES; i++) { @@ -2105,13 +2114,22 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_ NBN_ConnectionStats stats = {0}; connection->stats = stats; - connection->driver = driver; - connection->driver_data = driver_data; for (int i = 0; i < NBN_CHANNEL_COUNT; i++) { Channel_Init(&connection->channels[i], i, NBN_CHANNEL_RELIABLE); } + switch (driver_id) { +#ifdef NBN_UDP + case NBN_DRIVER_UDP: + connection->driver = &nbn_udp_driver; + break; +#endif // NBN_UDP + default: + NBN_LogError("Unsupported driver: %d", driver_id); + NBN_Abort(); + } + return connection; } @@ -2172,7 +2190,7 @@ static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connectio } static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) { -#if defined(NBNET_WINDOWS) +#if defined(NBN_PLATFORM_WINDOWS) endpoint->time = GetTickCount64() / 1000.0; #elif defined(__EMSCRIPTEN__) endpoint->time = emscripten_get_now() / 1000; @@ -2185,7 +2203,7 @@ static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) { } endpoint->time = tp.tv_sec + (tp.tv_nsec / (double)1e9); -#endif // NBNET_WINDOWS +#endif // NBN_PLATFORM_WINDOWS } #pragma endregion /* NBN_Endpoint */ @@ -2193,27 +2211,10 @@ static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) { #pragma region Network driver static void ClientDriver_OnPacketReceived(NBN_Packet *packet); -static int ServerDriver_OnClientConnected(NBN_Connection *); +static void ServerDriver_OnClientConnected(NBN_Connection *); static int ServerDriver_OnClientPacketReceived(NBN_Packet *); -void NBN_Driver_Register(int id, const char *name, NBN_DriverImplementation implementation) { - // driver id must be valid - NBN_Assert(id >= 0 && id < NBN_MAX_DRIVERS); - - NBN_Driver *driver = &nbn_drivers[id]; - - // driver id must be unique - NBN_Assert(driver->id == -1); - - driver->id = id; - driver->name = name; - driver->impl = implementation; - - NBN_LogInfo("Registered driver (ID: %d, Name: %s)", id, name); - - nbn_driver_count++; -} - +// TODO: just have the driver call functions from this file WTF int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data) { switch (ev) { case NBN_DRIVER_CLI_PACKET_RECEIVED: @@ -2221,7 +2222,8 @@ int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data) { break; case NBN_DRIVER_SERV_CLIENT_CONNECTED: - return ServerDriver_OnClientConnected((NBN_Connection *)data); + ServerDriver_OnClientConnected((NBN_Connection *)data); + break; case NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED: return ServerDriver_OnClientPacketReceived((NBN_Packet *)data); @@ -2242,21 +2244,18 @@ static int GameClient_HandleMessageReceivedEvent(void); void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port) { nbn_game_client.config = (NBN_GameClient_Config){.protocol_name = protocol_name, .host = host, .port = port}; + + nbn_game_client.client_data_writer.position = 0; } -NBN_Writer *NBN_GameClient_GetConnectionDataWriter(void) { - NBN_Writer_Init(&nbn_game_client.client_data_writer, nbn_game_client.client_data_buffer, - sizeof(nbn_game_client.server_data_buffer)); +NBN_Writer *NBN_GameClient_GetConnectionRequestDataWriter(void) { + NBN_Writer_Init(&nbn_game_client.client_data_writer, nbn_game_client.endpoint.connection_request_data_buffer, + sizeof(nbn_game_client.endpoint.connection_request_data_buffer)); return &nbn_game_client.client_data_writer; } int NBN_GameClient_Start(void) { - if (nbn_driver_count < 1) { - NBN_LogError("At least one network driver has to be registered"); - NBN_Abort(); - } - NBN_GameClient_Config config = nbn_game_client.config; const char *protocol_name = config.protocol_name; const char *host = config.host; @@ -2265,32 +2264,37 @@ int NBN_GameClient_Start(void) { Endpoint_Init(&nbn_game_client.endpoint, protocol_id, false); - nbn_game_client.server_connection = NULL; - nbn_game_client.is_connected = false; - nbn_game_client.closed_code = -1; + int driver_count = 0; - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) { - NBN_Driver *driver = &nbn_drivers[i]; +#ifdef NBN_UDP + nbn_game_client.server_connection = NBN_GameClient_CreateServerConnection(NBN_DRIVER_UDP); - if (driver->id < 0) - continue; + if (nbn_udp_driver.impl.cli_start(&nbn_game_client, host, port) < 0) { + NBN_LogError("Failed to start driver %s", nbn_udp_driver.name); + return NBN_ERROR; + } - if (driver->impl.cli_start(protocol_id, host, port) < 0) { - NBN_LogError("Failed to start driver %s", driver->name); - return NBN_ERROR; - } + driver_count++; +#endif // NBN_UDP + + if (driver_count != 1) { + NBN_LogError("Only one network driver can be activated for the client"); + NBN_Abort(); } + nbn_game_client.is_connected = false; + nbn_game_client.closed_code = -1; + unsigned int connection_data_len = nbn_game_client.client_data_writer.position; NBN_GameClient_CreateReliableMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE); NBN_Writer *writer = NBN_GameClient_GetMessageWriter(); if (connection_data_len > 0) { - NBN_Assert(connection_data_len <= sizeof(nbn_game_client.client_data_buffer)); + NBN_Assert(connection_data_len <= sizeof(nbn_game_client.endpoint.connection_request_data_buffer)); NBN_Writer_WriteUInt32(writer, connection_data_len); - NBN_Writer_WriteBytes(writer, nbn_game_client.client_data_buffer, connection_data_len); + NBN_Writer_WriteBytes(writer, nbn_game_client.endpoint.connection_request_data_buffer, connection_data_len); } else { NBN_Writer_WriteUInt32(writer, 0); } @@ -2333,18 +2337,13 @@ void NBN_GameClient_Stop(void) { NBN_LogInfo("Stopping all drivers..."); - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) { - NBN_Driver *driver = &nbn_drivers[i]; - - if (driver->id < 0) - continue; - - driver->impl.cli_stop(); - } +#ifdef NBN_UDP + nbn_udp_driver.impl.cli_stop(&nbn_game_client); +#endif // NBN_UDP nbn_game_client.is_connected = false; nbn_game_client.closed_code = -1; - nbn_game_client.server_data_len = 0; + nbn_game_client.endpoint.server_initial_data_len = 0; Endpoint_Deinit(&nbn_game_client.endpoint); @@ -2352,8 +2351,10 @@ void NBN_GameClient_Stop(void) { } NBN_Reader *NBN_GameClient_GetServerDataReader(void) { - NBN_Reader_Init(&nbn_game_client.server_data_reader, nbn_game_client.server_data_buffer, - nbn_game_client.server_data_len); + NBN_Endpoint *endpoint = &nbn_game_client.endpoint; + + NBN_Reader_Init(&nbn_game_client.server_data_reader, endpoint->server_initial_data_buffer, + endpoint->server_initial_data_len); return &nbn_game_client.server_data_reader; } @@ -2381,17 +2382,12 @@ int NBN_GameClient_Poll(void) { if (!NBN_EventQueue_Enqueue(&endpoint->event_queue, e)) return NBN_ERROR; } else { - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) { - NBN_Driver *driver = &nbn_drivers[i]; - - if (driver->id < 0) - continue; - - if (driver->impl.cli_recv_packets() < 0) { - NBN_LogError("Failed to read packets from driver %s", driver->name); - return NBN_ERROR; - } +#ifdef NBN_UDP + if (nbn_udp_driver.impl.cli_recv_packets(&nbn_game_client) < 0) { + NBN_LogError("Failed to read packets from driver %s", nbn_udp_driver.name); + return NBN_ERROR; } +#endif // NBN_UDP NBN_Connection *server_conn = nbn_game_client.server_connection; @@ -2483,8 +2479,8 @@ NBN_Reader *NBN_GameClient_GetMessageReader(void) { return reader; } -NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *driver_data) { - NBN_Connection *server_connection = Endpoint_CreateConnection(&nbn_game_client.endpoint, 0, driver_id, driver_data); +NBN_Connection *NBN_GameClient_CreateServerConnection(NBN_Driver_ID driver_id) { + NBN_Connection *server_connection = Endpoint_CreateConnection(&nbn_game_client.endpoint, 0, driver_id); #ifdef NBN_DEBUG server_connection->OnMessageAddedToRecvQueue = nbn_game_client.endpoint.OnMessageAddedToRecvQueue; @@ -2586,20 +2582,20 @@ static int GameClient_HandleMessageReceivedEvent(void) { } if (data_length > 0) { - if (data_length > sizeof(nbn_game_client.server_data_buffer)) { - NBN_LogError("Received an invalid client accepted message"); + if (data_length > sizeof(endpoint->server_initial_data_buffer)) { + NBN_LogError("Received invalid connection data from the server"); return NBN_ERROR; } - if (NBN_Reader_ReadBytes(reader, nbn_game_client.server_data_buffer, data_length) < 0) { + if (NBN_Reader_ReadBytes(reader, endpoint->server_initial_data_buffer, data_length) < 0) { NBN_LogError("Failed to read server data"); return NBN_ERROR; } } - nbn_game_client.server_data_len = data_length; + endpoint->server_initial_data_len = data_length; nbn_game_client.is_connected = true; ret = NBN_CONNECTED; } else { @@ -2628,10 +2624,9 @@ static void ClientDriver_OnPacketReceived(NBN_Packet *packet) { NBN_GameServer nbn_game_server; static int GameServer_EnqueueMessageFor(NBN_Connection *client, NBN_Message *message); -static int GameServer_AddClient(NBN_Connection *); +static void GameServer_AddClient(NBN_Connection *); static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection); static void GameServer_AddClientToClosedList(NBN_Connection *client); -static unsigned int GameServer_GetClientCount(void); static int GameServer_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); static int GameServer_CloseStaleClientConnections(void); static void GameServer_RemoveClosedClientConnections(void); @@ -2640,14 +2635,12 @@ static int GameServer_HandleMessageReceivedEvent(void); void NBN_GameServer_Init(const char *protocol_name, uint16_t port) { nbn_game_server.config = (NBN_GameServer_Config){.protocol_name = protocol_name, .port = port}; + nbn_game_server.server_data_writer.position = 0; + + hmdefault(nbn_game_server.clients, NULL); } int NBN_GameServer_Start(void) { - if (nbn_driver_count < 1) { - NBN_LogError("At least one network driver has to be registered"); - NBN_Abort(); - } - NBN_GameServer_Config config = nbn_game_server.config; const char *protocol_name = config.protocol_name; uint16_t port = config.port; @@ -2655,23 +2648,22 @@ int NBN_GameServer_Start(void) { Endpoint_Init(&nbn_game_server.endpoint, protocol_id, true); - if ((nbn_game_server.clients = NBN_ConnectionVector_Create()) == NULL) { - NBN_LogError("Failed to create connections vector"); - NBN_Abort(); - } - nbn_game_server.closed_clients_head = NULL; - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) { - NBN_Driver *driver = &nbn_drivers[i]; + int driver_count = 0; - if (driver->id < 0) - continue; +#ifdef NBN_UDP + if (nbn_udp_driver.impl.serv_start(&nbn_game_server, port) < 0) { + NBN_LogError("Failed to start driver %s", nbn_udp_driver.name); + return NBN_ERROR; + } - if (driver->impl.serv_start(protocol_id, port) < 0) { - NBN_LogError("Failed to start driver %s", driver->name); - return NBN_ERROR; - } + driver_count++; +#endif // NBN_UDP + + if (driver_count < 1) { + NBN_LogError("At least one network driver has to be activated"); + NBN_Abort(); } NBN_LogInfo("Started (channel count: %d)", NBN_CHANNEL_COUNT); @@ -2684,23 +2676,18 @@ void NBN_GameServer_Stop(void) { while (NBN_GameServer_Poll() != NBN_NO_EVENT) { } - for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { - NBN_Connection *conn = nbn_game_server.clients->connections[i]; + for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { + NBN_Connection *conn = nbn_game_server.clients[i].value; - conn->driver->impl.serv_remove_connection(conn); + conn->driver->impl.serv_cleanup_connection(&nbn_game_server, conn); free(conn); } - NBN_ConnectionVector_Destroy(nbn_game_server.clients); + hmfree(nbn_game_server.clients); - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) { - NBN_Driver *driver = &nbn_drivers[i]; - - if (driver->id < 0) - continue; - - driver->impl.serv_stop(); - } +#ifdef NBN_UDP + nbn_udp_driver.impl.serv_stop(&nbn_game_server); +#endif // NBN_UDP // Free closed clients list NBN_ConnectionListNode *current = nbn_game_server.closed_clients_head; @@ -2719,6 +2706,19 @@ void NBN_GameServer_Stop(void) { NBN_LogInfo("Stopped"); } +static NBN_Connection_ID NBN_BuildConnectionHash(NBN_Connection_ID id, NBN_Driver_ID driver_id) { + NBN_Assert(id <= UINT64_MAX - 0xFF); + uint8_t driver_byte = NBN_DRIVER_UDP; + + return ((NBN_Connection_ID)driver_byte << 56) | id; +} + +NBN_Connection *NBN_GameServer_FindConnection(NBN_Connection_ID id) { return hmget(nbn_game_server.clients, id); } + +unsigned int NBN_GameServer_GetClientCount(void) { return hmlen(nbn_game_server.clients); } + +NBN_Connection *NBN_GameServer_GetClientByIndex(unsigned int index) { return nbn_game_server.clients[index].value; } + int NBN_GameServer_Poll(void) { Endpoint_UpdateTime(&nbn_game_server.endpoint); @@ -2728,22 +2728,17 @@ int NBN_GameServer_Poll(void) { if (GameServer_CloseStaleClientConnections() < 0) return NBN_ERROR; - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) { - NBN_Driver *driver = &nbn_drivers[i]; - - if (driver->id < 0) - continue; - - if (driver->impl.serv_recv_packets() < 0) { - NBN_LogError("Failed to read packets from driver %s", driver->name); - return NBN_ERROR; - } +#ifdef NBN_UDP + if (nbn_udp_driver.impl.serv_recv_packets(&nbn_game_server) < 0) { + NBN_LogError("Failed to read packets from driver %s", nbn_udp_driver.name); + return NBN_ERROR; } +#endif // NBN_UDP nbn_game_server.stats.download_bandwidth = 0; - for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { - NBN_Connection *client = nbn_game_server.clients->connections[i]; + for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { + NBN_Connection *client = nbn_game_server.clients[i].value; for (unsigned int i = 0; i < NBN_CHANNEL_COUNT; i++) { NBN_Channel *channel = &client->channels[i]; @@ -2786,8 +2781,8 @@ int NBN_GameServer_Flush(void) { GameServer_RemoveClosedClientConnections(); - for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { - NBN_Connection *client = nbn_game_server.clients->connections[i]; + for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { + NBN_Connection *client = nbn_game_server.clients[i].value; NBN_Assert(!(client->is_closed && client->is_stale)); @@ -2803,10 +2798,10 @@ int NBN_GameServer_Flush(void) { return 0; } -NBN_Connection *NBN_GameServer_CreateClientConnection(int driver_id, void *driver_data, uint32_t conn_id) { - NBN_Assert(conn_id > 0); // Connection IDs start at 1 - - NBN_Connection *client = Endpoint_CreateConnection(&nbn_game_server.endpoint, conn_id, driver_id, driver_data); +NBN_Connection *NBN_GameServer_CreateClientConnection(NBN_Driver_ID driver_id, NBN_Connection_ID conn_id) { + // write the driver ID to the first byte of the connection ID to avoid collisions between drivers + conn_id = NBN_BuildConnectionHash(conn_id, driver_id); + NBN_Connection *client = Endpoint_CreateConnection(&nbn_game_server.endpoint, conn_id, driver_id); #ifdef NBN_DEBUG client->OnMessageAddedToRecvQueue = nbn_game_server.endpoint.OnMessageAddedToRecvQueue; @@ -2856,8 +2851,8 @@ int NBN_GameServer_EnqueueBroadcastMessage(void) { int ret = 0; - for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { - NBN_Connection *conn = nbn_game_server.clients->connections[i]; + for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { + NBN_Connection *conn = nbn_game_server.clients[i].value; if (!conn->is_accepted || conn->is_closed) continue; @@ -2889,8 +2884,10 @@ NBN_Writer *NBN_GameServer_GetConnectionDataWriter(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); - NBN_Writer_Init(&nbn_game_server.server_data_writer, nbn_game_server.server_data_buffer, - sizeof(nbn_game_server.server_data_buffer)); + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + + NBN_Writer_Init(&nbn_game_server.server_data_writer, endpoint->server_initial_data_buffer, + sizeof(endpoint->server_initial_data_buffer)); return &nbn_game_server.server_data_writer; } @@ -2900,15 +2897,14 @@ int NBN_GameServer_AcceptIncomingConnection(void) { NBN_Assert(nbn_game_server.last_event.data.connection != NULL); unsigned data_length = nbn_game_server.server_data_writer.position; - NBN_Connection *client = nbn_game_server.last_event.data.connection; NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); if (data_length > 0) { - NBN_Assert(data_length <= NBN_SERVER_DATA_MAX_SIZE); + NBN_Assert(data_length <= sizeof(nbn_game_server.endpoint.server_initial_data_buffer)); NBN_Writer_WriteUInt32(writer, data_length); - NBN_Writer_WriteBytes(writer, nbn_game_server.server_data_buffer, data_length); + NBN_Writer_WriteBytes(writer, nbn_game_server.endpoint.server_initial_data_buffer, data_length); } else { NBN_Writer_WriteUInt32(writer, 0); } @@ -2942,11 +2938,13 @@ NBN_Connection *NBN_GameServer_GetIncomingConnection(void) { return nbn_game_server.last_event.data.connection; } -NBN_Reader *NBN_GameServer_GetConnectionDataReader(void) { +NBN_Reader *NBN_GameServer_GetConnectionRequestDataReader(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); - NBN_Reader_Init(&nbn_game_server.client_data_reader, nbn_game_server.client_data_buffer, - nbn_game_server.client_data_len); + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + + NBN_Reader_Init(&nbn_game_server.client_data_reader, endpoint->connection_request_data_buffer, + endpoint->client_connection_request_data_len); return &nbn_game_server.client_data_reader; } @@ -2999,17 +2997,11 @@ static int GameServer_EnqueueMessageFor(NBN_Connection *client, NBN_Message *mes return 0; } -static int GameServer_AddClient(NBN_Connection *client) { - if (nbn_game_server.clients->count >= NBN_MAX_CLIENTS) { - NBN_LogError("Cannot accept new client: too many clients"); - - return NBN_ERROR; - } - - NBN_ConnectionVector_Add(nbn_game_server.clients, client); - NBN_LogDebug("Added client %d", client->id); +static void GameServer_AddClient(NBN_Connection *client) { + NBN_Assert(hmgeti(nbn_game_server.clients, client->id) == -1); - return 0; + hmput(nbn_game_server.clients, client->id, client); + NBN_LogDebug("Added client %llu", client->id); } static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection) { @@ -3054,6 +3046,7 @@ static void GameServer_AddClientToClosedList(NBN_Connection *client) { if (client->is_closed) return; + // TODO: do we need to use a linked list, maybe use stb dynamic array? NBN_ConnectionListNode *node = (NBN_ConnectionListNode *)malloc(sizeof(NBN_ConnectionListNode)); node->conn = client; @@ -3075,8 +3068,6 @@ static void GameServer_AddClientToClosedList(NBN_Connection *client) { } } -static unsigned int GameServer_GetClientCount(void) { return nbn_game_server.clients->count; } - static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *client) { NBN_Event ev; @@ -3090,7 +3081,7 @@ static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio msg_info.sender = client; msg_info.data = message->data; - NBN_LogDebug("Received message (type: %d, id: %d) from client %d", message->header.type, message->header.id, + NBN_LogDebug("Received message (type: %d, id: %d) from client %lld", message->header.type, message->header.id, client->id); ev.data.message_info = msg_info; @@ -3101,11 +3092,11 @@ static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio } static int GameServer_CloseStaleClientConnections(void) { - for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) { - NBN_Connection *client = nbn_game_server.clients->connections[i]; + for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { + NBN_Connection *client = nbn_game_server.clients[i].value; if (!client->is_stale && NBN_Connection_CheckIfStale(client, nbn_game_server.endpoint.time)) { - NBN_LogInfo("Client %d connection is stale, closing it.", client->id); + NBN_LogInfo("Client %lld connection is stale, closing it.", client->id); client->is_stale = true; @@ -3130,16 +3121,11 @@ static void GameServer_RemoveClosedClientConnections(void) { if (client->is_stale) { NBN_LogDebug("Remove closed client connection (ID: %d)", client->id); - client->driver->impl.serv_remove_connection(client); // Notify the driver to remove the connection - - // Remove the connection from the connections vector and table + client->driver->impl.serv_cleanup_connection(&nbn_game_server, + client); // Notify the driver to clean up the connection - uint32_t rm_conn_id = NBN_ConnectionVector_RemoveAt(nbn_game_server.clients, client->vector_pos); - - if (rm_conn_id != client->id) { - NBN_LogError("Failed to remove client connection from connections vector"); - NBN_Abort(); - } + int ret = hmdel(nbn_game_server.clients, client->id); + NBN_Assert(ret == 1); // Destroy the connection @@ -3203,6 +3189,7 @@ static int GameServer_HandleMessageReceivedEvent(void) { } if (message_info.type != NBN_CONNECTION_REQUEST_MESSAGE_TYPE) { + nbn_game_server.server_data_writer.position = 0; return NBN_CLIENT_MESSAGE_RECEIVED; } @@ -3225,20 +3212,20 @@ static int GameServer_HandleMessageReceivedEvent(void) { } if (data_length > 0) { - if (data_length > sizeof(nbn_game_server.client_data_buffer)) { - NBN_LogError("Invalid client data length"); + if (data_length > sizeof(endpoint->connection_request_data_buffer)) { + NBN_LogError("Received invalid connection request data"); return NBN_ERROR; } - if (NBN_Reader_ReadBytes(reader, nbn_game_server.client_data_buffer, data_length) < 0) { + if (NBN_Reader_ReadBytes(reader, nbn_game_server.endpoint.connection_request_data_buffer, data_length) < 0) { NBN_LogError("Failed to read client data"); return NBN_ERROR; } } - nbn_game_server.client_data_len = data_length; + nbn_game_server.endpoint.client_connection_request_data_len = data_length; NBN_Event e; @@ -3255,27 +3242,310 @@ static int GameServer_HandleMessageReceivedEvent(void) { #pragma region Game server driver -static int ServerDriver_OnClientConnected(NBN_Connection *client) { - if (GameServer_AddClient(client) < 0) { - NBN_LogError("Failed to add client"); +static void ServerDriver_OnClientConnected(NBN_Connection *client) { GameServer_AddClient(client); } + +static int ServerDriver_OnClientPacketReceived(NBN_Packet *packet) { + if (Endpoint_ProcessReceivedPacket(&nbn_game_server.endpoint, packet, packet->sender) < 0) { + NBN_LogError("An error occured while processing packet from client %d, closing the client", packet->sender->id); + + return GameServer_CloseClientWithCode(packet->sender, -1, false); + } + + return 0; +} + +#pragma endregion /* Game server driver */ + +#ifdef NBN_UDP + +#pragma region UDP driver + +#ifdef NBN_PLATFORM_WINDOWS + +static char err_msg[32]; + +#endif + +static int NBN_UDP_InitSocket(void); +static void NBN_UDP_DeinitSocket(void); +static int NBN_UDP_BindSocket(uint16_t); +static char *NBN_UDP_GetLastErrorMessage(void); + +static int NBN_UDP_InitSocket(void) { +#ifdef NBN_PLATFORM_WINDOWS + WSADATA wsa; + int err = WSAStartup(MAKEWORD(2, 2), &wsa); + if (err < 0) { + NBN_LogError("WSAStartup() failed"); return NBN_ERROR; } +#endif + + if ((nbn_udp_sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) + return NBN_ERROR; + +#if defined(NBN_PLATFORM_WINDOWS) + DWORD non_blocking = 1; + + if (ioctlsocket(nbn_udp_sock, FIONBIO, &non_blocking) != 0) { + NBN_LogError("ioctlsocket() failed: %s", GetLastErrorMessage()); + + return NBN_ERROR; + } +#elif defined(NBN_PLATFORM_MAC) || defined(NBN_PLATFORM_UNIX) + int non_blocking = 1; + + if (fcntl(nbn_udp_sock, F_SETFL, O_NONBLOCK, non_blocking) < 0) { + NBN_LogError("fcntl() failed: %s", NBN_UDP_GetLastErrorMessage()); + + return NBN_ERROR; + } +#endif return 0; } -static int ServerDriver_OnClientPacketReceived(NBN_Packet *packet) { - if (Endpoint_ProcessReceivedPacket(&nbn_game_server.endpoint, packet, packet->sender) < 0) { - NBN_LogError("An error occured while processing packet from client %d, closing the client", packet->sender->id); +static void NBN_UDP_DeinitSocket(void) { + closesocket(nbn_udp_sock); - return GameServer_CloseClientWithCode(packet->sender, -1, false); +#ifdef NBN_PLATFORM_WINDOWS + WSACleanup(); +#endif +} + +static int NBN_UDP_BindSocket(uint16_t port) { + SOCKADDR_IN sin; + + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + + if (bind(nbn_udp_sock, (SOCKADDR *)&sin, sizeof(sin)) < 0) { + NBN_LogError("bind() failed: %s", NBN_UDP_GetLastErrorMessage()); + + return NBN_ERROR; } return 0; } -#pragma endregion /* Game server driver */ +static NBN_Connection_ID NBN_UDP_BuildConnectionID(NBN_IPAddress address) { + return ((NBN_Connection_ID)address.host << 2) | address.port; +} + +static NBN_Connection *NBN_UDP_FindOrCreateClientConnectionByAddress(NBN_IPAddress address) { + NBN_Connection_ID conn_id = NBN_UDP_BuildConnectionID(address); + conn_id = NBN_BuildConnectionHash(conn_id, NBN_DRIVER_UDP); + NBN_Connection *conn = NBN_GameServer_FindConnection(conn_id); + + if (conn == NULL) { + // this is a new connection + + conn = NBN_GameServer_CreateClientConnection(NBN_DRIVER_UDP, conn_id); + conn->driver_data.udp.ip_address = address; + + NBN_LogDebug("New UDP connection (id: %llu, addr: %d, port: %d)", conn->id, address.host, address.port); + + if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, conn) < 0) { + NBN_LogError("Failed to raise game server event"); + + return NULL; + } + + return conn; + } + + return conn; +} + +static int NBN_UDP_ParseIpAddress(const char *host, uint16_t port, NBN_IPAddress *address) { + // TODO: need to malloc here? + size_t host_len = strlen(host); + char *dup_host = (char *)malloc(host_len + 1); + memcpy(dup_host, host, host_len + 1); + uint8_t arr[4]; + + for (int i = 0; i < 4; i++) { + char *s; + + // TODO: replace strtok with strsep + if ((s = strtok(i == 0 ? dup_host : NULL, ".")) == NULL) + return NBN_ERROR; + + char *end = NULL; + int v = strtol(s, &end, 10); + + if (end == s || v < 0 || v > 255) + return NBN_ERROR; + + arr[i] = (uint8_t)v; + } + + address->host = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3]; + address->port = port; + + free(dup_host); + + return 0; +} + +static char *NBN_UDP_GetLastErrorMessage(void) { +#ifdef NBN_PLATFORM_WINDOWS + snprintf(err_msg, sizeof(err_msg), "%d", WSAGetLastError()); + + return err_msg; +#else + return strerror(errno); +#endif +} + +int NBN_UDP_Server_Start(NBN_GameServer *server, uint16_t port) { + if (NBN_UDP_InitSocket() < 0) + return NBN_ERROR; + + if (NBN_UDP_BindSocket(port) < 0) + return NBN_ERROR; + + return 0; +} + +void NBN_UDP_Server_Stop(NBN_GameServer *server) { NBN_UDP_DeinitSocket(); } + +int NBN_UDP_Server_RecvPackets(NBN_GameServer *server) { + NBN_Packet packet = {0}; + SOCKADDR_IN src_addr; + socklen_t src_addr_len = sizeof(src_addr); + + while (true) { + int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, + &src_addr_len); + + if (bytes <= 0) + break; + + if (bytes <= NBN_PACKET_HEADER_SIZE) + continue; + + if (NBN_Packet_InitRead(&packet, server->endpoint.protocol_id, bytes) < 0) { + NBN_LogDebug("Discarded invalid packet"); + continue; + } + + NBN_IPAddress ip_address; + ip_address.host = ntohl(src_addr.sin_addr.s_addr); + ip_address.port = ntohs(src_addr.sin_port); + + NBN_LogDebug("Received valid UDP packet from %d:%d", ip_address.host, ip_address.port); + + packet.sender = NBN_UDP_FindOrCreateClientConnectionByAddress(ip_address); + + if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet) < 0) { + NBN_LogError("Failed to raise game server event"); + + return NBN_ERROR; + } + } + + return 0; +} + +void NBN_UDP_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *connection) {} + +int NBN_UDP_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *connection) { + NBN_IPAddress dest_address = connection->driver_data.udp.ip_address; + SOCKADDR_IN dest_addr; + + dest_addr.sin_addr.s_addr = htonl(dest_address.host); + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(dest_address.port); + + if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, + sizeof(dest_addr)) == SOCKET_ERROR) { + NBN_LogError("sendto() failed: %s", NBN_UDP_GetLastErrorMessage()); + + return NBN_ERROR; + } + + return 0; +} + +int NBN_UDP_Client_Start(NBN_GameClient *client, const char *host, uint16_t port) { + NBN_IPAddress *ip_address = &client->server_connection->driver_data.udp.ip_address; + + if (NBN_UDP_ParseIpAddress(host, port, ip_address) < 0) { + NBN_LogError("Failed to resolve IP address from %s", host); + + return NBN_ERROR; + } + + if (NBN_UDP_InitSocket() < 0) + return NBN_ERROR; + + if (NBN_UDP_BindSocket(0) < 0) + return NBN_ERROR; + + return 0; +} + +void NBN_UDP_Client_Stop(NBN_GameClient *client) { NBN_UDP_DeinitSocket(); } + +int NBN_UDP_Client_RecvPackets(NBN_GameClient *client) { + NBN_IPAddress server_address = client->server_connection->driver_data.udp.ip_address; + static NBN_Packet packet = {0}; + SOCKADDR_IN src_addr; + socklen_t src_addr_len = sizeof(src_addr); + + while (true) { + int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, + &src_addr_len); + + if (bytes <= 0) + break; + + if (bytes < NBN_PACKET_HEADER_SIZE) + continue; + + uint32_t host = ntohl(src_addr.sin_addr.s_addr); + uint16_t port = ntohs(src_addr.sin_port); + + if (host != server_address.host || port != server_address.port) + continue; + + if (NBN_Packet_InitRead(&packet, client->endpoint.protocol_id, bytes) < 0) { + NBN_LogDebug("Discarded invalid packet"); + continue; + } + + packet.sender = client->server_connection; + + NBN_Driver_RaiseEvent(NBN_DRIVER_CLI_PACKET_RECEIVED, &packet); + } + + return 0; +} + +int NBN_UDP_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet) { + NBN_IPAddress server_address = client->server_connection->driver_data.udp.ip_address; + SOCKADDR_IN dest_addr; + + dest_addr.sin_addr.s_addr = htonl(server_address.host); + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(server_address.port); + + if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, + sizeof(dest_addr)) == SOCKET_ERROR) { + NBN_LogError("sendto() failed: %s", NBN_UDP_GetLastErrorMessage()); + + return NBN_ERROR; + } + + return 0; +} + +#pragma enregion /* UDP driver */ + +#endif // NBN_UDP #pragma region Packet simulator @@ -3284,7 +3554,7 @@ static int ServerDriver_OnClientPacketReceived(NBN_Packet *packet) { #define RAND_RATIO_BETWEEN(min, max) (((rand() % (int)((max * 100.f) - (min * 100.f) + 1)) + (min * 100.f)) / 100.f) #define RAND_RATIO RAND_RATIO_BETWEEN(0, 1) -#ifdef NBNET_WINDOWS +#ifdef NBN_PLATFORM_WINDOWS DWORD WINAPI PacketSimulator_Routine(LPVOID); #else static void *PacketSimulator_Routine(void *); @@ -3305,7 +3575,7 @@ void NBN_PacketSimulator_Init(NBN_PacketSimulator *packet_simulator, NBN_Endpoin packet_simulator->tail_packet = NULL; packet_simulator->packet_count = 0; -#ifdef NBNET_WINDOWS +#ifdef NBN_PLATFORM_WINDOWS packet_simulator->queue_mutex = CreateMutex(NULL, FALSE, NULL); #else packet_simulator->queue_mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER; @@ -3314,7 +3584,7 @@ void NBN_PacketSimulator_Init(NBN_PacketSimulator *packet_simulator, NBN_Endpoin int NBN_PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN_Packet *packet, NBN_Connection *receiver) { -#ifdef NBNET_WINDOWS +#ifdef NBN_PLATFORM_WINDOWS WaitForSingleObject(packet_simulator->queue_mutex, INFINITE); #else pthread_mutex_lock(&packet_simulator->queue_mutex); @@ -3353,7 +3623,7 @@ int NBN_PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN packet_simulator->packet_count++; -#ifdef NBNET_WINDOWS +#ifdef NBN_PLATFORM_WINDOWS ReleaseMutex(packet_simulator->queue_mutex); #else pthread_mutex_unlock(&packet_simulator->queue_mutex); @@ -3363,7 +3633,7 @@ int NBN_PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN } void NBN_PacketSimulator_Start(NBN_PacketSimulator *packet_simulator) { -#ifdef NBNET_WINDOWS +#ifdef NBN_PLATFORM_WINDOWS packet_simulator->thread = CreateThread(NULL, 0, PacketSimulator_Routine, packet_simulator, 0, NULL); #else pthread_create(&packet_simulator->thread, NULL, PacketSimulator_Routine, packet_simulator); @@ -3375,14 +3645,14 @@ void NBN_PacketSimulator_Start(NBN_PacketSimulator *packet_simulator) { void NBN_PacketSimulator_Stop(NBN_PacketSimulator *packet_simulator) { packet_simulator->running = false; -#ifdef NBNET_WINDOWS +#ifdef NBN_PLATFORM_WINDOWS WaitForSingleObject(packet_simulator->thread, INFINITE); #else pthread_join(packet_simulator->thread, NULL); #endif } -#ifdef NBNET_WINDOWS +#ifdef NBN_PLATFORM_WINDOWS DWORD WINAPI PacketSimulator_Routine(LPVOID arg) #else static void *PacketSimulator_Routine(void *arg) @@ -3391,7 +3661,7 @@ static void *PacketSimulator_Routine(void *arg) NBN_PacketSimulator *packet_simulator = (NBN_PacketSimulator *)arg; while (packet_simulator->running) { -#ifdef NBNET_WINDOWS +#ifdef NBN_PLATFORM_WINDOWS WaitForSingleObject(packet_simulator->queue_mutex, INFINITE); #else pthread_mutex_lock(&packet_simulator->queue_mutex); @@ -3447,14 +3717,14 @@ static void *PacketSimulator_Routine(void *arg) entry = next; } -#ifdef NBNET_WINDOWS +#ifdef NBN_PLATFORM_WINDOWS ReleaseMutex(packet_simulator->queue_mutex); #else pthread_mutex_unlock(&packet_simulator->queue_mutex); #endif } -#ifdef NBNET_WINDOWS +#ifdef NBN_PLATFORM_WINDOWS return 0; #else return NULL; @@ -3477,9 +3747,9 @@ static int PacketSimulator_SendPacket(NBN_PacketSimulator *packet_simulator, NBN if (receiver->is_stale) return 0; - return driver->impl.serv_send_packet_to(packet, receiver); + return driver->impl.serv_send_packet_to(&nbn_game_server, packet, receiver); } else { - return driver->impl.cli_send_packet(packet); + return driver->impl.cli_send_packet(&nbn_game_client, packet); } } @@ -3494,6 +3764,6 @@ static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimu #pragma endregion /* Packet simulator */ -#endif /* NBNET_IMPL */ +#endif /* NBN_IMPLEMENTATION */ #pragma endregion /* Implementations */ diff --git a/net_drivers/udp.h b/net_drivers/udp.h deleted file mode 100644 index 1ec0dec..0000000 --- a/net_drivers/udp.h +++ /dev/null @@ -1,475 +0,0 @@ -/* - -Copyright (C) 2024 BIAGINI Nathan - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -*/ - -/* - --- NBNET UDP DRIVER --- - - Portable single UDP socket network driver for the nbnet library. - - How to use: - - 1. Include this header *once* after the nbnet header in the same file where you defined the NBNET_IMPL macro - 2. Call NBN_UDP_Register in both your client and server code before calling NBN_GameClient_Start or - NBN_GameServer_Start -*/ - -void NBN_UDP_Register(void); - -#ifdef NBNET_IMPL - -#include -#include -#include -#include -#include -#include -#include - -#ifndef NBNET_H -#include "../nbnet.h" -#endif - -#define NBN_UDP_DRIVER_ID 0 -#define NBN_UDP_DRIVER_NAME "UDP" - -#pragma region Platform detection - -#if defined(_WIN32) || defined(_WIN64) -#ifndef PLATFORM_WINDOWS -#define PLATFORM_WINDOWS -#endif -#elif (defined(__APPLE__) && defined(__MACH__)) -#define PLATFORM_MAC -#else -#define PLATFORM_UNIX -#endif - -#pragma endregion /* Platform detection */ - -#if defined(PLATFORM_WINDOWS) - -#include - -typedef int socklen_t; - -#elif defined(PLATFORM_UNIX) || defined(PLATFORM_MAC) - -#include -#include -#include -#include -#include -#include - -#define INVALID_SOCKET -1 -#define SOCKET_ERROR -1 -#define closesocket(s) close(s) - -typedef int SOCKET; -typedef struct sockaddr_in SOCKADDR_IN; -typedef struct sockaddr SOCKADDR; -typedef struct in_addr IN_ADDR; - -#endif - -typedef struct { - uint32_t host; - uint16_t port; -} NBN_IPAddress; - -typedef struct { - uint32_t id; - NBN_IPAddress address; - NBN_Connection *conn; // nbnet connection associated to this UDP connection -} NBN_UDP_Connection; - -static SOCKET nbn_udp_sock; - -#pragma region Socket functions - -#ifdef PLATFORM_WINDOWS - -static char err_msg[32]; - -#endif - -static int InitSocket(void); -static void DeinitSocket(void); -static int BindSocket(uint16_t); -static char *GetLastErrorMessage(void); - -static int InitSocket(void) { -#ifdef PLATFORM_WINDOWS - WSADATA wsa; - int err = WSAStartup(MAKEWORD(2, 2), &wsa); - if (err < 0) { - NBN_LogError("WSAStartup() failed"); - - return NBN_ERROR; - } -#endif - - if ((nbn_udp_sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) - return NBN_ERROR; - -#if defined(PLATFORM_WINDOWS) - DWORD non_blocking = 1; - - if (ioctlsocket(nbn_udp_sock, FIONBIO, &non_blocking) != 0) { - NBN_LogError("ioctlsocket() failed: %s", GetLastErrorMessage()); - - return NBN_ERROR; - } -#elif defined(PLATFORM_MAC) || defined(PLATFORM_UNIX) - int non_blocking = 1; - - if (fcntl(nbn_udp_sock, F_SETFL, O_NONBLOCK, non_blocking) < 0) { - NBN_LogError("fcntl() failed: %s", GetLastErrorMessage()); - - return NBN_ERROR; - } -#endif - - return 0; -} - -static void DeinitSocket(void) { - closesocket(nbn_udp_sock); - -#ifdef PLATFORM_WINDOWS - WSACleanup(); -#endif -} - -static int BindSocket(uint16_t port) { - SOCKADDR_IN sin; - - sin.sin_addr.s_addr = htonl(INADDR_ANY); - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - - if (bind(nbn_udp_sock, (SOCKADDR *)&sin, sizeof(sin)) < 0) { - NBN_LogError("bind() failed: %s", GetLastErrorMessage()); - - return NBN_ERROR; - } - - return 0; -} - -static int ResolveIpAddress(const char *host, uint16_t port, NBN_IPAddress *address) { - size_t host_len = strlen(host); - char *dup_host = (char *)malloc(host_len + 1); - memcpy(dup_host, host, host_len + 1); - uint8_t arr[4]; - - for (int i = 0; i < 4; i++) { - char *s; - - // TODO: replace strtok with strsep - if ((s = strtok(i == 0 ? dup_host : NULL, ".")) == NULL) - return NBN_ERROR; - - char *end = NULL; - int v = strtol(s, &end, 10); - - if (end == s || v < 0 || v > 255) - return NBN_ERROR; - - arr[i] = (uint8_t)v; - } - - address->host = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3]; - address->port = port; - - free(dup_host); - - return 0; -} - -static char *GetLastErrorMessage(void) { -#ifdef PLATFORM_WINDOWS - snprintf(err_msg, sizeof(err_msg), "%d", WSAGetLastError()); - - return err_msg; -#else - return strerror(errno); -#endif -} - -#pragma endregion /* Socket functions */ - -#pragma region Game server - -typedef struct NBN_UDP_Server { - struct { - NBN_IPAddress key; - NBN_UDP_Connection *value; - } *connections; - uint32_t next_conn_id; // nbnet connection ids, starts at 1 - uint32_t protocol_id; -} NBN_UDP_Server; - -static NBN_UDP_Server nbn_udp_serv = {NULL, 1, 0}; - -static NBN_Connection *FindOrCreateClientConnectionByAddress(NBN_IPAddress); - -static int NBN_UDP_ServStart(uint32_t protocol_id, uint16_t port) { - nbn_udp_serv.protocol_id = protocol_id; - - hmdefault(nbn_udp_serv.connections, NULL); - - if (InitSocket() < 0) - return NBN_ERROR; - - if (BindSocket(port) < 0) - return NBN_ERROR; - - return 0; -} - -static void NBN_UDP_ServStop(void) { - hmfree(nbn_udp_serv.connections); - DeinitSocket(); -} - -static int NBN_UDP_ServRecvPackets(void) { - NBN_Packet packet = {0}; - SOCKADDR_IN src_addr; - socklen_t src_addr_len = sizeof(src_addr); - NBN_IPAddress ip_address; - - while (true) { - int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, - &src_addr_len); - - if (bytes <= 0) - break; - - if (bytes <= NBN_PACKET_HEADER_SIZE) - continue; - - if (NBN_Packet_InitRead(&packet, nbn_udp_serv.protocol_id, bytes) < 0) { - NBN_LogDebug("Discarded invalid packet"); - continue; - } - - ip_address.host = ntohl(src_addr.sin_addr.s_addr); - ip_address.port = ntohs(src_addr.sin_port); - - NBN_Connection *conn = FindOrCreateClientConnectionByAddress(ip_address); - - if (conn == NULL) - continue; - - packet.sender = conn; - - if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet) < 0) { - NBN_LogError("Failed to raise game server event"); - - return NBN_ERROR; - } - } - - return 0; -} - -static void NBN_UDP_ServRemoveClientConnection(NBN_Connection *connection) { - NBN_Assert(connection != NULL); - - NBN_IPAddress address = ((NBN_UDP_Connection *)connection->driver_data)->address; - NBN_UDP_Connection *udp_conn = hmget(nbn_udp_serv.connections, address); - - if (udp_conn) { - NBN_LogDebug("Destroyed UDP connection %d", connection->id); - - int ret = hmdel(nbn_udp_serv.connections, address); - - NBN_Assert(ret == 1); - free(udp_conn); - } -} - -static int NBN_UDP_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *connection) { - NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)connection->driver_data; - - SOCKADDR_IN dest_addr; - - dest_addr.sin_addr.s_addr = htonl(udp_conn->address.host); - dest_addr.sin_family = AF_INET; - dest_addr.sin_port = htons(udp_conn->address.port); - - if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, - sizeof(dest_addr)) == SOCKET_ERROR) { - NBN_LogError("sendto() failed: %s", GetLastErrorMessage()); - - return NBN_ERROR; - } - - return 0; -} - -static NBN_Connection *FindOrCreateClientConnectionByAddress(NBN_IPAddress address) { - NBN_UDP_Connection *udp_conn = hmget(nbn_udp_serv.connections, address); - - if (udp_conn == NULL) { - /* this is a new connection */ - - if (GameServer_GetClientCount() >= NBN_MAX_CLIENTS) - return NULL; - - udp_conn = (NBN_UDP_Connection *)malloc(sizeof(NBN_UDP_Connection)); - - udp_conn->id = nbn_udp_serv.next_conn_id++; - udp_conn->address = address; - udp_conn->conn = NBN_GameServer_CreateClientConnection(NBN_UDP_DRIVER_ID, udp_conn, udp_conn->id); - - hmput(nbn_udp_serv.connections, address, udp_conn); - - NBN_LogDebug("New UDP connection (id: %d)", udp_conn->id); - - if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, udp_conn->conn) < 0) { - NBN_LogError("Failed to raise game server event"); - - return NULL; - } - } - - return udp_conn->conn; -} - -#pragma endregion /* Game server */ - -#pragma region Game client - -typedef struct NBN_UDP_Client { - NBN_Connection *server_conn; - uint32_t protocol_id; -} NBN_UDP_Client; - -static NBN_UDP_Client nbn_udp_cli = {NULL, 0}; - -static int ResolveIpAddress(const char *, uint16_t, NBN_IPAddress *); - -static int NBN_UDP_CliStart(uint32_t protocol_id, const char *host, uint16_t port) { - NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)malloc(sizeof(NBN_Connection)); - - nbn_udp_cli.protocol_id = protocol_id; - - if (ResolveIpAddress(host, port, &udp_conn->address) < 0) { - NBN_LogError("Failed to resolve IP address from %s", host); - - return NBN_ERROR; - } - - if (InitSocket() < 0) - return NBN_ERROR; - - if (BindSocket(0) < 0) - return NBN_ERROR; - - nbn_udp_cli.server_conn = NBN_GameClient_CreateServerConnection(NBN_UDP_DRIVER_ID, udp_conn); - - return 0; -} - -static void NBN_UDP_CliStop(void) { - NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)nbn_udp_cli.server_conn->driver_data; - - free(udp_conn); - DeinitSocket(); -} - -static int NBN_UDP_CliRecvPackets(void) { - NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)nbn_udp_cli.server_conn->driver_data; - static NBN_Packet packet = {0}; - SOCKADDR_IN src_addr; - socklen_t src_addr_len = sizeof(src_addr); - - while (true) { - int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, - &src_addr_len); - - if (bytes <= 0) - break; - - if (bytes < NBN_PACKET_HEADER_SIZE) - continue; - - NBN_IPAddress ip_address; - - ip_address.host = ntohl(src_addr.sin_addr.s_addr); - ip_address.port = ntohs(src_addr.sin_port); - - if (ip_address.host != udp_conn->address.host || ip_address.port != udp_conn->address.port) - continue; - - if (NBN_Packet_InitRead(&packet, nbn_udp_cli.protocol_id, bytes) < 0) { - NBN_LogDebug("Discarded invalid packet"); - continue; - } - - packet.sender = nbn_udp_cli.server_conn; - - NBN_Driver_RaiseEvent(NBN_DRIVER_CLI_PACKET_RECEIVED, &packet); - } - - return 0; -} - -static int NBN_UDP_CliSendPacket(NBN_Packet *packet) { - NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)nbn_udp_cli.server_conn->driver_data; - SOCKADDR_IN dest_addr; - - dest_addr.sin_addr.s_addr = htonl(udp_conn->address.host); - dest_addr.sin_family = AF_INET; - dest_addr.sin_port = htons(udp_conn->address.port); - - if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, - sizeof(dest_addr)) == SOCKET_ERROR) { - NBN_LogError("sendto() failed: %s", GetLastErrorMessage()); - - return NBN_ERROR; - } - - return 0; -} - -#pragma endregion /* Game client */ - -#pragma region Driver registering - -void NBN_UDP_Register(void) { - NBN_DriverImplementation driver_impl = {// Client implementation - NBN_UDP_CliStart, NBN_UDP_CliStop, NBN_UDP_CliRecvPackets, - NBN_UDP_CliSendPacket, - - // Server implementation - NBN_UDP_ServStart, NBN_UDP_ServStop, NBN_UDP_ServRecvPackets, - NBN_UDP_ServSendPacketTo, NBN_UDP_ServRemoveClientConnection}; - - NBN_Driver_Register(NBN_UDP_DRIVER_ID, NBN_UDP_DRIVER_NAME, driver_impl); -} - -#pragma endregion /* Driver registering */ - -#endif /* NBNET_IMPL */ diff --git a/soak/CMakeLists.txt b/soak/CMakeLists.txt index 61f3529..bcbe56d 100644 --- a/soak/CMakeLists.txt +++ b/soak/CMakeLists.txt @@ -63,10 +63,12 @@ if (EMSCRIPTEN) set(ASYNCIFY_IMPORTS "[\"__js_game_server_start\", \"__js_game_client_start\", \"__js_game_client_close\"]") set_target_properties(server PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../net_drivers/webrtc/js/api.js \ + -s EXPORTED_RUNTIME_METHODS=[HEAPU8] \ -s TOTAL_MEMORY=30MB \ -s USE_PTHREADS=1 \ -s PTHREAD_POOL_SIZE=4 \ -s EXIT_RUNTIME=1 \ + -s RUNTIME_DEBUG=1 \ -s ASSERTIONS=1 \ -s ASYNCIFY \ -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") @@ -77,6 +79,7 @@ if (EMSCRIPTEN) -s PTHREAD_POOL_SIZE=4 \ -s EXIT_RUNTIME=1 \ -s ASSERTIONS=1 \ + -s RUNTIME_DEBUG=1 \ -s ASYNCIFY \ -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") endif() diff --git a/soak/client.c b/soak/client.c index 24e70de..778e830 100644 --- a/soak/client.c +++ b/soak/client.c @@ -21,24 +21,11 @@ */ -#define NBNET_IMPL +#define NBN_UDP +#define NBN_IMPLEMENTATION #include "soak.h" -#ifdef __EMSCRIPTEN__ - -#include "../net_drivers/webrtc.h" - -#else - -#include "../net_drivers/udp.h" - -#ifdef WEBRTC_NATIVE -#include "../net_drivers/webrtc_native.h" -#endif - -#endif // __EMSCRIPTEN__ - #include typedef struct { @@ -297,8 +284,6 @@ int main(int argc, char *argv[]) { #else - NBN_UDP_Register(); - #endif // WEBRTC_NATIVE #endif // __EMSCRIPTEN__ diff --git a/soak/server.c b/soak/server.c index c86d213..d685b6d 100644 --- a/soak/server.c +++ b/soak/server.c @@ -25,21 +25,11 @@ #include #include -#define NBNET_IMPL +#define NBN_UDP +#define NBN_IMPLEMENTATION #include "soak.h" -#ifdef __EMSCRIPTEN__ -#include "../net_drivers/webrtc.h" -#else -#include "../net_drivers/udp.h" - -#ifdef WEBRTC_NATIVE -#include "../net_drivers/webrtc_native.h" -#endif - -#endif // __EMSCRIPTEN__ - typedef struct { uint8_t channel_id; unsigned int msg_id; @@ -62,33 +52,17 @@ typedef struct { } SoakChannel; typedef struct { - NBN_Connection *conn; bool error; bool is_closed; SoakChannel *channels; } SoakClient; -static SoakClient *clients[SOAK_MAX_CLIENTS] = {NULL}; -static unsigned int client_count = 0; - static void HandleNewConnection(void) { - if (client_count == SOAK_MAX_CLIENTS) { - NBN_LogInfo("Connection rejected"); - - NBN_GameServer_RejectIncomingConnectionWithCode(SOAK_SERVER_FULL_CODE); - - return; - } - - NBN_Connection *conn = NBN_GameServer_GetIncomingConnection(); - - assert(clients[conn->id - 1] == NULL); - NBN_GameServer_AcceptIncomingConnection(); + NBN_Connection *conn = NBN_GameServer_GetIncomingConnection(); SoakClient *soak_client = (SoakClient *)malloc(sizeof(SoakClient)); - soak_client->conn = conn; soak_client->error = false; soak_client->is_closed = false; soak_client->channels = (SoakChannel *)malloc(sizeof(SoakChannel) * NBN_CHANNEL_COUNT); @@ -108,14 +82,13 @@ static void HandleNewConnection(void) { memset(channel->echo_queue.messages, 0, sizeof(channel->echo_queue.messages)); } - clients[conn->id - 1] = soak_client; - client_count++; + conn->user_data = soak_client; - Soak_LogInfo("Client has connected (ID: %d)", soak_client->conn->id); + Soak_LogInfo("Client has connected (ID: %llu)", conn->id); } static void HandleClientDisconnection(NBN_DisconnectionInfo info) { - SoakClient *soak_client = clients[info.conn_id - 1]; + SoakClient *soak_client = (SoakClient *)info.user_data; assert(soak_client != NULL); @@ -123,16 +96,20 @@ static void HandleClientDisconnection(NBN_DisconnectionInfo info) { free(soak_client->channels); free(soak_client); - - clients[info.conn_id - 1] = NULL; - client_count--; } static void EchoReceivedSoakMessages(void) { - for (unsigned int i = 0; i < SOAK_MAX_CLIENTS; i++) { - SoakClient *soak_client = clients[i]; + for (unsigned int i = 0; i < NBN_GameServer_GetClientCount(); i++) { + NBN_Connection *conn = NBN_GameServer_GetClientByIndex(i); + + if (!conn->is_accepted) + continue; + + SoakClient *soak_client = (SoakClient *)conn->user_data; + + assert(soak_client != NULL); - if (soak_client == NULL || soak_client->is_closed) + if (soak_client->is_closed) continue; for (unsigned int c = 0; c < NBN_CHANNEL_COUNT; c++) { @@ -144,14 +121,14 @@ static void EchoReceivedSoakMessages(void) { SoakMessage_Write(writer, msg_entry->msg_id, msg_entry->data, msg_entry->length); - Soak_LogInfo("Send soak message %d's echo (length: %d) to client %d", msg_entry->msg_id, - msg_entry->length, soak_client->conn->id); + Soak_LogInfo("Send soak message %d's echo (length: %d) to client %llu", msg_entry->msg_id, + msg_entry->length, conn->id); - if (NBN_GameServer_EnqueueMessageFor(soak_client->conn) < 0) { - Soak_LogError("Failed to send soak message to client %d, closing client", soak_client->conn->id); + if (NBN_GameServer_EnqueueMessageFor(conn) < 0) { + Soak_LogError("Failed to send soak message to client %llu, closing client", conn->id); - if (NBN_GameServer_CloseClient(soak_client->conn) < 0) { - Soak_LogError("Failed to close client %d", soak_client->conn->id); + if (NBN_GameServer_CloseClient(conn) < 0) { + Soak_LogError("Failed to close client %llu", conn->id); abort(); } @@ -169,13 +146,14 @@ static void EchoReceivedSoakMessages(void) { } static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_Connection *sender, uint8_t channel_id) { - SoakClient *soak_client = clients[sender->id - 1]; + SoakClient *soak_client = (SoakClient *)sender->user_data; - if (!soak_client || soak_client->error) + assert(soak_client != NULL); + + if (soak_client->error) return 0; SoakChannel *channel = &soak_client->channels[channel_id]; - unsigned int msg_id; unsigned int data_length; static uint8_t recv_buffer[SOAK_MESSAGE_BIG_MAX_DATA_LENGTH]; @@ -195,8 +173,8 @@ static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_Connection *sender, return -1; } - Soak_LogInfo("Received soak message %d (length: %d) from client %d on channel %d", msg_id, data_length, sender->id, - channel_id); + Soak_LogInfo("Received soak message %d (length: %d) from client %llu on channel %d", msg_id, data_length, + sender->id, channel_id); channel->recved_messages_count++; channel->last_recved_message_id = msg_id; @@ -206,8 +184,7 @@ static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_Connection *sender, assert(channel->echo_queue.count < SOAK_CLIENT_MAX_PENDING_MESSAGES); assert(msg_entry->length == 0); - Soak_LogInfo("Enqueue soak message %d's echo for client %d on channel %d", msg_id, soak_client->conn->id, - channel_id); + Soak_LogInfo("Enqueue soak message %d's echo for client %llu on channel %d", msg_id, sender->id, channel_id); memcpy(msg_entry->data, recv_buffer, data_length); msg_entry->msg_id = msg_id; @@ -223,13 +200,13 @@ static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_Connection *sender, static void HandleReceivedMessage(void) { NBN_MessageInfo msg_info = NBN_GameServer_GetMessageInfo(); NBN_Reader *reader = NBN_GameServer_GetMessageReader(); - SoakClient *soak_client = clients[msg_info.sender->id - 1]; + SoakClient *soak_client = (SoakClient *)msg_info.sender->user_data; switch (msg_info.type) { case SOAK_MESSAGE_SMALL: if (HandleReceivedSoakMessage(reader, msg_info.sender, msg_info.channel_id) < 0) { if (NBN_GameServer_CloseClient(msg_info.sender) < 0) { - Soak_LogError("Failed to close client %d", msg_info.sender->id); + Soak_LogError("Failed to close client %llu", msg_info.sender->id); abort(); } @@ -243,7 +220,7 @@ static void HandleReceivedMessage(void) { Soak_LogError("Received unexpected message (type: %d, channel_id: %d)", msg_info.type, msg_info.channel_id); if (NBN_GameServer_CloseClient(msg_info.sender) < 0) { - Soak_LogError("Failed to close client %d", msg_info.sender->id); + Soak_LogError("Failed to close client %llu", msg_info.sender->id); abort(); } @@ -300,7 +277,6 @@ int main(int argc, char *argv[]) { #ifdef __EMSCRIPTEN__ NBN_WebRTC_Register((NBN_WebRTC_Config){.enable_tls = false}); // Register JS WebRTC driver #else - NBN_UDP_Register(); // Register the UDP driver #endif // __EMSCRIPTEN__ #ifdef WEBRTC_NATIVE diff --git a/soak/soak.h b/soak/soak.h index ae9b8ca..3c92d53 100644 --- a/soak/soak.h +++ b/soak/soak.h @@ -48,7 +48,7 @@ #include "../nbnet.h" #define SOAK_PROTOCOL_NAME "nbnet_soak" -#define SOAK_PORT 42043 +#define SOAK_PORT 42044 #define SOAK_TICK_RATE 60 #define SOAK_TICK_DT (1.0 / SOAK_TICK_RATE) #define SOAK_MESSAGE_HEADER_LENGTH 8 // 4 bytes for ID, 4 bytes for data length @@ -63,7 +63,6 @@ #define SOAK_MESSAGE_BIG 43 // may get chunked #define SOAK_SEED time(NULL) #define SOAK_DONE 1 -#define SOAK_MAX_CLIENTS 256 #define SOAK_CLIENT_MAX_PENDING_MESSAGES 50 // max number of unacked messages at a time #define SOAK_SERVER_FULL_CODE 1234 From 3a969556e0b5682c7790299991342f4cead72118 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Fri, 30 Jan 2026 10:58:49 +0100 Subject: [PATCH 35/85] WIP: re-organize library --- nbnet.c | 2816 +++++++++++++++++++++++++++++++++++++++++ nbnet.h | 2903 +------------------------------------------ soak/CMakeLists.txt | 22 +- soak/client.c | 53 +- soak/logging.c | 73 +- soak/logging.h | 27 +- soak/server.c | 44 +- soak/soak.c | 11 +- soak/soak.h | 10 - 9 files changed, 2953 insertions(+), 3006 deletions(-) create mode 100644 nbnet.c diff --git a/nbnet.c b/nbnet.c new file mode 100644 index 0000000..cca6927 --- /dev/null +++ b/nbnet.c @@ -0,0 +1,2816 @@ +/* + + Copyright (C) 2026 BIAGINI Nathan + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +#define STB_DS_IMPLEMENTATION +#include "stb_ds.h" + +#include "nbnet.h" + +static NBN_GameServer nbn_game_server; +static NBN_GameClient nbn_game_client; + +static NBN_LogFunc nbn_log_func = NULL; + +#ifdef NBN_DEBUG +static NBN_LogLevel nbn_log_level = NBN_LOG_DEBUG; +#else +static NBN_LogLevel nbn_log_level = NBN_LOG_INFO; +#endif // NBN_DEBUG + +#define Log(level, filename, line, msg, ...) \ + do { \ + if (nbn_log_func != NULL && nbn_log_level >= level) \ + nbn_log_func(level, filename, line, msg, ##__VA_ARGS__); \ + } while (0); + +#define LogInfo(msg, ...) Log(NBN_LOG_INFO, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define LogWarning(msg, ...) Log(NBN_LOG_WARNING, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define LogError(msg, ...) Log(NBN_LOG_ERROR, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define LogDebug(msg, ...) Log(NBN_LOG_DEBUG, __FILE__, __LINE__, msg, ##__VA_ARGS__) + +void NBN_SetLogFunction(NBN_LogFunc func) { nbn_log_func = func; } + +void NBN_SetLogLevel(NBN_LogLevel level) { nbn_log_level = level; } + +#define NBN_Abort abort + +#ifdef NBN_DISABLE_ASSERTS + +#define NBN_Assert(cond) \ + do { \ + } while (0); + +#else + +// TODO: log +#define NBN_Assert(cond) \ + do { \ + if (!(cond)) { \ + NBN_Abort(); \ + } \ + } while (0); + +#endif + +#ifdef NBN_UDP + +#if defined(NBN_PLATFORM_WINDOWS) + +#include + +typedef int socklen_t; + +#elif defined(NBN_PLATFORM_UNIX) || defined(NBN_PLATFORM_MAC) + +#include +#include +#include +#include +#include +#include + +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#define closesocket(s) close(s) + +typedef int SOCKET; +typedef struct sockaddr_in SOCKADDR_IN; +typedef struct sockaddr SOCKADDR; +typedef struct in_addr IN_ADDR; + +#endif // NBN_PLATFORM_WINDOWS + +static int UDP_Client_Start(NBN_GameClient *client, const char *host, uint16_t port); +static void UDP_Client_Stop(NBN_GameClient *client); +static int UDP_Client_RecvPackets(NBN_GameClient *client); +static int UDP_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet); + +static int UDP_Server_Start(NBN_GameServer *server, uint16_t port); +static void UDP_Server_Stop(NBN_GameServer *server); +static int UDP_Server_RecvPackets(NBN_GameServer *server); +static int UDP_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *connection); +static void UDP_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *connection); + +static NBN_Driver nbn_udp_driver = {.name = "UDP", + .impl = {// Client implementation + .cli_start = UDP_Client_Start, + .cli_stop = UDP_Client_Stop, + .cli_recv_packets = UDP_Client_RecvPackets, + .cli_send_packet = UDP_Client_SendPacket, + + // Server implementation + .serv_start = UDP_Server_Start, + .serv_stop = UDP_Server_Stop, + .serv_recv_packets = UDP_Server_RecvPackets, + .serv_send_packet_to = UDP_Server_SendPacketTo, + .serv_cleanup_connection = UDP_Server_CleanupConnection}}; +// TODO: remove global state +static SOCKET nbn_udp_sock; + +#endif // NBN_UDP + +#pragma region Serialization + +void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length) { + writer->buffer = buffer; + writer->length = length; + writer->position = 0; +} + +void NBN_Writer_WriteInt32(NBN_Writer *writer, int32_t value) { NBN_Writer_WriteUInt32(writer, value); } + +void NBN_Writer_WriteUInt16(NBN_Writer *writer, uint16_t value) { + NBN_Assert(writer->position + 2 <= writer->length); + + *((uint16_t *)(writer->buffer + writer->position)) = htons(value); + writer->position += 2; +} + +void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value) { + NBN_Assert(writer->position + 4 <= writer->length); + + *((uint32_t *)(writer->buffer + writer->position)) = htonl(value); + writer->position += 4; +} + +void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value) { + NBN_Assert(writer->position + 1 <= writer->length); + + writer->buffer[writer->position] = value; + writer->position++; +} + +void NBN_Writer_WriteFloat(NBN_Writer *writer, float value) { + NBN_Assert(writer->position + 4 <= writer->length); + + uint32_t *val_u = (uint32_t *)&value; + NBN_Writer_WriteUInt32(writer, htonl(*val_u)); +} + +void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int length) { + NBN_Assert(writer->position + length <= writer->length); + + memcpy(writer->buffer + writer->position, bytes, length); + writer->position += length; +} + +void NBN_Writer_WriteString(NBN_Writer *writer, const char *str, unsigned int max_len) { + unsigned int len = strnlen(str, max_len); + + NBN_Writer_WriteUInt32(writer, max_len); + NBN_Writer_WriteBytes(writer, (uint8_t *)str, len); +} + +void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length) { + reader->buffer = buffer; + reader->length = length; + reader->position = 0; +} + +int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value) { + return NBN_Reader_ReadUInt32(reader, (uint32_t *)value); +} + +int NBN_Reader_ReadUInt16(NBN_Reader *reader, uint16_t *value) { + if (reader->position + 2 > reader->length) { + return NBN_ERROR; + } + + *value = ntohs(*((uint16_t *)(reader->buffer + reader->position))); + reader->position += 2; + + return 0; +} + +int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value) { + if (reader->position + 4 > reader->length) { + return NBN_ERROR; + } + + *value = ntohl(*((uint32_t *)(reader->buffer + reader->position))); + reader->position += 4; + + return 0; +} + +int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value) { + if (reader->position + 1 > reader->length) { + return NBN_ERROR; + } + + *value = reader->buffer[reader->position]; + reader->position++; + + return 0; +} + +int NBN_Reader_ReadFloat(NBN_Reader *reader, float *value) { + if (NBN_Reader_ReadUInt32(reader, (uint32_t *)value) < 0) { + return -1; + } + + return 0; +} + +int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length) { + if (reader->position + length > reader->length) { + LogError("reader->position = %d, length = %d, reader->length = %d", reader->position, length, reader->length); + return NBN_ERROR; + } + + memcpy(bytes, reader->buffer + reader->position, length); + reader->position += length; + + return 0; +} + +int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len) { + unsigned int len; + + if (NBN_Reader_ReadUInt32(reader, &len) < 0) { + return NBN_ERROR; + } + + if (len > max_len - 1) { + return NBN_ERROR; + } + + NBN_Reader_ReadBytes(reader, (uint8_t *)str, len); + str[len] = 0; + + return 0; +} + +#pragma endregion /* Serialization */ + +#pragma region NBN_Packet + +void NBN_Packet_InitWrite(NBN_Packet *packet, uint32_t protocol_id, uint16_t seq_number, uint16_t ack, + uint32_t ack_bits) { + packet->header.protocol_id = protocol_id; + packet->header.messages_count = 0; + packet->header.seq_number = seq_number; + packet->header.ack = ack; + packet->header.ack_bits = ack_bits; + + packet->mode = NBN_PACKET_MODE_WRITE; + packet->sender = NULL; + packet->size = NBN_PACKET_HEADER_SIZE; + packet->sealed = false; + + memset(packet->buffer, 0, sizeof(packet->buffer)); +} + +int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_OutgoingMessage *out_msg) { + NBN_Message *message = &out_msg->message; + + LogDebug("Write message %d (type: %d, length: %d) to packet %d", out_msg->id, message->header.type, + message->header.length, packet->header.seq_number); + + if (packet->mode != NBN_PACKET_MODE_WRITE || packet->sealed) + return NBN_PACKET_WRITE_ERROR; + + unsigned int message_size = NBN_MESSAGE_HEADER_SIZE + message->header.length; + + if (packet->header.messages_count >= NBN_MAX_MESSAGES_PER_PACKET || + packet->size + message_size > NBN_PACKET_MAX_SIZE) { + return NBN_PACKET_WRITE_NO_SPACE; + } + + NBN_Writer writer; + + NBN_Writer_Init(&writer, packet->buffer + packet->size, sizeof(packet->buffer) - packet->size); + + NBN_Writer_WriteUInt16(&writer, out_msg->id); + NBN_Writer_WriteUInt16(&writer, message->header.length); + NBN_Writer_WriteUInt8(&writer, message->header.type); + NBN_Writer_WriteUInt8(&writer, message->header.channel_id); + + NBN_Assert(writer.position == NBN_MESSAGE_HEADER_SIZE); + + if (message->header.length > 0) { + NBN_Writer_WriteBytes(&writer, message->data, message->header.length); + } + + NBN_Assert(writer.position == message_size); + + packet->size += writer.position; + packet->header.messages_count++; + + return NBN_PACKET_WRITE_OK; +} + +int NBN_Packet_Seal(NBN_Packet *packet) { + if (packet->mode != NBN_PACKET_MODE_WRITE) + return NBN_ERROR; + + NBN_Writer writer; + + NBN_Writer_Init(&writer, packet->buffer, NBN_PACKET_HEADER_SIZE); + + NBN_Writer_WriteUInt32(&writer, packet->header.protocol_id); + NBN_Writer_WriteUInt32(&writer, packet->header.ack_bits); + NBN_Writer_WriteUInt16(&writer, packet->header.seq_number); + NBN_Writer_WriteUInt16(&writer, packet->header.ack); + NBN_Writer_WriteUInt8(&writer, packet->header.messages_count); + + NBN_Assert(writer.position == NBN_PACKET_HEADER_SIZE); + + packet->sealed = true; + + return 0; +} + +int NBN_Packet_InitRead(NBN_Packet *packet, uint32_t protocol_id, unsigned int size) { + if (size < NBN_PACKET_HEADER_SIZE || size > NBN_PACKET_MAX_SIZE) { + return NBN_ERROR; + } + + packet->mode = NBN_PACKET_MODE_READ; + packet->size = size; + packet->sender = NULL; // IMPORTANT: must be set by the drivers + packet->sealed = false; + + NBN_Reader reader; + + NBN_Reader_Init(&reader, packet->buffer, NBN_PACKET_HEADER_SIZE); + + if (NBN_Reader_ReadUInt32(&reader, &packet->header.protocol_id) < 0) { + LogDebug("Failed to read packet's protocol id"); + return NBN_ERROR; + } + + if (packet->header.protocol_id != protocol_id) { + LogDebug("Packet's protocol id did not match (expected: %d, received: %d)", protocol_id, + packet->header.protocol_id); + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt32(&reader, &packet->header.ack_bits) < 0) { + LogDebug("Failed to read packet's acked bits"); + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt16(&reader, &packet->header.seq_number) < 0) { + LogDebug("Failed to read packet's sequence number"); + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt16(&reader, &packet->header.ack) < 0) { + LogDebug("Failed to read packet's ack"); + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt8(&reader, &packet->header.messages_count) < 0) { + LogDebug("Failed to read packet's message count"); + return NBN_ERROR; + } + + return 0; +} + +#pragma endregion /* NBN_Packet */ + +#pragma region NBN_Channel + +static unsigned int Channel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2); + +static void Channel_Init(NBN_Channel *channel, uint8_t id, NBN_ChannelType type) { + channel->id = id; + channel->type = type; + channel->next_outgoing_message_id = 0; + channel->next_recv_message_id = 0; + channel->outgoing_message_count = 0; + channel->last_received_message_id = 0; + channel->next_outgoing_message_slot = 0; + channel->oldest_unacked_message_id = 0; + channel->most_recent_message_id = 0; + + for (unsigned int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) { + channel->incoming_messages_buffer[i].free = true; + channel->outgoing_messages_buffer[i].free = true; + } + + for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) + channel->ack_buffer[i] = false; +} + +static void Channel_UpdateMessageSendTime(NBN_Channel *channel, uint16_t msg_id, double time) { + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; + + NBN_Assert(msg_id == out_msg->id); + out_msg->last_send_time = time; +} + +static bool Channel_AddReceivedMessage(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) { + if (channel->type == NBN_CHANNEL_UNRELIABLE) { + if (SEQUENCE_NUMBER_GT(message->header.id, channel->last_received_message_id)) { + NBN_IncomingMessage *inc_msg = + &channel->incoming_messages_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; + + inc_msg->free = false; + memcpy(&inc_msg->message, message, sizeof(NBN_Message)); + + channel->last_received_message_id = message->header.id; + + LogDebug("Add incomoing message %d of type %d to unreliable channel %d (last received msg id: %d)", + message->header.id, message->header.type, channel->id, channel->last_received_message_id); + + return true; + } + + return false; + } else if (channel->type == NBN_CHANNEL_RELIABLE) { + unsigned int dt = Channel_ComputeMessageIdDelta(message->header.id, channel->most_recent_message_id); + + LogDebug("Add incomoing message %d of type %d to reliable channel %d (most recent msg id: %d, dt: %d)", + message->header.id, message->header.type, channel->id, channel->most_recent_message_id, dt); + + if (SEQUENCE_NUMBER_GT(message->header.id, channel->most_recent_message_id)) { + NBN_Assert(dt < NBN_CHANNEL_BUFFER_SIZE); + + channel->most_recent_message_id = message->header.id; + } else { + /* This is an old message that has already been received, probably coming from + an out of order late packet. */ + if (dt >= NBN_CHANNEL_BUFFER_SIZE) + return false; + + if (SEQUENCE_NUMBER_LT(message->header.id, channel->next_recv_message_id)) { + return false; + } + } + + NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; + + inc_msg->free = false; + memcpy(&inc_msg->message, message, sizeof(NBN_Message)); + + return true; + } + + NBN_Abort(); +} + +static bool Channel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *message) { + NBN_Assert(channel->type == NBN_CHANNEL_UNRELIABLE || channel->type == NBN_CHANNEL_RELIABLE); + + uint16_t msg_id = channel->next_outgoing_message_id; + int index = msg_id % NBN_CHANNEL_BUFFER_SIZE; + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[index]; + + // make sure the outgoing message is not already in use + if (!out_msg->free) { + LogError("No outgoing message available in channel %d (type: %d, outgoing message count: %d)", channel->type, + channel->id, channel->outgoing_message_count); +#ifdef NBN_DEBUG + NBN_Abort(); +#endif + + return false; + } + + out_msg->id = msg_id; + out_msg->free = false; + out_msg->last_send_time = -1; + memcpy(&out_msg->message, message, sizeof(NBN_Message)); + + channel->next_outgoing_message_id++; + channel->outgoing_message_count++; + + return true; +} + +static NBN_Message *Channel_GetNextRecvedMessage(NBN_Channel *channel) { + NBN_IncomingMessage *inc_msg = + &channel->incoming_messages_buffer[channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; + + if (channel->type == NBN_CHANNEL_UNRELIABLE) { + while (SEQUENCE_NUMBER_LT(channel->next_recv_message_id, channel->last_received_message_id)) { + if (!inc_msg->free && inc_msg->message.header.id == channel->next_recv_message_id) { + inc_msg->free = true; + + return &inc_msg->message; + } + + channel->next_recv_message_id++; + } + + return NULL; + } else if (channel->type == NBN_CHANNEL_RELIABLE) { + if (!inc_msg->free && inc_msg->message.header.id == channel->next_recv_message_id) { + inc_msg->free = true; + channel->next_recv_message_id++; + + return &inc_msg->message; + } + + return NULL; + } else { + NBN_Abort(); + } +} + +static bool Channel_GetNextOutgoingMessage(NBN_Channel *channel, NBN_OutgoingMessage *res_out_msg, double time) { + if (channel->type == NBN_CHANNEL_UNRELIABLE) { + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[channel->next_outgoing_message_slot]; + + if (out_msg->free) + return false; + + *res_out_msg = *out_msg; + out_msg->free = true; + channel->next_outgoing_message_slot++; + channel->next_outgoing_message_slot %= NBN_CHANNEL_BUFFER_SIZE; + + return true; + } else if (channel->type == NBN_CHANNEL_RELIABLE) { + int max_message_id = (channel->oldest_unacked_message_id + (NBN_CHANNEL_BUFFER_SIZE - 1)) % (0xFFFF + 1); + + if (SEQUENCE_NUMBER_LT(channel->next_outgoing_message_id, max_message_id)) + max_message_id = channel->next_outgoing_message_id; + + uint16_t msg_id = channel->oldest_unacked_message_id; + + while (SEQUENCE_NUMBER_LT(msg_id, max_message_id)) { + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; + + if (!out_msg->free && + (out_msg->last_send_time < 0 || time - out_msg->last_send_time >= NBN_MESSAGE_RESEND_DELAY)) { + *res_out_msg = *out_msg; + return true; + } + + msg_id++; + } + + return false; + } else { + NBN_Abort(); + } +} + +static int Channel_OnMessageSent(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) { + if (channel->type != NBN_CHANNEL_UNRELIABLE) { + return 0; + } + + channel->outgoing_message_count--; + + return 0; +} + +static int Channel_OnOutgoingMessageAcked(NBN_Endpoint *endpoint, NBN_Channel *channel, uint16_t msg_id) { + if (channel->type != NBN_CHANNEL_RELIABLE) { + return 0; + } + + int index = msg_id % NBN_CHANNEL_BUFFER_SIZE; + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[index]; + + if (out_msg->free || out_msg->id != msg_id) + return 0; + + out_msg->free = true; + + LogDebug("Message %d acked on channel %d (buffer index: %d, oldest unacked: %d)", msg_id, channel->id, index, + channel->oldest_unacked_message_id); + + channel->ack_buffer[index] = true; + channel->outgoing_message_count--; + + if (msg_id == channel->oldest_unacked_message_id) { + for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) { + uint16_t ack_msg_id = msg_id + i; + int index = ack_msg_id % NBN_CHANNEL_BUFFER_SIZE; + + if (channel->ack_buffer[index]) { + channel->ack_buffer[index] = false; + channel->oldest_unacked_message_id++; + } else { + break; + } + } + + LogDebug("Updated oldest unacked message id on channel %d: %d", channel->id, + channel->oldest_unacked_message_id); + } + + return 0; +} + +static unsigned int Channel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2) { + if (SEQUENCE_NUMBER_GT(id1, id2)) + return (id1 >= id2) ? id1 - id2 : ((0xFFFF + 1) - id2) + id1; + else + return (id2 >= id1) ? id2 - id1 : (((0xFFFF + 1) - id1) + id2) % 0xFFFF; +} + +#pragma endregion /* NBN_Channel */ + +#pragma region NBN_Connection + +static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *, uint8_t, uint8_t); + +static uint32_t Connection_BuildPacketAckBits(NBN_Connection *); +static int Connection_DecodePacketHeader(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); +static int Connection_AckPacket(NBN_Endpoint *, NBN_Connection *, uint16_t, double time); +static void Connection_InitOutgoingPacket(NBN_Connection *, uint32_t, NBN_Packet *, NBN_PacketEntry **); +static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *, uint16_t); +static bool Connection_InsertReceivedPacketEntry(NBN_Connection *, uint16_t); +static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *, uint16_t); +static bool Connection_IsPacketReceived(NBN_Connection *, uint16_t); +static int Connection_SendPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry *, double); +static int Connection_ReadNextMessageFromBuffer(NBN_Endpoint *, NBN_Reader *, NBN_Message *); +static void Connection_UpdateAveragePing(NBN_Connection *, double); +static void Connection_UpdateAveragePacketLoss(NBN_Connection *, uint16_t); +static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *, float); +static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *, double); + +int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, + double time) { + if (Connection_DecodePacketHeader(endpoint, connection, packet, time) < 0) { + LogError("Failed to decode packet %d header", packet->header.seq_number); + + return NBN_ERROR; + } + + Connection_UpdateAveragePacketLoss(connection, packet->header.ack); + + if (!Connection_InsertReceivedPacketEntry(connection, packet->header.seq_number)) + return 0; + + if (SEQUENCE_NUMBER_GT(packet->header.seq_number, connection->last_received_packet_seq_number)) + connection->last_received_packet_seq_number = packet->header.seq_number; + + NBN_Reader msg_reader; + + NBN_Reader_Init(&msg_reader, packet->buffer + NBN_PACKET_HEADER_SIZE, packet->size - NBN_PACKET_HEADER_SIZE); + + LogDebug("Processing received packet %d (message count: %d)", packet->header.seq_number, + packet->header.messages_count); + + for (int i = 0; i < packet->header.messages_count; i++) { + LogDebug("Reading message number %d from packet %d", i, packet->header.seq_number); + + static NBN_Message message = {0}; + message.type = NBN_INCOMING_MESSAGE; + int msg_len = Connection_ReadNextMessageFromBuffer(endpoint, &msg_reader, &message); + + if (msg_len < 0) { + LogError("Failed to read packet, invalid data"); + + return NBN_ERROR; + } + + uint8_t channel_id = message.header.channel_id; + + if (channel_id > NBN_CHANNEL_COUNT - 1) { + LogError("Failed to read packet, message had invalid channel"); + + return NBN_ERROR; + } + + NBN_Channel *channel = &connection->channels[channel_id]; + + if (Channel_AddReceivedMessage(endpoint, channel, &message)) { + LogDebug("Received message %d (type: %d) on channel %d", message.header.id, message.header.type, + channel->id); + +#ifdef NBN_DEBUG + if (connection->OnMessageAddedToRecvQueue) + connection->OnMessageAddedToRecvQueue(connection, &message); +#endif + } else { + LogDebug("Received message %d : discarded", message.header.id); + } + } + + return 0; +} + +int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connection, uint32_t protocol_id, + double time) { + LogDebug("Flushing all channels"); + + NBN_Packet packet = {0}; + NBN_PacketEntry *packet_entry; + unsigned int sent_packet_count = 0; + unsigned int sent_bytes = 0; + + Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); + + for (unsigned int i = 0; i < NBN_CHANNEL_COUNT; i++) { + NBN_Channel *channel = &connection->channels[i]; + + LogDebug("Flushing channel %d (message count: %d)", channel->id, channel->outgoing_message_count); + + NBN_OutgoingMessage out_msg; + unsigned int j = 0; + + // TODO: use bandwidth to determine how many packets to send at most + while (j < channel->outgoing_message_count && sent_packet_count < NBN_CONNECTION_MAX_SENT_PACKET_COUNT && + Channel_GetNextOutgoingMessage(channel, &out_msg, time)) { + NBN_Message *message = &out_msg.message; + uint16_t msg_id = out_msg.id; + bool message_sent = false; + int ret = NBN_Packet_WriteMessage(&packet, &out_msg); + + if (ret == NBN_PACKET_WRITE_OK) { + message_sent = true; + } else if (ret == NBN_PACKET_WRITE_NO_SPACE) { + if (Connection_SendPacket(connection, &packet, packet_entry, time) < 0) { + LogError("Failed to send packet %d", packet.header.seq_number); + + return NBN_ERROR; + } + + sent_packet_count++; + sent_bytes += packet.size; + + Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); + + int ret = NBN_Packet_WriteMessage(&packet, &out_msg); + + if (ret != NBN_PACKET_WRITE_OK) { + LogError("Failed to send packet %d", packet.header.seq_number); + + return NBN_ERROR; + } + + message_sent = true; + } else if (ret == NBN_PACKET_WRITE_ERROR) { + LogError("Failed to write message %d of type %d to packet %d", msg_id, message->header.type, + packet.header.seq_number); + + return NBN_ERROR; + } + + if (message_sent) { + LogDebug("Message %d added to packet %d (length: %d, type: %d)", msg_id, packet.header.seq_number, + message->header.length, message->header.type); + + Channel_UpdateMessageSendTime(channel, msg_id, time); + + packet_entry->messages[packet_entry->messages_count++] = (NBN_MessageEntry){msg_id, channel->id}; + + Channel_OnMessageSent(endpoint, channel, message); + } + + j++; + } + } + + if (Connection_SendPacket(connection, &packet, packet_entry, time) < 0) { + LogError("Failed to send packet %d to connection %d", packet.header.seq_number, connection->id); + + return NBN_ERROR; + } + + sent_bytes += packet.size; + sent_packet_count++; + + double t = time - connection->last_flush_time; + + if (t > 0) + Connection_UpdateAverageUploadBandwidth(connection, sent_bytes / t); + + connection->last_flush_time = time; + + return 0; +} + +bool NBN_Connection_CheckIfStale(NBN_Connection *connection, double time) { +#if defined(NBN_DEBUG) && defined(NBN_DISABLE_STALE_CONNECTION_DETECTION) + /* When testing under bad network conditions (in soak test for instance), we don't want to deal + with stale connections */ + return false; +#else + return time - connection->last_recv_packet_time > NBN_CONNECTION_STALE_TIME_THRESHOLD; +#endif +} + +static int Connection_DecodePacketHeader(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, + double time) { + if (Connection_AckPacket(endpoint, connection, packet->header.ack, time) < 0) { + LogError("Failed to ack packet %d", packet->header.seq_number); + + return NBN_ERROR; + } + + for (unsigned int i = 0; i < 32; i++) { + if (B_IS_UNSET(packet->header.ack_bits, i)) + continue; + + if (Connection_AckPacket(endpoint, connection, packet->header.ack - (i + 1), time) < 0) { + LogError("Failed to ack packet %d", packet->header.seq_number); + + return NBN_ERROR; + } + } + + return 0; +} + +static uint32_t Connection_BuildPacketAckBits(NBN_Connection *connection) { + uint32_t ack_bits = 0; + + for (int i = 0; i < 32; i++) { + /* + when last_received_packet_seq_number is lower than 32, the value of acked_packet_seq_number will + eventually wrap around, which means the packets from before the wrap around will naturally be acked + */ + + uint16_t acked_packet_seq_number = connection->last_received_packet_seq_number - (i + 1); + + if (Connection_IsPacketReceived(connection, acked_packet_seq_number)) + B_SET(ack_bits, i); + } + + return ack_bits; +} + +static int Connection_AckPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, uint16_t ack_packet_seq_number, + double time) { + NBN_PacketEntry *packet_entry = Connection_FindSendPacketEntry(connection, ack_packet_seq_number); + + if (packet_entry && !packet_entry->acked) { + LogDebug("Packet %d acked (connection: %d)", ack_packet_seq_number, connection->id); + + packet_entry->acked = true; + + Connection_UpdateAveragePing(connection, time - packet_entry->send_time); + + for (unsigned int i = 0; i < packet_entry->messages_count; i++) { + NBN_MessageEntry *msg_entry = &packet_entry->messages[i]; + NBN_Channel *channel = &connection->channels[msg_entry->channel_id]; + + NBN_Assert(channel != NULL); + + if (Channel_OnOutgoingMessageAcked(endpoint, channel, msg_entry->id) < 0) { + return NBN_ERROR; + } + } + } + + return 0; +} + +static void Connection_InitOutgoingPacket(NBN_Connection *connection, uint32_t protocol_id, NBN_Packet *outgoing_packet, + NBN_PacketEntry **packet_entry) { + NBN_Packet_InitWrite(outgoing_packet, protocol_id, connection->next_packet_seq_number++, + connection->last_received_packet_seq_number, Connection_BuildPacketAckBits(connection)); + + *packet_entry = Connection_InsertOutgoingPacketEntry(connection, outgoing_packet->header.seq_number); +} + +static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *connection, uint16_t seq_number) { + uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; + NBN_PacketEntry entry = { + .acked = false, .flagged_as_lost = false, .messages_count = 0, .send_time = 0, .messages = {{0, 0}}}; + + connection->packet_send_seq_buffer[index] = seq_number; + connection->packet_send_buffer[index] = entry; + + return &connection->packet_send_buffer[index]; +} + +static bool Connection_InsertReceivedPacketEntry(NBN_Connection *connection, uint16_t seq_number) { + uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; + + /* Ignore duplicated packets */ + if (connection->packet_recv_seq_buffer[index] != 0xFFFFFFFF && + connection->packet_recv_seq_buffer[index] == seq_number) + return false; + + /* + Clear entries between the previous highest sequence numbers and new highest one + to avoid entries staying inside the sequence buffer from before the sequence wrap around + and break the packet acking logic. + */ + if (SEQUENCE_NUMBER_GT(seq_number, connection->last_received_packet_seq_number)) { + for (uint16_t seq = connection->last_received_packet_seq_number + 1; SEQUENCE_NUMBER_LT(seq, seq_number); seq++) + connection->packet_recv_seq_buffer[seq % NBN_MAX_PACKET_ENTRIES] = 0xFFFFFFFF; + } + + connection->packet_recv_seq_buffer[index] = seq_number; + + return true; +} + +static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *connection, uint16_t seq_number) { + uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; + + if (connection->packet_send_seq_buffer[index] == seq_number) + return &connection->packet_send_buffer[index]; + + return NULL; +} + +static bool Connection_IsPacketReceived(NBN_Connection *connection, uint16_t packet_seq_number) { + uint16_t index = packet_seq_number % NBN_MAX_PACKET_ENTRIES; + + return connection->packet_recv_seq_buffer[index] == packet_seq_number; +} + +static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, NBN_PacketEntry *packet_entry, + double time) { + LogDebug("Send packet %d to connection %d (messages count: %d)", packet->header.seq_number, connection->id, + packet->header.messages_count); + + NBN_Assert(packet_entry->messages_count == packet->header.messages_count); + + if (NBN_Packet_Seal(packet) < 0) { + LogError("Failed to seal packet"); + + return NBN_ERROR; + } + + packet_entry->send_time = time; + + if (connection->endpoint->is_server) { +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + return PacketSimulator_EnqueuePacket(&nbn_game_server.endpoint.packet_simulator, packet, connection); +#else + if (connection->is_stale) + return 0; + + return connection->driver->impl.serv_send_packet_to(&nbn_game_server, packet, connection); +#endif + } else { +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + return PacketSimulator_EnqueuePacket(&nbn_game_client.endpoint.packet_simulator, packet, connection); +#else + NBN_Driver *driver = nbn_game_client.server_connection->driver; + + return driver->impl.cli_send_packet(&nbn_game_client, packet); +#endif + } +} + +static int Connection_ReadNextMessageFromBuffer(NBN_Endpoint *endpoint, NBN_Reader *reader, NBN_Message *message) { + if (NBN_Reader_ReadUInt16(reader, &message->header.id) < 0) { + LogError("Failed to read message id"); + + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt16(reader, &message->header.length) < 0) { + LogError("Failed to read message length"); + + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt8(reader, &message->header.type) < 0) { + LogError("Failed to read message type"); + + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt8(reader, &message->header.channel_id) < 0) { + LogError("Failed to read message channel"); + + return NBN_ERROR; + } + + uint16_t msg_len = message->header.length; + + if (msg_len > 0) { + if (msg_len > NBN_MESSAGE_MAX_SIZE) { + LogError("Failed to read message: too big"); + } + + if (NBN_Reader_ReadBytes(reader, message->data, msg_len) < 0) { + LogError("Failed to read message data"); + + return NBN_ERROR; + } + } + + return 0; +} + +static void Connection_UpdateAveragePing(NBN_Connection *connection, double ping) { + /* exponential smoothing with a factor of 0.05 */ + connection->stats.ping = connection->stats.ping + .05f * (ping - connection->stats.ping); +} + +static void Connection_UpdateAveragePacketLoss(NBN_Connection *connection, uint16_t seq) { + unsigned int lost_packet_count = 0; + uint16_t start_seq = seq - 64; + + for (int i = 0; i < 100; i++) { + uint16_t s = start_seq - i; + NBN_PacketEntry *entry = Connection_FindSendPacketEntry(connection, s); + + if (entry && !entry->acked) { + lost_packet_count++; + + if (!entry->flagged_as_lost) { + entry->flagged_as_lost = true; + connection->stats.total_lost_packets++; + } + } + } + + float packet_loss = lost_packet_count / 100.f; + + /* exponential smoothing with a factor of 0.1 */ + connection->stats.packet_loss = connection->stats.packet_loss + .1f * (packet_loss - connection->stats.packet_loss); +} + +static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *connection, float bytes_per_sec) { + /* exponential smoothing with a factor of 0.1 */ + connection->stats.upload_bandwidth = + connection->stats.upload_bandwidth + .1f * (bytes_per_sec - connection->stats.upload_bandwidth); +} + +static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *connection, double time) { + double t = time - connection->last_read_packets_time; + + if (t == 0) + return; + + float bytes_per_sec = connection->downloaded_bytes / t; + + /* exponential smoothing with a factor of 0.1 */ + connection->stats.download_bandwidth = + connection->stats.download_bandwidth + .1f * (bytes_per_sec - connection->stats.download_bandwidth); + + connection->downloaded_bytes = 0; +} + +#pragma endregion /* NBN_Connection */ + +#pragma region NBN_EventQueue + +void NBN_EventQueue_Init(NBN_EventQueue *event_queue) { + event_queue->head = 0; + event_queue->tail = 0; + event_queue->count = 0; +} + +bool NBN_EventQueue_Enqueue(NBN_EventQueue *event_queue, NBN_Event ev) { + if (event_queue->count >= NBN_EVENT_QUEUE_CAPACITY) + return false; + + event_queue->events[event_queue->tail] = ev; + + event_queue->tail = (event_queue->tail + 1) % NBN_EVENT_QUEUE_CAPACITY; + event_queue->count++; + + return true; +} + +bool NBN_EventQueue_Dequeue(NBN_EventQueue *event_queue, NBN_Event *ev) { + if (NBN_EventQueue_IsEmpty(event_queue)) + return false; + + memcpy(ev, &event_queue->events[event_queue->head], sizeof(NBN_Event)); + event_queue->head = (event_queue->head + 1) % NBN_EVENT_QUEUE_CAPACITY; + event_queue->count--; + + return true; +} + +bool NBN_EventQueue_IsEmpty(NBN_EventQueue *event_queue) { return event_queue->count == 0; } + +#pragma endregion /* NBN_EventQueue */ + +#pragma region NBN_Endpoint + +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + +static void PacketSimulator_Init(NBN_PacketSimulator *, NBN_Endpoint *); +static int PacketSimulator_EnqueuePacket(NBN_PacketSimulator *, NBN_Packet *, NBN_Connection *); +static void PacketSimulator_Start(NBN_PacketSimulator *); +static void PacketSimulator_Stop(NBN_PacketSimulator *); + +#endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ + +static void Endpoint_Init(NBN_Endpoint *, uint32_t, bool); +static void Endpoint_Deinit(NBN_Endpoint *); +static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, NBN_Connection_ID, NBN_Driver_ID); +static uint32_t Endpoint_BuildProtocolId(const char *); +static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *, NBN_Packet *, NBN_Connection *); +static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN_Message *); +static void Endpoint_UpdateTime(NBN_Endpoint *); + +static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_server) { + endpoint->is_server = is_server; + endpoint->protocol_id = protocol_id; + + NBN_EventQueue_Init(&endpoint->event_queue); + +#ifdef NBN_DEBUG + endpoint->OnMessageAddedToRecvQueue = NULL; +#endif + +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + PacketSimulator_Init(&endpoint->packet_simulator, endpoint); + PacketSimulator_Start(&endpoint->packet_simulator); +#endif + + Endpoint_UpdateTime(endpoint); +} + +static void Endpoint_Deinit(NBN_Endpoint *endpoint) { + (void)endpoint; + +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + PacketSimulator_Stop(&endpoint->packet_simulator); +#endif +} + +static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, NBN_Connection_ID id, + NBN_Driver_ID driver_id) { + NBN_Connection *connection = (NBN_Connection *)malloc(sizeof(NBN_Connection)); + + connection->id = id; + connection->endpoint = endpoint; + connection->last_recv_packet_time = endpoint->time; + connection->next_packet_seq_number = 1; + connection->last_received_packet_seq_number = 0; + connection->last_flush_time = endpoint->time; + connection->last_read_packets_time = endpoint->time; + connection->downloaded_bytes = 0; + connection->is_accepted = false; + connection->is_stale = false; + connection->is_closed = false; + connection->user_data = NULL; + + for (int i = 0; i < NBN_MAX_PACKET_ENTRIES; i++) { + connection->packet_send_seq_buffer[i] = 0xFFFFFFFF; + connection->packet_recv_seq_buffer[i] = 0xFFFFFFFF; + } + + NBN_ConnectionStats stats = {0}; + + connection->stats = stats; + + for (int i = 0; i < NBN_CHANNEL_COUNT; i++) { + // channels are created in unreliable mode by default + // TODO: expose a method to change the reliability mode of a channel + Channel_Init(&connection->channels[i], i, NBN_CHANNEL_RELIABLE); + } + + switch (driver_id) { +#ifdef NBN_UDP + case NBN_DRIVER_UDP: + connection->driver = &nbn_udp_driver; + break; +#endif // NBN_UDP + default: + LogError("Unsupported driver: %d", driver_id); + NBN_Abort(); + } + + return connection; +} + +static uint32_t Endpoint_BuildProtocolId(const char *protocol_name) { + uint32_t protocol_id = 2166136261; + + for (unsigned int i = 0; i < strlen(protocol_name); i++) { + protocol_id *= 16777619; + protocol_id ^= protocol_name[i]; + } + + return protocol_id; +} + +static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *packet, NBN_Connection *connection) { + (void)endpoint; + + LogDebug("Received packet %d (conn id: %d, ack: %d, messages count: %d)", packet->header.seq_number, connection->id, + packet->header.ack, packet->header.messages_count); + + if (NBN_Connection_ProcessReceivedPacket(endpoint, connection, packet, endpoint->time) < 0) + return NBN_ERROR; + + connection->last_recv_packet_time = endpoint->time; + connection->downloaded_bytes += packet->size; + + return 0; +} + +static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, uint8_t type, uint8_t channel_id) { + NBN_Message *message = &endpoint->write_message; + + message->header = (NBN_MessageHeader){0, 0, type, channel_id}; + message->sender = NULL; + message->type = NBN_OUTGOING_MESSAGE; + + endpoint->message_writer.position = 0; +} + +static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Message *message) { + NBN_Assert(!connection->is_closed || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); + NBN_Assert(!connection->is_stale); + + NBN_Channel *channel = &connection->channels[message->header.channel_id]; + + NBN_Assert(channel); + + LogDebug("Enqueue message of type %d on channel %d", message->header.type, channel->id); + + if (!Channel_AddOutgoingMessage(channel, message)) { + LogError("Failed to enqueue outgoing message of type %d on channel %d", message->header.type, + message->header.channel_id); + + return NBN_ERROR; + } + + return 0; +} + +static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) { +#if defined(NBN_PLATFORM_WINDOWS) + endpoint->time = GetTickCount64() / 1000.0; +#elif defined(__EMSCRIPTEN__) + endpoint->time = emscripten_get_now() / 1000; +#else + static struct timespec tp; + + if (clock_gettime(CLOCK_MONOTONIC_RAW, &tp) < 0) { + LogError("gettimeofday() failed"); + NBN_Abort(); + } + + endpoint->time = tp.tv_sec + (tp.tv_nsec / (double)1e9); +#endif // NBN_PLATFORM_WINDOWS +} + +#pragma endregion /* NBN_Endpoint */ + +#pragma region Network driver + +static void ClientDriver_OnPacketReceived(NBN_Packet *packet); +static void ServerDriver_OnClientConnected(NBN_Connection *); +static int ServerDriver_OnClientPacketReceived(NBN_Packet *); + +// TODO: just have the driver call functions from this file WTF +int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data) { + switch (ev) { + case NBN_DRIVER_CLI_PACKET_RECEIVED: + ClientDriver_OnPacketReceived((NBN_Packet *)data); + break; + + case NBN_DRIVER_SERV_CLIENT_CONNECTED: + ServerDriver_OnClientConnected((NBN_Connection *)data); + break; + + case NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED: + return ServerDriver_OnClientPacketReceived((NBN_Packet *)data); + } + + return 0; +} + +#pragma endregion /* Network driver */ + +#pragma region NBN_GameClient + +static int GameClient_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); +static int GameClient_HandleEvent(void); +static int GameClient_HandleMessageReceivedEvent(void); + +void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port) { + nbn_game_client.config = (NBN_GameClient_Config){.protocol_name = protocol_name, .host = host, .port = port}; + + nbn_game_client.client_data_writer.position = 0; +} + +NBN_Writer *NBN_GameClient_GetConnectionRequestDataWriter(void) { + NBN_Writer_Init(&nbn_game_client.client_data_writer, nbn_game_client.endpoint.connection_request_data_buffer, + sizeof(nbn_game_client.endpoint.connection_request_data_buffer)); + + return &nbn_game_client.client_data_writer; +} + +int NBN_GameClient_Start(void) { + NBN_GameClient_Config config = nbn_game_client.config; + const char *protocol_name = config.protocol_name; + const char *host = config.host; + uint16_t port = config.port; + uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); + + Endpoint_Init(&nbn_game_client.endpoint, protocol_id, false); + + int driver_count = 0; + +#ifdef NBN_UDP + nbn_game_client.server_connection = NBN_GameClient_CreateServerConnection(NBN_DRIVER_UDP); + + if (nbn_udp_driver.impl.cli_start(&nbn_game_client, host, port) < 0) { + LogError("Failed to start driver %s", nbn_udp_driver.name); + return NBN_ERROR; + } + + driver_count++; +#endif // NBN_UDP + + if (driver_count != 1) { + LogError("Only one network driver can be activated for the client"); + NBN_Abort(); + } + + nbn_game_client.is_connected = false; + nbn_game_client.closed_code = -1; + + unsigned int connection_data_len = nbn_game_client.client_data_writer.position; + + NBN_GameClient_CreateReliableMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE); + NBN_Writer *writer = NBN_GameClient_GetMessageWriter(); + + if (connection_data_len > 0) { + NBN_Assert(connection_data_len <= sizeof(nbn_game_client.endpoint.connection_request_data_buffer)); + + NBN_Writer_WriteUInt32(writer, connection_data_len); + NBN_Writer_WriteBytes(writer, nbn_game_client.endpoint.connection_request_data_buffer, connection_data_len); + } else { + NBN_Writer_WriteUInt32(writer, 0); + } + + if (NBN_GameClient_EnqueueMessage() < 0) + return NBN_ERROR; + + LogInfo("Started"); + + return 0; +} + +void NBN_GameClient_Stop(void) { + // Poll remaining events to clear the event queue + while (NBN_GameClient_Poll() != NBN_NO_EVENT) { + } + + if (nbn_game_client.server_connection) { + if (!nbn_game_client.server_connection->is_closed && !nbn_game_client.server_connection->is_stale) { + LogInfo("Disconnecting..."); + + NBN_GameClient_CreateReliableMessage(NBN_DISCONNECTION_MESSAGE_TYPE); + + if (NBN_GameClient_EnqueueMessage() < 0) { + LogError("Failed to send disconnection message"); + } + + if (NBN_GameClient_Flush() < 0) { + LogError("Failed to send packets"); + } + + nbn_game_client.server_connection->is_closed = true; + + LogInfo("Disconnected"); + } + + free(nbn_game_client.server_connection); + nbn_game_client.server_connection = NULL; + } + + LogInfo("Stopping all drivers..."); + +#ifdef NBN_UDP + nbn_udp_driver.impl.cli_stop(&nbn_game_client); +#endif // NBN_UDP + + nbn_game_client.is_connected = false; + nbn_game_client.closed_code = -1; + nbn_game_client.endpoint.server_initial_data_len = 0; + + Endpoint_Deinit(&nbn_game_client.endpoint); + + LogInfo("Stopped"); +} + +NBN_Reader *NBN_GameClient_GetServerDataReader(void) { + NBN_Endpoint *endpoint = &nbn_game_client.endpoint; + + NBN_Reader_Init(&nbn_game_client.server_data_reader, endpoint->server_initial_data_buffer, + endpoint->server_initial_data_len); + + return &nbn_game_client.server_data_reader; +} + +int NBN_GameClient_Poll(void) { + Endpoint_UpdateTime(&nbn_game_client.endpoint); + + NBN_Endpoint *endpoint = &nbn_game_client.endpoint; + + if (nbn_game_client.server_connection->is_stale) + return NBN_NO_EVENT; + + if (NBN_EventQueue_IsEmpty(&endpoint->event_queue)) { + if (NBN_Connection_CheckIfStale(nbn_game_client.server_connection, nbn_game_client.endpoint.time)) { + nbn_game_client.server_connection->is_stale = true; + nbn_game_client.is_connected = false; + + LogInfo("Server connection is stale. Disconnected."); + + NBN_Event e; + + e.type = NBN_DISCONNECTED; + e.data.connection = (NBN_Connection *)NULL; + + if (!NBN_EventQueue_Enqueue(&endpoint->event_queue, e)) + return NBN_ERROR; + } else { +#ifdef NBN_UDP + if (nbn_udp_driver.impl.cli_recv_packets(&nbn_game_client) < 0) { + LogError("Failed to read packets from driver %s", nbn_udp_driver.name); + return NBN_ERROR; + } +#endif // NBN_UDP + + NBN_Connection *server_conn = nbn_game_client.server_connection; + + for (unsigned int i = 0; i < NBN_CHANNEL_COUNT; i++) { + NBN_Channel *channel = &server_conn->channels[i]; + + NBN_Message *msg; + + while ((msg = Channel_GetNextRecvedMessage(channel)) != NULL) { + LogDebug("Got message %d of type %d from channel %d", msg->header.id, msg->header.type, + channel->id); + + if (GameClient_ProcessReceivedMessage(msg, server_conn) < 0) { + LogError("Failed to process received message"); + + return NBN_ERROR; + } + } + } + + Connection_UpdateAverageDownloadBandwidth(server_conn, nbn_game_client.endpoint.time); + + server_conn->last_read_packets_time = nbn_game_client.endpoint.time; + } + } + + bool ret = NBN_EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_client.last_event); + + return ret ? GameClient_HandleEvent() : NBN_NO_EVENT; +} + +int NBN_GameClient_Flush(void) { + return NBN_Connection_FlushChannels(&nbn_game_client.endpoint, nbn_game_client.server_connection, + nbn_game_client.endpoint.protocol_id, nbn_game_client.endpoint.time); +} + +NBN_Writer *NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id) { + NBN_Endpoint *endpoint = &nbn_game_client.endpoint; + NBN_Writer *writer = &endpoint->message_writer; + + NBN_Writer_Init(writer, endpoint->write_message.data, sizeof(endpoint->write_message.data)); + Endpoint_CreateOutgoingMessage(endpoint, type, channel_id); + + return writer; +} + +NBN_Writer *NBN_GameClient_CreateUnreliableMessage(uint8_t type) { + return NBN_GameClient_CreateMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE); +} + +NBN_Writer *NBN_GameClient_CreateReliableMessage(uint8_t type) { + return NBN_GameClient_CreateMessage(type, NBN_CHANNEL_RESERVED_RELIABLE); +} + +int NBN_GameClient_EnqueueMessage(void) { + NBN_Endpoint *endpoint = &nbn_game_client.endpoint; + NBN_Message *message = &endpoint->write_message; + + message->header.length = endpoint->message_writer.position; + + if (Endpoint_EnqueueOutgoingMessage(endpoint, nbn_game_client.server_connection, message) < 0) { + LogError("Failed to create outgoing message"); + + return NBN_ERROR; + } + + return 0; +} + +NBN_Writer *NBN_GameClient_GetMessageWriter(void) { + NBN_Endpoint *endpoint = &nbn_game_client.endpoint; + NBN_Writer *writer = &endpoint->message_writer; + + NBN_Writer_Init(writer, endpoint->write_message.data, sizeof(endpoint->write_message.data)); + + return writer; +} + +NBN_Reader *NBN_GameClient_GetMessageReader(void) { + NBN_Assert(nbn_game_client.last_event.type == NBN_MESSAGE_RECEIVED); + + NBN_MessageInfo msg_info = nbn_game_client.last_event.data.message_info; + NBN_Assert(msg_info.length > 0 && msg_info.data != NULL); + + NBN_Reader *reader = &nbn_game_client.endpoint.message_reader; + + NBN_Reader_Init(reader, msg_info.data, msg_info.length); + + return reader; +} + +NBN_Connection *NBN_GameClient_CreateServerConnection(NBN_Driver_ID driver_id) { + NBN_Connection *server_connection = Endpoint_CreateConnection(&nbn_game_client.endpoint, 0, driver_id); + +#ifdef NBN_DEBUG + server_connection->OnMessageAddedToRecvQueue = nbn_game_client.endpoint.OnMessageAddedToRecvQueue; +#endif + + nbn_game_client.server_connection = server_connection; + + return server_connection; +} + +NBN_MessageInfo NBN_GameClient_GetMessageInfo(void) { + NBN_Assert(nbn_game_client.last_event.type == NBN_MESSAGE_RECEIVED); + + return nbn_game_client.last_event.data.message_info; +} + +NBN_ConnectionStats NBN_GameClient_GetStats(void) { return nbn_game_client.server_connection->stats; } + +int NBN_GameClient_GetServerCloseCode(void) { return nbn_game_client.closed_code; } + +bool NBN_GameClient_IsConnected(void) { return nbn_game_client.is_connected; } + +#ifdef NBN_DEBUG + +void NBN_GameClient_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, void *cb) { + switch (cb_type) { + case NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE: + nbn_game_client.endpoint.OnMessageAddedToRecvQueue = (void (*)(NBN_Connection *, NBN_Message *))cb; + break; + } +} + +#endif /* NBN_DEBUG */ + +static int GameClient_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *server_connection) { + NBN_Assert(nbn_game_client.server_connection == server_connection); + + NBN_Event ev; + + ev.type = NBN_MESSAGE_RECEIVED; + + NBN_MessageInfo msg_info; + + msg_info.type = message->header.type; + msg_info.channel_id = message->header.channel_id; + msg_info.length = message->header.length; + msg_info.sender = server_connection; + msg_info.data = message->data; + + ev.data.message_info = msg_info; + + if (!NBN_EventQueue_Enqueue(&nbn_game_client.endpoint.event_queue, ev)) + return NBN_ERROR; + + return 0; +} + +static int GameClient_HandleEvent(void) { + switch (nbn_game_client.last_event.type) { + case NBN_MESSAGE_RECEIVED: + return GameClient_HandleMessageReceivedEvent(); + + default: + return nbn_game_client.last_event.type; + } +} + +static int GameClient_HandleMessageReceivedEvent(void) { + NBN_MessageInfo message_info = nbn_game_client.last_event.data.message_info; + NBN_Endpoint *endpoint = &nbn_game_client.endpoint; + + int ret = NBN_NO_EVENT; + + if (message_info.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE) { + nbn_game_client.is_connected = false; + NBN_Reader *reader = NBN_GameClient_GetMessageReader(); + + if (NBN_Reader_ReadInt32(reader, &nbn_game_client.closed_code) < 0) { + LogError("Failed to read code from client closed message"); + + return NBN_ERROR; + } + + ret = NBN_DISCONNECTED; + } else if (message_info.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE) { + if (message_info.length < 4) { + LogError("Accept message invalid length"); + + return NBN_ERROR; + } + + NBN_Reader *reader = NBN_GameClient_GetMessageReader(); + unsigned int data_length; + + if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) { + LogError("Failed to read client data length"); + + return NBN_ERROR; + } + + if (data_length > 0) { + if (data_length > sizeof(endpoint->server_initial_data_buffer)) { + LogError("Received invalid connection data from the server"); + + return NBN_ERROR; + } + + if (NBN_Reader_ReadBytes(reader, endpoint->server_initial_data_buffer, data_length) < 0) { + LogError("Failed to read server data"); + + return NBN_ERROR; + } + } + + endpoint->server_initial_data_len = data_length; + nbn_game_client.is_connected = true; + ret = NBN_CONNECTED; + } else { + ret = NBN_MESSAGE_RECEIVED; + } + + return ret; +} + +#pragma endregion /* NBN_GameClient */ + +#pragma region Game client driver + +static void ClientDriver_OnPacketReceived(NBN_Packet *packet) { + if (Endpoint_ProcessReceivedPacket(&nbn_game_client.endpoint, packet, nbn_game_client.server_connection) < 0) { + // packets from the server should always be valid + LogError("Received invalid packet from server"); + NBN_Abort(); + } +} + +#pragma endregion /* Game Client driver */ + +#pragma region NBN_GameServer + +static int GameServer_EnqueueMessageFor(NBN_Connection *client, NBN_Message *message); +static void GameServer_AddClient(NBN_Connection *); +static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection); +static void GameServer_AddClientToClosedList(NBN_Connection *client); +static int GameServer_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); +static int GameServer_CloseStaleClientConnections(void); +static void GameServer_RemoveClosedClientConnections(void); +static int GameServer_HandleEvent(void); +static int GameServer_HandleMessageReceivedEvent(void); + +void NBN_GameServer_Init(const char *protocol_name, uint16_t port) { + nbn_game_server.config = (NBN_GameServer_Config){.protocol_name = protocol_name, .port = port}; + nbn_game_server.server_data_writer.position = 0; + + hmdefault(nbn_game_server.clients, NULL); +} + +int NBN_GameServer_Start(void) { + NBN_GameServer_Config config = nbn_game_server.config; + const char *protocol_name = config.protocol_name; + uint16_t port = config.port; + uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); + + Endpoint_Init(&nbn_game_server.endpoint, protocol_id, true); + + nbn_game_server.closed_clients_head = NULL; + + int driver_count = 0; + +#ifdef NBN_UDP + if (nbn_udp_driver.impl.serv_start(&nbn_game_server, port) < 0) { + LogError("Failed to start driver %s", nbn_udp_driver.name); + return NBN_ERROR; + } + + driver_count++; +#endif // NBN_UDP + + if (driver_count < 1) { + LogError("At least one network driver has to be activated"); + NBN_Abort(); + } + + LogInfo("Started (channel count: %d)", NBN_CHANNEL_COUNT); + + return 0; +} + +void NBN_GameServer_Stop(void) { + // Poll remaning events to clear the event queue + while (NBN_GameServer_Poll() != NBN_NO_EVENT) { + } + + for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { + NBN_Connection *conn = nbn_game_server.clients[i].value; + + conn->driver->impl.serv_cleanup_connection(&nbn_game_server, conn); + free(conn); + } + + hmfree(nbn_game_server.clients); + +#ifdef NBN_UDP + nbn_udp_driver.impl.serv_stop(&nbn_game_server); +#endif // NBN_UDP + + // Free closed clients list + NBN_ConnectionListNode *current = nbn_game_server.closed_clients_head; + + while (current) { + NBN_ConnectionListNode *next = current->next; + + free(current); + + current = next; + } + + nbn_game_server.closed_clients_head = NULL; + Endpoint_Deinit(&nbn_game_server.endpoint); + + LogInfo("Stopped"); +} + +static NBN_Connection_ID NBN_BuildConnectionHash(NBN_Connection_ID id, NBN_Driver_ID driver_id) { + NBN_Assert(id <= UINT64_MAX - 0xFF); + uint8_t driver_byte = NBN_DRIVER_UDP; + + return ((NBN_Connection_ID)driver_byte << 56) | id; +} + +NBN_Connection *NBN_GameServer_FindConnection(NBN_Connection_ID id) { return hmget(nbn_game_server.clients, id); } + +unsigned int NBN_GameServer_GetClientCount(void) { return hmlen(nbn_game_server.clients); } + +NBN_Connection *NBN_GameServer_GetClientByIndex(unsigned int index) { return nbn_game_server.clients[index].value; } + +int NBN_GameServer_Poll(void) { + Endpoint_UpdateTime(&nbn_game_server.endpoint); + + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + + if (NBN_EventQueue_IsEmpty(&endpoint->event_queue)) { + if (GameServer_CloseStaleClientConnections() < 0) + return NBN_ERROR; + +#ifdef NBN_UDP + if (nbn_udp_driver.impl.serv_recv_packets(&nbn_game_server) < 0) { + LogError("Failed to read packets from driver %s", nbn_udp_driver.name); + return NBN_ERROR; + } +#endif // NBN_UDP + + nbn_game_server.stats.download_bandwidth = 0; + + for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { + NBN_Connection *client = nbn_game_server.clients[i].value; + + for (unsigned int i = 0; i < NBN_CHANNEL_COUNT; i++) { + NBN_Channel *channel = &client->channels[i]; + + if (channel) { + NBN_Message *msg; + + while ((msg = Channel_GetNextRecvedMessage(channel)) != NULL) { + if (GameServer_ProcessReceivedMessage(msg, client) < 0) { + LogError("Failed to process received message"); + + return NBN_ERROR; + } + } + } + } + + if (!client->is_closed) + Connection_UpdateAverageDownloadBandwidth(client, endpoint->time); + + nbn_game_server.stats.download_bandwidth += client->stats.download_bandwidth; + client->last_read_packets_time = endpoint->time; + } + + GameServer_RemoveClosedClientConnections(); + } + + while (NBN_EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_server.last_event)) { + int ev = GameServer_HandleEvent(); + + if (ev != NBN_SKIP_EVENT) + return ev; + } + + return NBN_NO_EVENT; +} + +int NBN_GameServer_Flush(void) { + nbn_game_server.stats.upload_bandwidth = 0; + + GameServer_RemoveClosedClientConnections(); + + for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { + NBN_Connection *client = nbn_game_server.clients[i].value; + + NBN_Assert(!(client->is_closed && client->is_stale)); + + if (!client->is_stale && + NBN_Connection_FlushChannels(&nbn_game_server.endpoint, client, nbn_game_server.endpoint.protocol_id, + nbn_game_server.endpoint.time) < 0) { + return NBN_ERROR; + } + + nbn_game_server.stats.upload_bandwidth += client->stats.upload_bandwidth; + } + + return 0; +} + +NBN_Connection *NBN_GameServer_CreateClientConnection(NBN_Driver_ID driver_id, NBN_Connection_ID conn_id) { + // write the driver ID to the first byte of the connection ID to avoid collisions between drivers + conn_id = NBN_BuildConnectionHash(conn_id, driver_id); + NBN_Connection *client = Endpoint_CreateConnection(&nbn_game_server.endpoint, conn_id, driver_id); + +#ifdef NBN_DEBUG + client->OnMessageAddedToRecvQueue = nbn_game_server.endpoint.OnMessageAddedToRecvQueue; +#endif + + return client; +} + +int NBN_GameServer_CloseClientWithCode(NBN_Connection *conn, int code) { + return GameServer_CloseClientWithCode(conn, code, false); +} + +int NBN_GameServer_CloseClient(NBN_Connection *conn) { return GameServer_CloseClientWithCode(conn, -1, false); } + +NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id) { + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + NBN_Writer *writer = &endpoint->message_writer; + + NBN_Writer_Init(writer, endpoint->write_message.data, sizeof(endpoint->write_message.data)); + Endpoint_CreateOutgoingMessage(endpoint, type, channel_id); + + return writer; +} + +NBN_Writer *NBN_GameServer_CreateUnreliableMessage(uint8_t type) { + return NBN_GameServer_CreateMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE); +} + +NBN_Writer *NBN_GameServer_CreateReliableMessage(uint8_t type) { + return NBN_GameServer_CreateMessage(type, NBN_CHANNEL_RESERVED_RELIABLE); +} + +int NBN_GameServer_EnqueueMessageFor(NBN_Connection *conn) { + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + NBN_Message *message = &endpoint->write_message; + message->header.length = endpoint->message_writer.position; + + int ret = GameServer_EnqueueMessageFor(conn, message); + + return ret; +} + +int NBN_GameServer_EnqueueBroadcastMessage(void) { + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + NBN_Message *message = &endpoint->write_message; + message->header.length = endpoint->message_writer.position; + + int ret = 0; + + for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { + NBN_Connection *conn = nbn_game_server.clients[i].value; + + if (!conn->is_accepted || conn->is_closed) + continue; + + if (GameServer_EnqueueMessageFor(conn, &endpoint->write_message) < 0) { + LogError("Failed to send message to client %d when broadcasting", conn->id); + ret = NBN_ERROR; + break; + } + } + + return ret; +} + +NBN_Reader *NBN_GameServer_GetMessageReader(void) { + NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); + + NBN_MessageInfo msg_info = nbn_game_server.last_event.data.message_info; + NBN_Assert(msg_info.length > 0 && msg_info.data != NULL); + + NBN_Reader *reader = &nbn_game_server.endpoint.message_reader; + + NBN_Reader_Init(reader, msg_info.data, msg_info.length); + + return reader; +} + +NBN_Writer *NBN_GameServer_GetConnectionDataWriter(void) { + NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); + NBN_Assert(nbn_game_server.last_event.data.connection != NULL); + + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + + NBN_Writer_Init(&nbn_game_server.server_data_writer, endpoint->server_initial_data_buffer, + sizeof(endpoint->server_initial_data_buffer)); + + return &nbn_game_server.server_data_writer; +} + +int NBN_GameServer_AcceptIncomingConnection(void) { + NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); + NBN_Assert(nbn_game_server.last_event.data.connection != NULL); + + unsigned data_length = nbn_game_server.server_data_writer.position; + NBN_Connection *client = nbn_game_server.last_event.data.connection; + NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); + + if (data_length > 0) { + NBN_Assert(data_length <= sizeof(nbn_game_server.endpoint.server_initial_data_buffer)); + + NBN_Writer_WriteUInt32(writer, data_length); + NBN_Writer_WriteBytes(writer, nbn_game_server.endpoint.server_initial_data_buffer, data_length); + } else { + NBN_Writer_WriteUInt32(writer, 0); + } + + if (NBN_GameServer_EnqueueMessageFor(client) < 0) + return NBN_ERROR; + + client->is_accepted = true; + + LogInfo("Client %d has been accepted into the server", client->id); + + return 0; +} + +int NBN_GameServer_RejectIncomingConnectionWithCode(int code) { + NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); + NBN_Assert(nbn_game_server.last_event.data.connection != NULL); + + NBN_Connection *conn = nbn_game_server.last_event.data.connection; + LogDebug("Rejecting incoming connection %d (code: %d)", conn->id, code); + + return GameServer_CloseClientWithCode(conn, code, false); +} + +int NBN_GameServer_RejectIncomingConnection(void) { return NBN_GameServer_RejectIncomingConnectionWithCode(-1); } + +NBN_Connection *NBN_GameServer_GetIncomingConnection(void) { + NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); + NBN_Assert(nbn_game_server.last_event.data.connection != NULL); + + return nbn_game_server.last_event.data.connection; +} + +NBN_Reader *NBN_GameServer_GetConnectionRequestDataReader(void) { + NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); + + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + + NBN_Reader_Init(&nbn_game_server.client_data_reader, endpoint->connection_request_data_buffer, + endpoint->client_connection_request_data_len); + + return &nbn_game_server.client_data_reader; +} + +NBN_DisconnectionInfo NBN_GameServer_GetDisconnectionInfo(void) { + NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_DISCONNECTED); + + return nbn_game_server.last_event.data.disconnection; +} + +NBN_MessageInfo NBN_GameServer_GetMessageInfo(void) { + NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); + + return nbn_game_server.last_event.data.message_info; +} + +NBN_GameServerStats NBN_GameServer_GetStats(void) { return nbn_game_server.stats; } + +#ifdef NBN_DEBUG + +void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, void *cb) { + switch (cb_type) { + case NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE: + nbn_game_server.endpoint.OnMessageAddedToRecvQueue = (void (*)(NBN_Connection *, NBN_Message *))cb; + break; + } +} + +#endif /* NBN_DEBUG */ + +static int GameServer_EnqueueMessageFor(NBN_Connection *client, NBN_Message *message) { + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + + /* Only NBN_CLIENT_ACCEPTED_MESSAGE_TYPE and NBN_CLIENT_CLOSED_MESSAGE_TYPE messages can be sent to an + * unaccapted client */ + NBN_Assert(client->is_accepted || message->header.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE || + message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); + + if (Endpoint_EnqueueOutgoingMessage(endpoint, client, message) < 0) { + LogError("Failed to create outgoing message for client %d", client->id); + + /* Do not close the client if we failed to send the close client message to avoid infinite loops */ + if (message->header.type != NBN_CLIENT_CLOSED_MESSAGE_TYPE) { + GameServer_CloseClientWithCode(client, -1, false); + + return NBN_ERROR; + } + } + + return 0; +} + +static void GameServer_AddClient(NBN_Connection *client) { + NBN_Assert(hmgeti(nbn_game_server.clients, client->id) == -1); + + hmput(nbn_game_server.clients, client->id, client); + LogDebug("New client %llu", client->id); +} + +static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection) { + if (!client->is_closed && client->is_accepted) { + if (!disconnection) { + NBN_Event e; + + e.type = NBN_CLIENT_DISCONNECTED; + e.data.disconnection = (NBN_DisconnectionInfo){client->id, client->user_data}; + + if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, e)) + return NBN_ERROR; + } + } + + if (client->is_stale) { + LogDebug("Closing stale connection %d", client->id); + + GameServer_AddClientToClosedList(client); + client->is_closed = true; + + return 0; + } + + LogDebug("Closing active connection %d (will send a disconnection message)", client->id); + + GameServer_AddClientToClosedList(client); + client->is_closed = true; + + if (!disconnection) { + LogDebug("Send close message for client %d (code: %d)", client->id, code); + + NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE); + NBN_Writer_WriteInt32(writer, code); + NBN_GameServer_EnqueueMessageFor(client); + } + + return 0; +} + +static void GameServer_AddClientToClosedList(NBN_Connection *client) { + if (client->is_closed) + return; + + // TODO: do we need to use a linked list, maybe use stb dynamic array? + NBN_ConnectionListNode *node = (NBN_ConnectionListNode *)malloc(sizeof(NBN_ConnectionListNode)); + + node->conn = client; + node->next = NULL; + + if (nbn_game_server.closed_clients_head == NULL) { + // list is empty + nbn_game_server.closed_clients_head = node; + node->prev = NULL; + } else { + // list is not empty, add node at the end + NBN_ConnectionListNode *tail = nbn_game_server.closed_clients_head; + + while (tail->next != NULL) + tail = tail->next; + + node->prev = tail; + tail->next = node; + } +} + +static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *client) { + NBN_Event ev; + + ev.type = NBN_CLIENT_MESSAGE_RECEIVED; + + NBN_MessageInfo msg_info; + + msg_info.type = message->header.type; + msg_info.channel_id = message->header.channel_id; + msg_info.length = message->header.length; + msg_info.sender = client; + msg_info.data = message->data; + + LogDebug("Received message (type: %d, id: %d) from client %lld", message->header.type, message->header.id, + client->id); + ev.data.message_info = msg_info; + + if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, ev)) + return NBN_ERROR; + + return 0; +} + +static int GameServer_CloseStaleClientConnections(void) { + for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { + NBN_Connection *client = nbn_game_server.clients[i].value; + + if (!client->is_stale && NBN_Connection_CheckIfStale(client, nbn_game_server.endpoint.time)) { + LogInfo("Client %lld connection is stale, closing it.", client->id); + + client->is_stale = true; + + if (GameServer_CloseClientWithCode(client, -1, false) < 0) + return NBN_ERROR; + } + } + + return 0; +} + +static void GameServer_RemoveClosedClientConnections(void) { + NBN_ConnectionListNode *current = nbn_game_server.closed_clients_head; + + while (current) { + NBN_ConnectionListNode *prev = current->prev; + NBN_ConnectionListNode *next = current->next; + NBN_Connection *client = current->conn; + + NBN_Assert(client->id > 0); + + if (client->is_stale) { + LogDebug("Remove closed client connection (ID: %d)", client->id); + + client->driver->impl.serv_cleanup_connection(&nbn_game_server, + client); // Notify the driver to clean up the connection + + int ret = hmdel(nbn_game_server.clients, client->id); + NBN_Assert(ret == 1); + + // Destroy the connection + + free(client); + + // Remove the connection from the closed clients list + + free(current); + + if (current == nbn_game_server.closed_clients_head) { + // delete the head of the list + NBN_ConnectionListNode *new_head = next; + + if (new_head) { + new_head->prev = NULL; + } + + nbn_game_server.closed_clients_head = new_head; + } else { + // delete a node in the middle of the list + prev->next = next; + + if (next) + next->prev = prev; + } + } + + current = next; + } +} + +static int GameServer_HandleEvent(void) { + return nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED ? GameServer_HandleMessageReceivedEvent() + : nbn_game_server.last_event.type; +} + +// TODO: big ass function +static int GameServer_HandleMessageReceivedEvent(void) { + NBN_Event *last_event = &nbn_game_server.last_event; + NBN_MessageInfo message_info = last_event->data.message_info; + NBN_Connection *sender = message_info.sender; + NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + + if (sender->is_closed || sender->is_stale) + return NBN_SKIP_EVENT; + + if (message_info.type == NBN_DISCONNECTION_MESSAGE_TYPE) { + LogInfo("Received a disconnection request from client %d", sender->id); + + if (GameServer_CloseClientWithCode(sender, -1, true) < 0) + return NBN_ERROR; + + sender->is_stale = true; + + last_event->type = NBN_CLIENT_DISCONNECTED; + last_event->data.disconnection = (NBN_DisconnectionInfo){sender->id, sender->user_data}; + + GameServer_RemoveClosedClientConnections(); + + return NBN_CLIENT_DISCONNECTED; + } + + if (message_info.type != NBN_CONNECTION_REQUEST_MESSAGE_TYPE) { + nbn_game_server.server_data_writer.position = 0; + return NBN_CLIENT_MESSAGE_RECEIVED; + } + + // at this point we know it's a connection request + NBN_Assert(message_info.type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE); + + if (message_info.length < 4) { + LogError("Connection request invalid length"); + + return NBN_ERROR; + } + + NBN_Reader *reader = NBN_GameServer_GetMessageReader(); + unsigned int data_length; + + if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) { + LogError("Failed to read client data length"); + + return NBN_ERROR; + } + + if (data_length > 0) { + if (data_length > sizeof(endpoint->connection_request_data_buffer)) { + LogError("Received invalid connection request data"); + + return NBN_ERROR; + } + + if (NBN_Reader_ReadBytes(reader, nbn_game_server.endpoint.connection_request_data_buffer, data_length) < 0) { + LogError("Failed to read client data"); + + return NBN_ERROR; + } + } + + nbn_game_server.endpoint.client_connection_request_data_len = data_length; + + NBN_Event e; + + e.type = NBN_NEW_CONNECTION; + e.data.connection = sender; + + if (!NBN_EventQueue_Enqueue(&endpoint->event_queue, e)) + return NBN_ERROR; + + return NBN_NO_EVENT; +} + +#pragma endregion /* NBN_GameServer */ + +#pragma region Game server driver + +static void ServerDriver_OnClientConnected(NBN_Connection *client) { GameServer_AddClient(client); } + +static int ServerDriver_OnClientPacketReceived(NBN_Packet *packet) { + if (Endpoint_ProcessReceivedPacket(&nbn_game_server.endpoint, packet, packet->sender) < 0) { + LogError("An error occured while processing packet from client %d, closing the client", packet->sender->id); + + return GameServer_CloseClientWithCode(packet->sender, -1, false); + } + + return 0; +} + +#pragma endregion /* Game server driver */ + +#ifdef NBN_UDP + +#pragma region UDP driver + +#ifdef NBN_PLATFORM_WINDOWS + +static char err_msg[32]; + +#endif + +static int UDP_InitSocket(void); +static void UDP_DeinitSocket(void); +static int UDP_BindSocket(uint16_t); +static char *UDP_GetLastErrorMessage(void); + +static int UDP_InitSocket(void) { +#ifdef NBN_PLATFORM_WINDOWS + WSADATA wsa; + int err = WSAStartup(MAKEWORD(2, 2), &wsa); + if (err < 0) { + LogError("WSAStartup() failed"); + + return NBN_ERROR; + } +#endif + + if ((nbn_udp_sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) + return NBN_ERROR; + +#if defined(NBN_PLATFORM_WINDOWS) + DWORD non_blocking = 1; + + if (ioctlsocket(nbn_udp_sock, FIONBIO, &non_blocking) != 0) { + LogError("ioctlsocket() failed: %s", UDP_GetLastErrorMessage()); + + return NBN_ERROR; + } +#elif defined(NBN_PLATFORM_MAC) || defined(NBN_PLATFORM_UNIX) + int non_blocking = 1; + + if (fcntl(nbn_udp_sock, F_SETFL, O_NONBLOCK, non_blocking) < 0) { + LogError("fcntl() failed: %s", UDP_GetLastErrorMessage()); + + return NBN_ERROR; + } +#endif + + return 0; +} + +static void UDP_DeinitSocket(void) { + closesocket(nbn_udp_sock); + +#ifdef NBN_PLATFORM_WINDOWS + WSACleanup(); +#endif +} + +static int UDP_BindSocket(uint16_t port) { + SOCKADDR_IN sin; + + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + + if (bind(nbn_udp_sock, (SOCKADDR *)&sin, sizeof(sin)) < 0) { + LogError("bind() failed: %s", UDP_GetLastErrorMessage()); + + return NBN_ERROR; + } + + return 0; +} + +static NBN_Connection_ID UDP_BuildConnectionID(NBN_IPAddress address) { + return ((NBN_Connection_ID)address.host << 2) | address.port; +} + +static NBN_Connection *UDP_FindOrCreateClientConnectionByAddress(NBN_IPAddress address) { + NBN_Connection_ID conn_id = UDP_BuildConnectionID(address); + conn_id = NBN_BuildConnectionHash(conn_id, NBN_DRIVER_UDP); + NBN_Connection *conn = NBN_GameServer_FindConnection(conn_id); + + if (conn == NULL) { + // this is a new connection + + conn = NBN_GameServer_CreateClientConnection(NBN_DRIVER_UDP, conn_id); + conn->driver_data.udp.ip_address = address; + + LogInfo("New UDP connection (id: %llu, addr: %d, port: %d)", conn->id, address.host, address.port); + + if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, conn) < 0) { + LogError("Failed to raise game server event"); + + return NULL; + } + + return conn; + } + + return conn; +} + +static int UDP_ParseIpAddress(const char *host, uint16_t port, NBN_IPAddress *address) { + // TODO: need to malloc here? + size_t host_len = strlen(host); + char *dup_host = (char *)malloc(host_len + 1); + memcpy(dup_host, host, host_len + 1); + uint8_t arr[4]; + + for (int i = 0; i < 4; i++) { + char *s; + + // TODO: replace strtok with strsep + if ((s = strtok(i == 0 ? dup_host : NULL, ".")) == NULL) + return NBN_ERROR; + + char *end = NULL; + int v = strtol(s, &end, 10); + + if (end == s || v < 0 || v > 255) + return NBN_ERROR; + + arr[i] = (uint8_t)v; + } + + address->host = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3]; + address->port = port; + + free(dup_host); + + return 0; +} + +static char *UDP_GetLastErrorMessage(void) { +#ifdef NBN_PLATFORM_WINDOWS + snprintf(err_msg, sizeof(err_msg), "%d", WSAGetLastError()); + + return err_msg; +#else + return strerror(errno); +#endif +} + +int UDP_Server_Start(NBN_GameServer *server, uint16_t port) { + if (UDP_InitSocket() < 0) + return NBN_ERROR; + + if (UDP_BindSocket(port) < 0) + return NBN_ERROR; + + return 0; +} + +void UDP_Server_Stop(NBN_GameServer *server) { UDP_DeinitSocket(); } + +int UDP_Server_RecvPackets(NBN_GameServer *server) { + NBN_Packet packet = {0}; + SOCKADDR_IN src_addr; + socklen_t src_addr_len = sizeof(src_addr); + + while (true) { + int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, + &src_addr_len); + + if (bytes <= 0) + break; + + if (bytes <= NBN_PACKET_HEADER_SIZE) + continue; + + if (NBN_Packet_InitRead(&packet, server->endpoint.protocol_id, bytes) < 0) { + LogDebug("Discarded invalid packet"); + continue; + } + + NBN_IPAddress ip_address; + ip_address.host = ntohl(src_addr.sin_addr.s_addr); + ip_address.port = ntohs(src_addr.sin_port); + + LogDebug("Received valid UDP packet from %d:%d", ip_address.host, ip_address.port); + + packet.sender = UDP_FindOrCreateClientConnectionByAddress(ip_address); + + if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet) < 0) { + LogError("Failed to raise game server event"); + + return NBN_ERROR; + } + } + + return 0; +} + +static void UDP_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *connection) {} + +static int UDP_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *connection) { + NBN_IPAddress dest_address = connection->driver_data.udp.ip_address; + SOCKADDR_IN dest_addr; + + dest_addr.sin_addr.s_addr = htonl(dest_address.host); + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(dest_address.port); + + if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, + sizeof(dest_addr)) == SOCKET_ERROR) { + LogError("sendto() failed: %s", UDP_GetLastErrorMessage()); + + return NBN_ERROR; + } + + return 0; +} + +int UDP_Client_Start(NBN_GameClient *client, const char *host, uint16_t port) { + NBN_IPAddress *ip_address = &client->server_connection->driver_data.udp.ip_address; + + if (UDP_ParseIpAddress(host, port, ip_address) < 0) { + LogError("Failed to resolve IP address from %s", host); + + return NBN_ERROR; + } + + if (UDP_InitSocket() < 0) + return NBN_ERROR; + + if (UDP_BindSocket(0) < 0) + return NBN_ERROR; + + return 0; +} + +void UDP_Client_Stop(NBN_GameClient *client) { UDP_DeinitSocket(); } + +int UDP_Client_RecvPackets(NBN_GameClient *client) { + NBN_IPAddress server_address = client->server_connection->driver_data.udp.ip_address; + static NBN_Packet packet = {0}; + SOCKADDR_IN src_addr; + socklen_t src_addr_len = sizeof(src_addr); + + while (true) { + int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, + &src_addr_len); + + if (bytes <= 0) + break; + + if (bytes < NBN_PACKET_HEADER_SIZE) + continue; + + uint32_t host = ntohl(src_addr.sin_addr.s_addr); + uint16_t port = ntohs(src_addr.sin_port); + + if (host != server_address.host || port != server_address.port) + continue; + + if (NBN_Packet_InitRead(&packet, client->endpoint.protocol_id, bytes) < 0) { + LogDebug("Discarded invalid packet"); + continue; + } + + packet.sender = client->server_connection; + + NBN_Driver_RaiseEvent(NBN_DRIVER_CLI_PACKET_RECEIVED, &packet); + } + + return 0; +} + +static int UDP_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet) { + NBN_IPAddress server_address = client->server_connection->driver_data.udp.ip_address; + SOCKADDR_IN dest_addr; + + dest_addr.sin_addr.s_addr = htonl(server_address.host); + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(server_address.port); + + if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, + sizeof(dest_addr)) == SOCKET_ERROR) { + LogError("sendto() failed: %s", UDP_GetLastErrorMessage()); + + return NBN_ERROR; + } + + return 0; +} + +#pragma enregion /* UDP driver */ + +#endif // NBN_UDP + +#pragma region Packet simulator + +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + +#define RAND_RATIO_BETWEEN(min, max) (((rand() % (int)((max * 100.f) - (min * 100.f) + 1)) + (min * 100.f)) / 100.f) +#define RAND_RATIO RAND_RATIO_BETWEEN(0, 1) + +#ifdef NBN_PLATFORM_WINDOWS +DWORD WINAPI PacketSimulator_Routine(LPVOID); +#else +static void *PacketSimulator_Routine(void *); +#endif + +static int PacketSimulator_SendPacket(NBN_PacketSimulator *, NBN_Packet *, NBN_Connection *receiver); +static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimulator *); + +void PacketSimulator_Init(NBN_PacketSimulator *packet_simulator, NBN_Endpoint *endpoint) { + packet_simulator->endpoint = endpoint; + packet_simulator->running = false; + packet_simulator->ping = 0; + packet_simulator->jitter = 0; + packet_simulator->packet_loss_ratio = 0; + packet_simulator->total_dropped_packets = 0; + packet_simulator->packet_duplication_ratio = 0; + packet_simulator->head_packet = NULL; + packet_simulator->tail_packet = NULL; + packet_simulator->packet_count = 0; + +#ifdef NBN_PLATFORM_WINDOWS + packet_simulator->queue_mutex = CreateMutex(NULL, FALSE, NULL); +#else + packet_simulator->queue_mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER; +#endif +} + +int PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN_Packet *packet, NBN_Connection *receiver) { +#ifdef NBN_PLATFORM_WINDOWS + WaitForSingleObject(packet_simulator->queue_mutex, INFINITE); +#else + pthread_mutex_lock(&packet_simulator->queue_mutex); +#endif + + /* Compute jitter in range [ -jitter, +jitter ]. + * Jitter is converted from seconds to milliseconds for the random operation below. + */ + + int jitter = packet_simulator->jitter * 1000; + + jitter = (jitter > 0) ? (rand() % (jitter * 2)) - jitter : 0; + + NBN_PacketSimulatorEntry *entry = (NBN_PacketSimulatorEntry *)malloc(sizeof(NBN_PacketSimulatorEntry)); + + entry->delay = packet_simulator->ping + (double)jitter / 1000; /* and converted back to seconds */ + entry->receiver = receiver; + entry->enqueued_at = packet_simulator->endpoint->time; + + memcpy(&entry->packet, packet, sizeof(NBN_Packet)); + + if (packet_simulator->packet_count > 0) { + entry->prev = packet_simulator->tail_packet; + entry->next = NULL; + + packet_simulator->tail_packet->next = entry; + packet_simulator->tail_packet = entry; + } else // the list is empty + { + entry->prev = NULL; + entry->next = NULL; + + packet_simulator->head_packet = entry; + packet_simulator->tail_packet = entry; + } + + packet_simulator->packet_count++; + +#ifdef NBN_PLATFORM_WINDOWS + ReleaseMutex(packet_simulator->queue_mutex); +#else + pthread_mutex_unlock(&packet_simulator->queue_mutex); +#endif + + return 0; +} + +void PacketSimulator_Start(NBN_PacketSimulator *packet_simulator) { +#ifdef NBN_PLATFORM_WINDOWS + packet_simulator->thread = CreateThread(NULL, 0, PacketSimulator_Routine, packet_simulator, 0, NULL); +#else + pthread_create(&packet_simulator->thread, NULL, PacketSimulator_Routine, packet_simulator); +#endif + + packet_simulator->running = true; +} + +void PacketSimulator_Stop(NBN_PacketSimulator *packet_simulator) { + packet_simulator->running = false; + +#ifdef NBN_PLATFORM_WINDOWS + WaitForSingleObject(packet_simulator->thread, INFINITE); +#else + pthread_join(packet_simulator->thread, NULL); +#endif +} + +#ifdef NBN_PLATFORM_WINDOWS +DWORD WINAPI PacketSimulator_Routine(LPVOID arg) +#else +static void *PacketSimulator_Routine(void *arg) +#endif +{ + NBN_PacketSimulator *packet_simulator = (NBN_PacketSimulator *)arg; + + while (packet_simulator->running) { +#ifdef NBN_PLATFORM_WINDOWS + WaitForSingleObject(packet_simulator->queue_mutex, INFINITE); +#else + pthread_mutex_lock(&packet_simulator->queue_mutex); +#endif + + NBN_PacketSimulatorEntry *entry = packet_simulator->head_packet; + + while (entry) { + NBN_PacketSimulatorEntry *next = entry->next; + + if (packet_simulator->endpoint->time - entry->enqueued_at < entry->delay) { + entry = next; + + continue; + } + + PacketSimulator_SendPacket(packet_simulator, &entry->packet, entry->receiver); + + for (unsigned int i = 0; i < PacketSimulator_GetRandomDuplicatePacketCount(packet_simulator); i++) { + LogDebug("Duplicate packet %d (count: %d)", entry->packet.header.seq_number, i + 1); + + PacketSimulator_SendPacket(packet_simulator, &entry->packet, entry->receiver); + } + + // remove the entry from the packet list + if (entry == packet_simulator->head_packet) // it's the head of the list + { + NBN_PacketSimulatorEntry *new_head = entry->next; + + if (new_head) + new_head->prev = NULL; + else + packet_simulator->tail_packet = NULL; + + packet_simulator->head_packet = new_head; + } else if (entry == packet_simulator->tail_packet) // it's the tail of the list + { + NBN_PacketSimulatorEntry *new_tail = entry->prev; + + new_tail->next = NULL; + packet_simulator->tail_packet = new_tail; + } else // it's in the middle of the list + { + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + } + + packet_simulator->packet_count--; + + // release the memory allocated for the entry + free(entry); + + entry = next; + } + +#ifdef NBN_PLATFORM_WINDOWS + ReleaseMutex(packet_simulator->queue_mutex); +#else + pthread_mutex_unlock(&packet_simulator->queue_mutex); +#endif + } + +#ifdef NBN_PLATFORM_WINDOWS + return 0; +#else + return NULL; +#endif +} + +static int PacketSimulator_SendPacket(NBN_PacketSimulator *packet_simulator, NBN_Packet *packet, + NBN_Connection *receiver) { + if (RAND_RATIO < packet_simulator->packet_loss_ratio) { + packet_simulator->total_dropped_packets++; + LogDebug("Drop packet %d (Total dropped packets: %d)", packet->header.seq_number, + packet_simulator->total_dropped_packets); + + return 0; + } + + NBN_Driver *driver = receiver->driver; + + if (receiver->endpoint->is_server) { + if (receiver->is_stale) + return 0; + + return driver->impl.serv_send_packet_to(&nbn_game_server, packet, receiver); + } else { + return driver->impl.cli_send_packet(&nbn_game_client, packet); + } +} + +static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimulator *packet_simulator) { + if (RAND_RATIO < packet_simulator->packet_duplication_ratio) + return rand() % 10 + 1; + + return 0; +} + +#endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ + +#pragma endregion /* Packet simulator */ diff --git a/nbnet.h b/nbnet.h index 6cf91b9..234cf94 100644 --- a/nbnet.h +++ b/nbnet.h @@ -1,6 +1,6 @@ /* - Copyright (C) 2024 BIAGINI Nathan + Copyright (C) 2026 BIAGINI Nathan This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -35,6 +35,12 @@ #if defined(_WIN32) || defined(_WIN64) #define NBN_PLATFORM_WINDOWS +#define WIN32_LEAN_AND_MEAN +// prevent inclusion of winnt.h in windows.h +#define _WINNT_ + +#include +#include #elif (defined(__APPLE__) && defined(__MACH__)) @@ -46,17 +52,6 @@ #endif -#if defined(NBN_PLATFORM_WINDOWS) - -#define WIN32_LEAN_AND_MEAN -// prevent inclusion of winnt.h in windows.h -#define _WINNT_ - -#include -#include - -#endif - #ifdef __EMSCRIPTEN__ #include @@ -75,52 +70,6 @@ #endif -#pragma region Declarations - -#ifndef NBN_Abort -#define NBN_Abort abort -#endif - -#ifndef NBN_Assert -#define NBN_Assert(cond) \ - { \ - if (!(cond)) { \ - NBN_LogError(#cond); \ - NBN_Abort(); \ - } \ - } -#endif - -#ifndef NBN_LogError -#define NBN_LogError(...) \ - do { \ - } while (0) -#endif - -#ifndef NBN_LogInfo -#define NBN_LogInfo(...) \ - do { \ - } while (0) -#endif - -#ifndef NBN_LogDebug -#define NBN_LogDebug(...) \ - do { \ - } while (0) -#endif - -#ifndef NBN_LogWarning -#define NBN_LogWarning(...) \ - do { \ - } while (0) -#endif - -#ifndef NBN_LogTrace -#define NBN_LogTrace(...) \ - do { \ - } while (0) -#endif - #define NBN_ERROR -1 typedef struct NBN_Endpoint NBN_Endpoint; @@ -129,6 +78,13 @@ typedef struct NBN_Channel NBN_Channel; typedef struct NBN_Driver NBN_Driver; typedef enum NBN_Driver_ID NBN_Driver_ID; +typedef enum NBN_LogLevel { NBN_LOG_ERROR, NBN_LOG_INFO, NBN_LOG_WARNING, NBN_LOG_DEBUG } NBN_LogLevel; + +typedef void (*NBN_LogFunc)(NBN_LogLevel level, const char *filename, int line, const char *msg, ...); + +void NBN_SetLogFunction(NBN_LogFunc); +void NBN_SetLogLevel(NBN_LogLevel); + #pragma region Serialization #define B_MASK(n) (1u << (n)) @@ -332,8 +288,6 @@ struct NBN_Channel { void NBN_Channel_Destroy(NBN_Endpoint *, NBN_Channel *); -static void Channel_UpdateMessageSendTime(NBN_Channel *channel, uint16_t msg_id, double time); - typedef struct NBN_UnreliableOrderedChannel { NBN_Channel base; } NBN_UnreliableOrderedChannel; @@ -484,33 +438,9 @@ bool NBN_EventQueue_IsEmpty(NBN_EventQueue *); #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) -/** - * Threading. - * - * Windows headers need to be included by the user of the library before - * the nbnet header because of some winsock2.h / windows.h dependencies. - */ #ifndef NBN_PLATFORM_WINDOWS -#include // Threading -#endif /* NBN_PLATFORM_WINDOWS */ - -#define NBN_GameClient_SetPing(v) \ - { nbn_game_client.endpoint.packet_simulator.ping = v; } -#define NBN_GameClient_SetJitter(v) \ - { nbn_game_client.endpoint.packet_simulator.jitter = v; } -#define NBN_GameClient_SetPacketLoss(v) \ - { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } -#define NBN_GameClient_SetPacketDuplication(v) \ - { nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; } - -#define NBN_GameServer_SetPing(v) \ - { nbn_game_server.endpoint.packet_simulator.ping = v; } -#define NBN_GameServer_SetJitter(v) \ - { nbn_game_server.endpoint.packet_simulator.jitter = v; } -#define NBN_GameServer_SetPacketLoss(v) \ - { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } -#define NBN_GameServer_SetPacketDuplication(v) \ - { nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; } +#include +#endif /* NBN_PLATFORM_WINDOWS */ typedef struct NBN_PacketSimulatorEntry NBN_PacketSimulatorEntry; @@ -548,24 +478,39 @@ typedef struct NBN_PacketSimulator { double jitter; } NBN_PacketSimulator; -void NBN_PacketSimulator_Init(NBN_PacketSimulator *, NBN_Endpoint *); -int NBN_PacketSimulator_EnqueuePacket(NBN_PacketSimulator *, NBN_Packet *, NBN_Connection *); -void NBN_PacketSimulator_Start(NBN_PacketSimulator *); -void NBN_PacketSimulator_Stop(NBN_PacketSimulator *); +#define NBN_GameClient_SetPing(v) \ + { nbn_game_client.endpoint.packet_simulator.ping = v; } +#define NBN_GameClient_SetJitter(v) \ + { nbn_game_client.endpoint.packet_simulator.jitter = v; } +#define NBN_GameClient_SetPacketLoss(v) \ + { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } +#define NBN_GameClient_SetPacketDuplication(v) \ + { nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; } + +#define NBN_GameServer_SetPing(v) \ + { nbn_game_server.endpoint.packet_simulator.ping = v; } +#define NBN_GameServer_SetJitter(v) \ + { nbn_game_server.endpoint.packet_simulator.jitter = v; } +#define NBN_GameServer_SetPacketLoss(v) \ + { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } +#define NBN_GameServer_SetPacketDuplication(v) \ + { nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; } #else -#define NBN_GameClient_SetPing(v) NBN_LogInfo("NBN_Debug_SetPing: packet simulator is not enabled, ignore") -#define NBN_GameClient_SetJitter(v) NBN_LogInfo("NBN_Debug_SetJitter: packet simulator is not enabled, ignore") -#define NBN_GameClient_SetPacketLoss(v) NBN_LogInfo("NBN_Debug_SetPacketLoss: packet simulator is not enabled, ignore") -#define NBN_GameClient_SetPacketDuplication(v) \ - NBN_LogInfo("NBN_Debug_SetPacketDuplication: packet simulator is not enabled, ignore") +#define NBN_PacketSimulator_Disabled \ + do { \ + } while (0); -#define NBN_GameServer_SetPing(v) NBN_LogInfo("NBN_Debug_SetPing: packet simulator is not enabled, ignore") -#define NBN_GameServer_SetJitter(v) NBN_LogInfo("NBN_Debug_SetJitter: packet simulator is not enabled, ignore") -#define NBN_GameServer_SetPacketLoss(v) NBN_LogInfo("NBN_Debug_SetPacketLoss: packet simulator is not enabled, ignore") -#define NBN_GameServer_SetPacketDuplication(v) \ - NBN_LogInfo("NBN_Debug_SetPacketDuplication: packet simulator is not enabled, ignore") +#define NBN_GameClient_SetPing(v) NBN_PacketSimulator_Disabled +#define NBN_GameClient_SetJitter(v) NBN_PacketSimulator_Disabled +#define NBN_GameClient_SetPacketLoss(v) NBN_PacketSimulator_Disabled +#define NBN_GameClient_SetPacketDuplication(v) NBN_PacketSimulator_Disabled + +#define NBN_GameServer_SetPing(v) NBN_PacketSimulator_Disabled +#define NBN_GameServer_SetJitter(v) NBN_PacketSimulator_Disabled +#define NBN_GameServer_SetPacketLoss(v) NBN_PacketSimulator_Disabled +#define NBN_GameServer_SetPacketDuplication(v) NBN_PacketSimulator_Disabled #endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ @@ -628,8 +573,6 @@ typedef struct NBN_GameClient { NBN_Reader server_data_reader; } NBN_GameClient; -extern NBN_GameClient nbn_game_client; - /** * Initialize the game client with minimal configuration. * @@ -775,8 +718,6 @@ typedef struct NBN_GameServer { NBN_Reader client_data_reader; } NBN_GameServer; -extern NBN_GameServer nbn_game_server; - /** * Initialize the game server with minimal configuration. * @@ -804,6 +745,9 @@ NBN_Connection *NBN_GameServer_FindConnection(NBN_Connection_ID); // TODO: doc unsigned int NBN_GameServer_GetClientCount(void); +// TODO: doc +NBN_Connection *NBN_GameServer_GetClientByIndex(unsigned int index); + /** * Poll game server events. * @@ -1015,2755 +959,4 @@ int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data); #pragma endregion /* Utils */ -#pragma region UDP driver - -#if defined(NBN_PLATFORM_WINDOWS) - -#include - -typedef int socklen_t; - -#elif defined(NBN_PLATFORM_UNIX) || defined(NBN_PLATFORM_MAC) - -#include -#include -#include -#include -#include -#include - -#define INVALID_SOCKET -1 -#define SOCKET_ERROR -1 -#define closesocket(s) close(s) - -typedef int SOCKET; -typedef struct sockaddr_in SOCKADDR_IN; -typedef struct sockaddr SOCKADDR; -typedef struct in_addr IN_ADDR; - -#endif // NBN_PLATFORM_WINDOWS - -int NBN_UDP_Client_Start(NBN_GameClient *client, const char *host, uint16_t port); -void NBN_UDP_Client_Stop(NBN_GameClient *client); -int NBN_UDP_Client_RecvPackets(NBN_GameClient *client); -int NBN_UDP_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet); - -int NBN_UDP_Server_Start(NBN_GameServer *server, uint16_t port); -void NBN_UDP_Server_Stop(NBN_GameServer *server); -int NBN_UDP_Server_RecvPackets(NBN_GameServer *server); -int NBN_UDP_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *connection); -void NBN_UDP_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *connection); - -static NBN_Driver nbn_udp_driver = {.name = "UDP", - .impl = {// Client implementation - .cli_start = NBN_UDP_Client_Start, - .cli_stop = NBN_UDP_Client_Stop, - .cli_recv_packets = NBN_UDP_Client_RecvPackets, - .cli_send_packet = NBN_UDP_Client_SendPacket, - - // Server implementation - .serv_start = NBN_UDP_Server_Start, - .serv_stop = NBN_UDP_Server_Stop, - .serv_recv_packets = NBN_UDP_Server_RecvPackets, - .serv_send_packet_to = NBN_UDP_Server_SendPacketTo, - .serv_cleanup_connection = NBN_UDP_Server_CleanupConnection}}; -// TODO: remove global state -static SOCKET nbn_udp_sock; - -#pragma endregion /* UDP driver */ - -#pragma endregion /* Declarations */ - #endif /* NBNET_H */ - -#pragma region Implementations - -#ifdef NBN_IMPLEMENTATION - -#define STB_DS_IMPLEMENTATION - -#include "stb_ds.h" - -#pragma region Serialization - -void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length) { - writer->buffer = buffer; - writer->length = length; - writer->position = 0; -} - -void NBN_Writer_WriteInt32(NBN_Writer *writer, int32_t value) { NBN_Writer_WriteUInt32(writer, value); } - -void NBN_Writer_WriteUInt16(NBN_Writer *writer, uint16_t value) { - NBN_Assert(writer->position + 2 <= writer->length); - - *((uint16_t *)(writer->buffer + writer->position)) = htons(value); - writer->position += 2; -} - -void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value) { - NBN_Assert(writer->position + 4 <= writer->length); - - *((uint32_t *)(writer->buffer + writer->position)) = htonl(value); - writer->position += 4; -} - -void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value) { - NBN_Assert(writer->position + 1 <= writer->length); - - writer->buffer[writer->position] = value; - writer->position++; -} - -void NBN_Writer_WriteFloat(NBN_Writer *writer, float value) { - NBN_Assert(writer->position + 4 <= writer->length); - - uint32_t *val_u = (uint32_t *)&value; - NBN_Writer_WriteUInt32(writer, htonl(*val_u)); -} - -void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int length) { - NBN_Assert(writer->position + length <= writer->length); - - memcpy(writer->buffer + writer->position, bytes, length); - writer->position += length; -} - -void NBN_Writer_WriteString(NBN_Writer *writer, const char *str, unsigned int max_len) { - unsigned int len = strnlen(str, max_len); - - NBN_Writer_WriteUInt32(writer, max_len); - NBN_Writer_WriteBytes(writer, (uint8_t *)str, len); -} - -void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length) { - reader->buffer = buffer; - reader->length = length; - reader->position = 0; -} - -int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value) { - return NBN_Reader_ReadUInt32(reader, (uint32_t *)value); -} - -int NBN_Reader_ReadUInt16(NBN_Reader *reader, uint16_t *value) { - if (reader->position + 2 > reader->length) { - return NBN_ERROR; - } - - *value = ntohs(*((uint16_t *)(reader->buffer + reader->position))); - reader->position += 2; - - return 0; -} - -int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value) { - if (reader->position + 4 > reader->length) { - return NBN_ERROR; - } - - *value = ntohl(*((uint32_t *)(reader->buffer + reader->position))); - reader->position += 4; - - return 0; -} - -int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value) { - if (reader->position + 1 > reader->length) { - return NBN_ERROR; - } - - *value = reader->buffer[reader->position]; - reader->position++; - - return 0; -} - -int NBN_Reader_ReadFloat(NBN_Reader *reader, float *value) { - if (NBN_Reader_ReadUInt32(reader, (uint32_t *)value) < 0) { - return -1; - } - - return 0; -} - -int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length) { - if (reader->position + length > reader->length) { - NBN_LogError("reader->position = %d, length = %d, reader->length = %d", reader->position, length, - reader->length); - return NBN_ERROR; - } - - memcpy(bytes, reader->buffer + reader->position, length); - reader->position += length; - - return 0; -} - -int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len) { - unsigned int len; - - if (NBN_Reader_ReadUInt32(reader, &len) < 0) { - return NBN_ERROR; - } - - if (len > max_len - 1) { - return NBN_ERROR; - } - - NBN_Reader_ReadBytes(reader, (uint8_t *)str, len); - str[len] = 0; - - return 0; -} - -#pragma endregion /* Serialization */ - -#pragma region NBN_Packet - -void NBN_Packet_InitWrite(NBN_Packet *packet, uint32_t protocol_id, uint16_t seq_number, uint16_t ack, - uint32_t ack_bits) { - packet->header.protocol_id = protocol_id; - packet->header.messages_count = 0; - packet->header.seq_number = seq_number; - packet->header.ack = ack; - packet->header.ack_bits = ack_bits; - - packet->mode = NBN_PACKET_MODE_WRITE; - packet->sender = NULL; - packet->size = NBN_PACKET_HEADER_SIZE; - packet->sealed = false; - - memset(packet->buffer, 0, sizeof(packet->buffer)); -} - -int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_OutgoingMessage *out_msg) { - NBN_Message *message = &out_msg->message; - - NBN_LogTrace("Write message %d (type: %d, length: %d) to packet %d", out_msg->id, message->header.type, - message->header.length, packet->header.seq_number); - - if (packet->mode != NBN_PACKET_MODE_WRITE || packet->sealed) - return NBN_PACKET_WRITE_ERROR; - - unsigned int message_size = NBN_MESSAGE_HEADER_SIZE + message->header.length; - - if (packet->header.messages_count >= NBN_MAX_MESSAGES_PER_PACKET || - packet->size + message_size > NBN_PACKET_MAX_SIZE) { - return NBN_PACKET_WRITE_NO_SPACE; - } - - NBN_Writer writer; - - NBN_Writer_Init(&writer, packet->buffer + packet->size, sizeof(packet->buffer) - packet->size); - - NBN_Writer_WriteUInt16(&writer, out_msg->id); - NBN_Writer_WriteUInt16(&writer, message->header.length); - NBN_Writer_WriteUInt8(&writer, message->header.type); - NBN_Writer_WriteUInt8(&writer, message->header.channel_id); - - NBN_Assert(writer.position == NBN_MESSAGE_HEADER_SIZE); - - if (message->header.length > 0) { - NBN_Writer_WriteBytes(&writer, message->data, message->header.length); - } - - NBN_Assert(writer.position == message_size); - - packet->size += writer.position; - packet->header.messages_count++; - - return NBN_PACKET_WRITE_OK; -} - -int NBN_Packet_Seal(NBN_Packet *packet) { - if (packet->mode != NBN_PACKET_MODE_WRITE) - return NBN_ERROR; - - NBN_Writer writer; - - NBN_Writer_Init(&writer, packet->buffer, NBN_PACKET_HEADER_SIZE); - - NBN_Writer_WriteUInt32(&writer, packet->header.protocol_id); - NBN_Writer_WriteUInt32(&writer, packet->header.ack_bits); - NBN_Writer_WriteUInt16(&writer, packet->header.seq_number); - NBN_Writer_WriteUInt16(&writer, packet->header.ack); - NBN_Writer_WriteUInt8(&writer, packet->header.messages_count); - - NBN_Assert(writer.position == NBN_PACKET_HEADER_SIZE); - - packet->sealed = true; - - return 0; -} - -int NBN_Packet_InitRead(NBN_Packet *packet, uint32_t protocol_id, unsigned int size) { - if (size < NBN_PACKET_HEADER_SIZE || size > NBN_PACKET_MAX_SIZE) { - return NBN_ERROR; - } - - packet->mode = NBN_PACKET_MODE_READ; - packet->size = size; - packet->sender = NULL; // IMPORTANT: must be set by the drivers - packet->sealed = false; - - NBN_Reader reader; - - NBN_Reader_Init(&reader, packet->buffer, NBN_PACKET_HEADER_SIZE); - - if (NBN_Reader_ReadUInt32(&reader, &packet->header.protocol_id) < 0) { - NBN_LogDebug("Failed to read packet's protocol id"); - return NBN_ERROR; - } - - if (packet->header.protocol_id != protocol_id) { - NBN_LogDebug("Packet's protocol id did not match (expected: %d, received: %d)", protocol_id, - packet->header.protocol_id); - return NBN_ERROR; - } - - if (NBN_Reader_ReadUInt32(&reader, &packet->header.ack_bits) < 0) { - NBN_LogDebug("Failed to read packet's acked bits"); - return NBN_ERROR; - } - - if (NBN_Reader_ReadUInt16(&reader, &packet->header.seq_number) < 0) { - NBN_LogDebug("Failed to read packet's sequence number"); - return NBN_ERROR; - } - - if (NBN_Reader_ReadUInt16(&reader, &packet->header.ack) < 0) { - NBN_LogDebug("Failed to read packet's ack"); - return NBN_ERROR; - } - - if (NBN_Reader_ReadUInt8(&reader, &packet->header.messages_count) < 0) { - NBN_LogDebug("Failed to read packet's message count"); - return NBN_ERROR; - } - - return 0; -} - -#pragma endregion /* NBN_Packet */ - -#pragma region NBN_Channel - -static unsigned int Channel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2); - -static void Channel_Init(NBN_Channel *channel, uint8_t id, NBN_ChannelType type) { - channel->id = id; - channel->type = type; - channel->next_outgoing_message_id = 0; - channel->next_recv_message_id = 0; - channel->outgoing_message_count = 0; - channel->last_received_message_id = 0; - channel->next_outgoing_message_slot = 0; - channel->oldest_unacked_message_id = 0; - channel->most_recent_message_id = 0; - - for (unsigned int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) { - channel->incoming_messages_buffer[i].free = true; - channel->outgoing_messages_buffer[i].free = true; - } - - for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) - channel->ack_buffer[i] = false; -} - -static void Channel_UpdateMessageSendTime(NBN_Channel *channel, uint16_t msg_id, double time) { - NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; - - NBN_Assert(msg_id == out_msg->id); - out_msg->last_send_time = time; -} - -static bool Channel_AddReceivedMessage(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) { - if (channel->type == NBN_CHANNEL_UNRELIABLE) { - if (SEQUENCE_NUMBER_GT(message->header.id, channel->last_received_message_id)) { - NBN_IncomingMessage *inc_msg = - &channel->incoming_messages_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; - - inc_msg->free = false; - memcpy(&inc_msg->message, message, sizeof(NBN_Message)); - - channel->last_received_message_id = message->header.id; - - NBN_LogTrace("Add incomoing message %d of type %d to unreliable channel %d (last received msg id: %d)", - message->header.id, message->header.type, channel->id, channel->last_received_message_id); - - return true; - } - - return false; - } else if (channel->type == NBN_CHANNEL_RELIABLE) { - unsigned int dt = Channel_ComputeMessageIdDelta(message->header.id, channel->most_recent_message_id); - - NBN_LogTrace("Add incomoing message %d of type %d to reliable channel %d (most recent msg id: %d, dt: %d)", - message->header.id, message->header.type, channel->id, channel->most_recent_message_id, dt); - - if (SEQUENCE_NUMBER_GT(message->header.id, channel->most_recent_message_id)) { - NBN_Assert(dt < NBN_CHANNEL_BUFFER_SIZE); - - channel->most_recent_message_id = message->header.id; - } else { - /* This is an old message that has already been received, probably coming from - an out of order late packet. */ - if (dt >= NBN_CHANNEL_BUFFER_SIZE) - return false; - - if (SEQUENCE_NUMBER_LT(message->header.id, channel->next_recv_message_id)) { - return false; - } - } - - NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; - - inc_msg->free = false; - memcpy(&inc_msg->message, message, sizeof(NBN_Message)); - - return true; - } - - NBN_Abort(); -} - -static bool Channel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *message) { - NBN_Assert(channel->type == NBN_CHANNEL_UNRELIABLE || channel->type == NBN_CHANNEL_RELIABLE); - - uint16_t msg_id = channel->next_outgoing_message_id; - int index = msg_id % NBN_CHANNEL_BUFFER_SIZE; - NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[index]; - - // make sure the outgoing message is not already in use - if (!out_msg->free) { - NBN_LogError("No outgoing message available in channel %d (type: %d, outgoing message count: %d)", - channel->type, channel->id, channel->outgoing_message_count); -#ifdef NBN_DEBUG - NBN_Abort(); -#endif - - return false; - } - - out_msg->id = msg_id; - out_msg->free = false; - out_msg->last_send_time = -1; - memcpy(&out_msg->message, message, sizeof(NBN_Message)); - - channel->next_outgoing_message_id++; - channel->outgoing_message_count++; - - return true; -} - -static NBN_Message *Channel_GetNextRecvedMessage(NBN_Channel *channel) { - NBN_IncomingMessage *inc_msg = - &channel->incoming_messages_buffer[channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; - - if (channel->type == NBN_CHANNEL_UNRELIABLE) { - while (SEQUENCE_NUMBER_LT(channel->next_recv_message_id, channel->last_received_message_id)) { - if (!inc_msg->free && inc_msg->message.header.id == channel->next_recv_message_id) { - inc_msg->free = true; - - return &inc_msg->message; - } - - channel->next_recv_message_id++; - } - - return NULL; - } else if (channel->type == NBN_CHANNEL_RELIABLE) { - if (!inc_msg->free && inc_msg->message.header.id == channel->next_recv_message_id) { - inc_msg->free = true; - channel->next_recv_message_id++; - - return &inc_msg->message; - } - - return NULL; - } else { - NBN_Abort(); - } -} - -static bool Channel_GetNextOutgoingMessage(NBN_Channel *channel, NBN_OutgoingMessage *res_out_msg, double time) { - if (channel->type == NBN_CHANNEL_UNRELIABLE) { - NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[channel->next_outgoing_message_slot]; - - if (out_msg->free) - return false; - - *res_out_msg = *out_msg; - out_msg->free = true; - channel->next_outgoing_message_slot++; - channel->next_outgoing_message_slot %= NBN_CHANNEL_BUFFER_SIZE; - - return true; - } else if (channel->type == NBN_CHANNEL_RELIABLE) { - int max_message_id = (channel->oldest_unacked_message_id + (NBN_CHANNEL_BUFFER_SIZE - 1)) % (0xFFFF + 1); - - if (SEQUENCE_NUMBER_LT(channel->next_outgoing_message_id, max_message_id)) - max_message_id = channel->next_outgoing_message_id; - - uint16_t msg_id = channel->oldest_unacked_message_id; - - while (SEQUENCE_NUMBER_LT(msg_id, max_message_id)) { - NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; - - if (!out_msg->free && - (out_msg->last_send_time < 0 || time - out_msg->last_send_time >= NBN_MESSAGE_RESEND_DELAY)) { - *res_out_msg = *out_msg; - return true; - } - - msg_id++; - } - - return false; - } else { - NBN_Abort(); - } -} - -static int Channel_OnMessageSent(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) { - if (channel->type != NBN_CHANNEL_UNRELIABLE) { - return 0; - } - - channel->outgoing_message_count--; - - return 0; -} - -static int Channel_OnOutgoingMessageAcked(NBN_Endpoint *endpoint, NBN_Channel *channel, uint16_t msg_id) { - if (channel->type != NBN_CHANNEL_RELIABLE) { - return 0; - } - - int index = msg_id % NBN_CHANNEL_BUFFER_SIZE; - NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[index]; - - if (out_msg->free || out_msg->id != msg_id) - return 0; - - out_msg->free = true; - - NBN_LogTrace("Message %d acked on channel %d (buffer index: %d, oldest unacked: %d)", msg_id, channel->id, index, - channel->oldest_unacked_message_id); - - channel->ack_buffer[index] = true; - channel->outgoing_message_count--; - - if (msg_id == channel->oldest_unacked_message_id) { - for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) { - uint16_t ack_msg_id = msg_id + i; - int index = ack_msg_id % NBN_CHANNEL_BUFFER_SIZE; - - if (channel->ack_buffer[index]) { - channel->ack_buffer[index] = false; - channel->oldest_unacked_message_id++; - } else { - break; - } - } - - NBN_LogTrace("Updated oldest unacked message id on channel %d: %d", channel->id, - channel->oldest_unacked_message_id); - } - - return 0; -} - -static unsigned int Channel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2) { - if (SEQUENCE_NUMBER_GT(id1, id2)) - return (id1 >= id2) ? id1 - id2 : ((0xFFFF + 1) - id2) + id1; - else - return (id2 >= id1) ? id2 - id1 : (((0xFFFF + 1) - id1) + id2) % 0xFFFF; -} - -#pragma endregion /* NBN_Channel */ - -#pragma region NBN_Connection - -static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *, uint8_t, uint8_t); - -static uint32_t Connection_BuildPacketAckBits(NBN_Connection *); -static int Connection_DecodePacketHeader(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); -static int Connection_AckPacket(NBN_Endpoint *, NBN_Connection *, uint16_t, double time); -static void Connection_InitOutgoingPacket(NBN_Connection *, uint32_t, NBN_Packet *, NBN_PacketEntry **); -static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *, uint16_t); -static bool Connection_InsertReceivedPacketEntry(NBN_Connection *, uint16_t); -static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *, uint16_t); -static bool Connection_IsPacketReceived(NBN_Connection *, uint16_t); -static int Connection_SendPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry *, double); -static int Connection_ReadNextMessageFromBuffer(NBN_Endpoint *, NBN_Reader *, NBN_Message *); -static void Connection_UpdateAveragePing(NBN_Connection *, double); -static void Connection_UpdateAveragePacketLoss(NBN_Connection *, uint16_t); -static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *, float); -static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *, double); - -int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, - double time) { - if (Connection_DecodePacketHeader(endpoint, connection, packet, time) < 0) { - NBN_LogError("Failed to decode packet %d header", packet->header.seq_number); - - return NBN_ERROR; - } - - Connection_UpdateAveragePacketLoss(connection, packet->header.ack); - - if (!Connection_InsertReceivedPacketEntry(connection, packet->header.seq_number)) - return 0; - - if (SEQUENCE_NUMBER_GT(packet->header.seq_number, connection->last_received_packet_seq_number)) - connection->last_received_packet_seq_number = packet->header.seq_number; - - NBN_Reader msg_reader; - - NBN_Reader_Init(&msg_reader, packet->buffer + NBN_PACKET_HEADER_SIZE, packet->size - NBN_PACKET_HEADER_SIZE); - - NBN_LogTrace("Processing received packet %d (message count: %d)", packet->header.seq_number, - packet->header.messages_count); - - for (int i = 0; i < packet->header.messages_count; i++) { - NBN_LogTrace("Reading message number %d from packet %d", i, packet->header.seq_number); - - static NBN_Message message = {0}; - message.type = NBN_INCOMING_MESSAGE; - int msg_len = Connection_ReadNextMessageFromBuffer(endpoint, &msg_reader, &message); - - if (msg_len < 0) { - NBN_LogError("Failed to read packet, invalid data"); - - return NBN_ERROR; - } - - uint8_t channel_id = message.header.channel_id; - - if (channel_id > NBN_CHANNEL_COUNT - 1) { - NBN_LogError("Failed to read packet, message had invalid channel"); - - return NBN_ERROR; - } - - NBN_Channel *channel = &connection->channels[channel_id]; - - if (Channel_AddReceivedMessage(endpoint, channel, &message)) { - NBN_LogTrace("Received message %d (type: %d) on channel %d", message.header.id, message.header.type, - channel->id); - -#ifdef NBN_DEBUG - if (connection->OnMessageAddedToRecvQueue) - connection->OnMessageAddedToRecvQueue(connection, &message); -#endif - } else { - NBN_LogDebug("Received message %d : discarded", message.header.id); - } - } - - return 0; -} - -int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connection, uint32_t protocol_id, - double time) { - NBN_LogTrace("Flushing all channels"); - - NBN_Packet packet = {0}; - NBN_PacketEntry *packet_entry; - unsigned int sent_packet_count = 0; - unsigned int sent_bytes = 0; - - Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); - - for (unsigned int i = 0; i < NBN_CHANNEL_COUNT; i++) { - NBN_Channel *channel = &connection->channels[i]; - - NBN_LogTrace("Flushing channel %d (message count: %d)", channel->id, channel->outgoing_message_count); - - NBN_OutgoingMessage out_msg; - unsigned int j = 0; - - // TODO: use bandwidth to determine how many packets to send at most - while (j < channel->outgoing_message_count && sent_packet_count < NBN_CONNECTION_MAX_SENT_PACKET_COUNT && - Channel_GetNextOutgoingMessage(channel, &out_msg, time)) { - NBN_Message *message = &out_msg.message; - uint16_t msg_id = out_msg.id; - bool message_sent = false; - int ret = NBN_Packet_WriteMessage(&packet, &out_msg); - - if (ret == NBN_PACKET_WRITE_OK) { - message_sent = true; - } else if (ret == NBN_PACKET_WRITE_NO_SPACE) { - if (Connection_SendPacket(connection, &packet, packet_entry, time) < 0) { - NBN_LogError("Failed to send packet %d", packet.header.seq_number); - - return NBN_ERROR; - } - - sent_packet_count++; - sent_bytes += packet.size; - - Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); - - int ret = NBN_Packet_WriteMessage(&packet, &out_msg); - - if (ret != NBN_PACKET_WRITE_OK) { - NBN_LogError("Failed to send packet %d", packet.header.seq_number); - - return NBN_ERROR; - } - - message_sent = true; - } else if (ret == NBN_PACKET_WRITE_ERROR) { - NBN_LogError("Failed to write message %d of type %d to packet %d", msg_id, message->header.type, - packet.header.seq_number); - - return NBN_ERROR; - } - - if (message_sent) { - NBN_LogTrace("Message %d added to packet %d (length: %d, type: %d)", msg_id, packet.header.seq_number, - message->header.length, message->header.type); - - Channel_UpdateMessageSendTime(channel, msg_id, time); - - packet_entry->messages[packet_entry->messages_count++] = (NBN_MessageEntry){msg_id, channel->id}; - - Channel_OnMessageSent(endpoint, channel, message); - } - - j++; - } - } - - if (Connection_SendPacket(connection, &packet, packet_entry, time) < 0) { - NBN_LogError("Failed to send packet %d to connection %d", packet.header.seq_number, connection->id); - - return NBN_ERROR; - } - - sent_bytes += packet.size; - sent_packet_count++; - - double t = time - connection->last_flush_time; - - if (t > 0) - Connection_UpdateAverageUploadBandwidth(connection, sent_bytes / t); - - connection->last_flush_time = time; - - return 0; -} - -bool NBN_Connection_CheckIfStale(NBN_Connection *connection, double time) { -#if defined(NBN_DEBUG) && defined(NBN_DISABLE_STALE_CONNECTION_DETECTION) - /* When testing under bad network conditions (in soak test for instance), we don't want to deal - with stale connections */ - return false; -#else - return time - connection->last_recv_packet_time > NBN_CONNECTION_STALE_TIME_THRESHOLD; -#endif -} - -static int Connection_DecodePacketHeader(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, - double time) { - if (Connection_AckPacket(endpoint, connection, packet->header.ack, time) < 0) { - NBN_LogError("Failed to ack packet %d", packet->header.seq_number); - - return NBN_ERROR; - } - - for (unsigned int i = 0; i < 32; i++) { - if (B_IS_UNSET(packet->header.ack_bits, i)) - continue; - - if (Connection_AckPacket(endpoint, connection, packet->header.ack - (i + 1), time) < 0) { - NBN_LogError("Failed to ack packet %d", packet->header.seq_number); - - return NBN_ERROR; - } - } - - return 0; -} - -static uint32_t Connection_BuildPacketAckBits(NBN_Connection *connection) { - uint32_t ack_bits = 0; - - for (int i = 0; i < 32; i++) { - /* - when last_received_packet_seq_number is lower than 32, the value of acked_packet_seq_number will - eventually wrap around, which means the packets from before the wrap around will naturally be acked - */ - - uint16_t acked_packet_seq_number = connection->last_received_packet_seq_number - (i + 1); - - if (Connection_IsPacketReceived(connection, acked_packet_seq_number)) - B_SET(ack_bits, i); - } - - return ack_bits; -} - -static int Connection_AckPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, uint16_t ack_packet_seq_number, - double time) { - NBN_PacketEntry *packet_entry = Connection_FindSendPacketEntry(connection, ack_packet_seq_number); - - if (packet_entry && !packet_entry->acked) { - NBN_LogTrace("Packet %d acked (connection: %d)", ack_packet_seq_number, connection->id); - - packet_entry->acked = true; - - Connection_UpdateAveragePing(connection, time - packet_entry->send_time); - - for (unsigned int i = 0; i < packet_entry->messages_count; i++) { - NBN_MessageEntry *msg_entry = &packet_entry->messages[i]; - NBN_Channel *channel = &connection->channels[msg_entry->channel_id]; - - NBN_Assert(channel != NULL); - - if (Channel_OnOutgoingMessageAcked(endpoint, channel, msg_entry->id) < 0) { - return NBN_ERROR; - } - } - } - - return 0; -} - -static void Connection_InitOutgoingPacket(NBN_Connection *connection, uint32_t protocol_id, NBN_Packet *outgoing_packet, - NBN_PacketEntry **packet_entry) { - NBN_Packet_InitWrite(outgoing_packet, protocol_id, connection->next_packet_seq_number++, - connection->last_received_packet_seq_number, Connection_BuildPacketAckBits(connection)); - - *packet_entry = Connection_InsertOutgoingPacketEntry(connection, outgoing_packet->header.seq_number); -} - -static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *connection, uint16_t seq_number) { - uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; - NBN_PacketEntry entry = { - .acked = false, .flagged_as_lost = false, .messages_count = 0, .send_time = 0, .messages = {{0, 0}}}; - - connection->packet_send_seq_buffer[index] = seq_number; - connection->packet_send_buffer[index] = entry; - - return &connection->packet_send_buffer[index]; -} - -static bool Connection_InsertReceivedPacketEntry(NBN_Connection *connection, uint16_t seq_number) { - uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; - - /* Ignore duplicated packets */ - if (connection->packet_recv_seq_buffer[index] != 0xFFFFFFFF && - connection->packet_recv_seq_buffer[index] == seq_number) - return false; - - /* - Clear entries between the previous highest sequence numbers and new highest one - to avoid entries staying inside the sequence buffer from before the sequence wrap around - and break the packet acking logic. - */ - if (SEQUENCE_NUMBER_GT(seq_number, connection->last_received_packet_seq_number)) { - for (uint16_t seq = connection->last_received_packet_seq_number + 1; SEQUENCE_NUMBER_LT(seq, seq_number); seq++) - connection->packet_recv_seq_buffer[seq % NBN_MAX_PACKET_ENTRIES] = 0xFFFFFFFF; - } - - connection->packet_recv_seq_buffer[index] = seq_number; - - return true; -} - -static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *connection, uint16_t seq_number) { - uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; - - if (connection->packet_send_seq_buffer[index] == seq_number) - return &connection->packet_send_buffer[index]; - - return NULL; -} - -static bool Connection_IsPacketReceived(NBN_Connection *connection, uint16_t packet_seq_number) { - uint16_t index = packet_seq_number % NBN_MAX_PACKET_ENTRIES; - - return connection->packet_recv_seq_buffer[index] == packet_seq_number; -} - -static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, NBN_PacketEntry *packet_entry, - double time) { - NBN_LogTrace("Send packet %d to connection %d (messages count: %d)", packet->header.seq_number, connection->id, - packet->header.messages_count); - - NBN_Assert(packet_entry->messages_count == packet->header.messages_count); - - if (NBN_Packet_Seal(packet) < 0) { - NBN_LogError("Failed to seal packet"); - - return NBN_ERROR; - } - - packet_entry->send_time = time; - - if (connection->endpoint->is_server) { -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - return NBN_PacketSimulator_EnqueuePacket(&nbn_game_server.endpoint.packet_simulator, packet, connection); -#else - if (connection->is_stale) - return 0; - - return connection->driver->impl.serv_send_packet_to(&nbn_game_server, packet, connection); -#endif - } else { -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - return NBN_PacketSimulator_EnqueuePacket(&nbn_game_client.endpoint.packet_simulator, packet, connection); -#else - NBN_Driver *driver = nbn_game_client.server_connection->driver; - - return driver->impl.cli_send_packet(&nbn_game_client, packet); -#endif - } -} - -static int Connection_ReadNextMessageFromBuffer(NBN_Endpoint *endpoint, NBN_Reader *reader, NBN_Message *message) { - if (NBN_Reader_ReadUInt16(reader, &message->header.id) < 0) { - NBN_LogError("Failed to read message id"); - - return NBN_ERROR; - } - - if (NBN_Reader_ReadUInt16(reader, &message->header.length) < 0) { - NBN_LogError("Failed to read message length"); - - return NBN_ERROR; - } - - if (NBN_Reader_ReadUInt8(reader, &message->header.type) < 0) { - NBN_LogError("Failed to read message type"); - - return NBN_ERROR; - } - - if (NBN_Reader_ReadUInt8(reader, &message->header.channel_id) < 0) { - NBN_LogError("Failed to read message channel"); - - return NBN_ERROR; - } - - uint16_t msg_len = message->header.length; - - if (msg_len > 0) { - if (msg_len > NBN_MESSAGE_MAX_SIZE) { - NBN_LogError("Failed to read message: too big"); - } - - if (NBN_Reader_ReadBytes(reader, message->data, msg_len) < 0) { - NBN_LogError("Failed to read message data"); - - return NBN_ERROR; - } - } - - return 0; -} - -static void Connection_UpdateAveragePing(NBN_Connection *connection, double ping) { - /* exponential smoothing with a factor of 0.05 */ - connection->stats.ping = connection->stats.ping + .05f * (ping - connection->stats.ping); -} - -static void Connection_UpdateAveragePacketLoss(NBN_Connection *connection, uint16_t seq) { - unsigned int lost_packet_count = 0; - uint16_t start_seq = seq - 64; - - for (int i = 0; i < 100; i++) { - uint16_t s = start_seq - i; - NBN_PacketEntry *entry = Connection_FindSendPacketEntry(connection, s); - - if (entry && !entry->acked) { - lost_packet_count++; - - if (!entry->flagged_as_lost) { - entry->flagged_as_lost = true; - connection->stats.total_lost_packets++; - } - } - } - - float packet_loss = lost_packet_count / 100.f; - - /* exponential smoothing with a factor of 0.1 */ - connection->stats.packet_loss = connection->stats.packet_loss + .1f * (packet_loss - connection->stats.packet_loss); -} - -static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *connection, float bytes_per_sec) { - /* exponential smoothing with a factor of 0.1 */ - connection->stats.upload_bandwidth = - connection->stats.upload_bandwidth + .1f * (bytes_per_sec - connection->stats.upload_bandwidth); -} - -static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *connection, double time) { - double t = time - connection->last_read_packets_time; - - if (t == 0) - return; - - float bytes_per_sec = connection->downloaded_bytes / t; - - /* exponential smoothing with a factor of 0.1 */ - connection->stats.download_bandwidth = - connection->stats.download_bandwidth + .1f * (bytes_per_sec - connection->stats.download_bandwidth); - - connection->downloaded_bytes = 0; -} - -#pragma endregion /* NBN_Connection */ - -#pragma region NBN_EventQueue - -void NBN_EventQueue_Init(NBN_EventQueue *event_queue) { - event_queue->head = 0; - event_queue->tail = 0; - event_queue->count = 0; -} - -bool NBN_EventQueue_Enqueue(NBN_EventQueue *event_queue, NBN_Event ev) { - if (event_queue->count >= NBN_EVENT_QUEUE_CAPACITY) - return false; - - event_queue->events[event_queue->tail] = ev; - - event_queue->tail = (event_queue->tail + 1) % NBN_EVENT_QUEUE_CAPACITY; - event_queue->count++; - - return true; -} - -bool NBN_EventQueue_Dequeue(NBN_EventQueue *event_queue, NBN_Event *ev) { - if (NBN_EventQueue_IsEmpty(event_queue)) - return false; - - memcpy(ev, &event_queue->events[event_queue->head], sizeof(NBN_Event)); - event_queue->head = (event_queue->head + 1) % NBN_EVENT_QUEUE_CAPACITY; - event_queue->count--; - - return true; -} - -bool NBN_EventQueue_IsEmpty(NBN_EventQueue *event_queue) { return event_queue->count == 0; } - -#pragma endregion /* NBN_EventQueue */ - -#pragma region NBN_Endpoint - -static void Endpoint_Init(NBN_Endpoint *, uint32_t, bool); -static void Endpoint_Deinit(NBN_Endpoint *); -static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, NBN_Connection_ID, NBN_Driver_ID); -static uint32_t Endpoint_BuildProtocolId(const char *); -static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *, NBN_Packet *, NBN_Connection *); -static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN_Message *); -static void Endpoint_UpdateTime(NBN_Endpoint *); - -static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_server) { - endpoint->is_server = is_server; - endpoint->protocol_id = protocol_id; - - NBN_EventQueue_Init(&endpoint->event_queue); - -#ifdef NBN_DEBUG - endpoint->OnMessageAddedToRecvQueue = NULL; -#endif - -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - NBN_PacketSimulator_Init(&endpoint->packet_simulator, endpoint); - NBN_PacketSimulator_Start(&endpoint->packet_simulator); -#endif - - Endpoint_UpdateTime(endpoint); -} - -static void Endpoint_Deinit(NBN_Endpoint *endpoint) { - (void)endpoint; - -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - NBN_PacketSimulator_Stop(&endpoint->packet_simulator); -#endif -} - -static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, NBN_Connection_ID id, - NBN_Driver_ID driver_id) { - NBN_Connection *connection = (NBN_Connection *)malloc(sizeof(NBN_Connection)); - - connection->id = id; - connection->endpoint = endpoint; - connection->last_recv_packet_time = endpoint->time; - connection->next_packet_seq_number = 1; - connection->last_received_packet_seq_number = 0; - connection->last_flush_time = endpoint->time; - connection->last_read_packets_time = endpoint->time; - connection->downloaded_bytes = 0; - connection->is_accepted = false; - connection->is_stale = false; - connection->is_closed = false; - connection->user_data = NULL; - - for (int i = 0; i < NBN_MAX_PACKET_ENTRIES; i++) { - connection->packet_send_seq_buffer[i] = 0xFFFFFFFF; - connection->packet_recv_seq_buffer[i] = 0xFFFFFFFF; - } - - NBN_ConnectionStats stats = {0}; - - connection->stats = stats; - - for (int i = 0; i < NBN_CHANNEL_COUNT; i++) { - Channel_Init(&connection->channels[i], i, NBN_CHANNEL_RELIABLE); - } - - switch (driver_id) { -#ifdef NBN_UDP - case NBN_DRIVER_UDP: - connection->driver = &nbn_udp_driver; - break; -#endif // NBN_UDP - default: - NBN_LogError("Unsupported driver: %d", driver_id); - NBN_Abort(); - } - - return connection; -} - -static uint32_t Endpoint_BuildProtocolId(const char *protocol_name) { - uint32_t protocol_id = 2166136261; - - for (unsigned int i = 0; i < strlen(protocol_name); i++) { - protocol_id *= 16777619; - protocol_id ^= protocol_name[i]; - } - - return protocol_id; -} - -static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *packet, NBN_Connection *connection) { - (void)endpoint; - - NBN_LogTrace("Received packet %d (conn id: %d, ack: %d, messages count: %d)", packet->header.seq_number, - connection->id, packet->header.ack, packet->header.messages_count); - - if (NBN_Connection_ProcessReceivedPacket(endpoint, connection, packet, endpoint->time) < 0) - return NBN_ERROR; - - connection->last_recv_packet_time = endpoint->time; - connection->downloaded_bytes += packet->size; - - return 0; -} - -static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, uint8_t type, uint8_t channel_id) { - NBN_Message *message = &endpoint->write_message; - - message->header = (NBN_MessageHeader){0, 0, type, channel_id}; - message->sender = NULL; - message->type = NBN_OUTGOING_MESSAGE; - - endpoint->message_writer.position = 0; -} - -static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Message *message) { - NBN_Assert(!connection->is_closed || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); - NBN_Assert(!connection->is_stale); - - NBN_Channel *channel = &connection->channels[message->header.channel_id]; - - NBN_Assert(channel); - - NBN_LogTrace("Enqueue message of type %d on channel %d", message->header.type, channel->id); - - if (!Channel_AddOutgoingMessage(channel, message)) { - NBN_LogError("Failed to enqueue outgoing message of type %d on channel %d", message->header.type, - message->header.channel_id); - - return NBN_ERROR; - } - - return 0; -} - -static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) { -#if defined(NBN_PLATFORM_WINDOWS) - endpoint->time = GetTickCount64() / 1000.0; -#elif defined(__EMSCRIPTEN__) - endpoint->time = emscripten_get_now() / 1000; -#else - static struct timespec tp; - - if (clock_gettime(CLOCK_MONOTONIC_RAW, &tp) < 0) { - NBN_LogError("gettimeofday() failed"); - NBN_Abort(); - } - - endpoint->time = tp.tv_sec + (tp.tv_nsec / (double)1e9); -#endif // NBN_PLATFORM_WINDOWS -} - -#pragma endregion /* NBN_Endpoint */ - -#pragma region Network driver - -static void ClientDriver_OnPacketReceived(NBN_Packet *packet); -static void ServerDriver_OnClientConnected(NBN_Connection *); -static int ServerDriver_OnClientPacketReceived(NBN_Packet *); - -// TODO: just have the driver call functions from this file WTF -int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data) { - switch (ev) { - case NBN_DRIVER_CLI_PACKET_RECEIVED: - ClientDriver_OnPacketReceived((NBN_Packet *)data); - break; - - case NBN_DRIVER_SERV_CLIENT_CONNECTED: - ServerDriver_OnClientConnected((NBN_Connection *)data); - break; - - case NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED: - return ServerDriver_OnClientPacketReceived((NBN_Packet *)data); - } - - return 0; -} - -#pragma endregion /* Network driver */ - -#pragma region NBN_GameClient - -NBN_GameClient nbn_game_client; - -static int GameClient_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); -static int GameClient_HandleEvent(void); -static int GameClient_HandleMessageReceivedEvent(void); - -void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port) { - nbn_game_client.config = (NBN_GameClient_Config){.protocol_name = protocol_name, .host = host, .port = port}; - - nbn_game_client.client_data_writer.position = 0; -} - -NBN_Writer *NBN_GameClient_GetConnectionRequestDataWriter(void) { - NBN_Writer_Init(&nbn_game_client.client_data_writer, nbn_game_client.endpoint.connection_request_data_buffer, - sizeof(nbn_game_client.endpoint.connection_request_data_buffer)); - - return &nbn_game_client.client_data_writer; -} - -int NBN_GameClient_Start(void) { - NBN_GameClient_Config config = nbn_game_client.config; - const char *protocol_name = config.protocol_name; - const char *host = config.host; - uint16_t port = config.port; - uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - - Endpoint_Init(&nbn_game_client.endpoint, protocol_id, false); - - int driver_count = 0; - -#ifdef NBN_UDP - nbn_game_client.server_connection = NBN_GameClient_CreateServerConnection(NBN_DRIVER_UDP); - - if (nbn_udp_driver.impl.cli_start(&nbn_game_client, host, port) < 0) { - NBN_LogError("Failed to start driver %s", nbn_udp_driver.name); - return NBN_ERROR; - } - - driver_count++; -#endif // NBN_UDP - - if (driver_count != 1) { - NBN_LogError("Only one network driver can be activated for the client"); - NBN_Abort(); - } - - nbn_game_client.is_connected = false; - nbn_game_client.closed_code = -1; - - unsigned int connection_data_len = nbn_game_client.client_data_writer.position; - - NBN_GameClient_CreateReliableMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE); - NBN_Writer *writer = NBN_GameClient_GetMessageWriter(); - - if (connection_data_len > 0) { - NBN_Assert(connection_data_len <= sizeof(nbn_game_client.endpoint.connection_request_data_buffer)); - - NBN_Writer_WriteUInt32(writer, connection_data_len); - NBN_Writer_WriteBytes(writer, nbn_game_client.endpoint.connection_request_data_buffer, connection_data_len); - } else { - NBN_Writer_WriteUInt32(writer, 0); - } - - if (NBN_GameClient_EnqueueMessage() < 0) - return NBN_ERROR; - - NBN_LogInfo("Started"); - - return 0; -} - -void NBN_GameClient_Stop(void) { - // Poll remaining events to clear the event queue - while (NBN_GameClient_Poll() != NBN_NO_EVENT) { - } - - if (nbn_game_client.server_connection) { - if (!nbn_game_client.server_connection->is_closed && !nbn_game_client.server_connection->is_stale) { - NBN_LogInfo("Disconnecting..."); - - NBN_GameClient_CreateReliableMessage(NBN_DISCONNECTION_MESSAGE_TYPE); - - if (NBN_GameClient_EnqueueMessage() < 0) { - NBN_LogError("Failed to send disconnection message"); - } - - if (NBN_GameClient_Flush() < 0) { - NBN_LogError("Failed to send packets"); - } - - nbn_game_client.server_connection->is_closed = true; - - NBN_LogInfo("Disconnected"); - } - - free(nbn_game_client.server_connection); - nbn_game_client.server_connection = NULL; - } - - NBN_LogInfo("Stopping all drivers..."); - -#ifdef NBN_UDP - nbn_udp_driver.impl.cli_stop(&nbn_game_client); -#endif // NBN_UDP - - nbn_game_client.is_connected = false; - nbn_game_client.closed_code = -1; - nbn_game_client.endpoint.server_initial_data_len = 0; - - Endpoint_Deinit(&nbn_game_client.endpoint); - - NBN_LogInfo("Stopped"); -} - -NBN_Reader *NBN_GameClient_GetServerDataReader(void) { - NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - - NBN_Reader_Init(&nbn_game_client.server_data_reader, endpoint->server_initial_data_buffer, - endpoint->server_initial_data_len); - - return &nbn_game_client.server_data_reader; -} - -int NBN_GameClient_Poll(void) { - Endpoint_UpdateTime(&nbn_game_client.endpoint); - - NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - - if (nbn_game_client.server_connection->is_stale) - return NBN_NO_EVENT; - - if (NBN_EventQueue_IsEmpty(&endpoint->event_queue)) { - if (NBN_Connection_CheckIfStale(nbn_game_client.server_connection, nbn_game_client.endpoint.time)) { - nbn_game_client.server_connection->is_stale = true; - nbn_game_client.is_connected = false; - - NBN_LogInfo("Server connection is stale. Disconnected."); - - NBN_Event e; - - e.type = NBN_DISCONNECTED; - e.data.connection = (NBN_Connection *)NULL; - - if (!NBN_EventQueue_Enqueue(&endpoint->event_queue, e)) - return NBN_ERROR; - } else { -#ifdef NBN_UDP - if (nbn_udp_driver.impl.cli_recv_packets(&nbn_game_client) < 0) { - NBN_LogError("Failed to read packets from driver %s", nbn_udp_driver.name); - return NBN_ERROR; - } -#endif // NBN_UDP - - NBN_Connection *server_conn = nbn_game_client.server_connection; - - for (unsigned int i = 0; i < NBN_CHANNEL_COUNT; i++) { - NBN_Channel *channel = &server_conn->channels[i]; - - NBN_Message *msg; - - while ((msg = Channel_GetNextRecvedMessage(channel)) != NULL) { - NBN_LogTrace("Got message %d of type %d from channel %d", msg->header.id, msg->header.type, - channel->id); - - if (GameClient_ProcessReceivedMessage(msg, server_conn) < 0) { - NBN_LogError("Failed to process received message"); - - return NBN_ERROR; - } - } - } - - Connection_UpdateAverageDownloadBandwidth(server_conn, nbn_game_client.endpoint.time); - - server_conn->last_read_packets_time = nbn_game_client.endpoint.time; - } - } - - bool ret = NBN_EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_client.last_event); - - return ret ? GameClient_HandleEvent() : NBN_NO_EVENT; -} - -int NBN_GameClient_Flush(void) { - return NBN_Connection_FlushChannels(&nbn_game_client.endpoint, nbn_game_client.server_connection, - nbn_game_client.endpoint.protocol_id, nbn_game_client.endpoint.time); -} - -NBN_Writer *NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id) { - NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - NBN_Writer *writer = &endpoint->message_writer; - - NBN_Writer_Init(writer, endpoint->write_message.data, sizeof(endpoint->write_message.data)); - Endpoint_CreateOutgoingMessage(endpoint, type, channel_id); - - return writer; -} - -NBN_Writer *NBN_GameClient_CreateUnreliableMessage(uint8_t type) { - return NBN_GameClient_CreateMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE); -} - -NBN_Writer *NBN_GameClient_CreateReliableMessage(uint8_t type) { - return NBN_GameClient_CreateMessage(type, NBN_CHANNEL_RESERVED_RELIABLE); -} - -int NBN_GameClient_EnqueueMessage(void) { - NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - NBN_Message *message = &endpoint->write_message; - - message->header.length = endpoint->message_writer.position; - - if (Endpoint_EnqueueOutgoingMessage(endpoint, nbn_game_client.server_connection, message) < 0) { - NBN_LogError("Failed to create outgoing message"); - - return NBN_ERROR; - } - - return 0; -} - -NBN_Writer *NBN_GameClient_GetMessageWriter(void) { - NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - NBN_Writer *writer = &endpoint->message_writer; - - NBN_Writer_Init(writer, endpoint->write_message.data, sizeof(endpoint->write_message.data)); - - return writer; -} - -NBN_Reader *NBN_GameClient_GetMessageReader(void) { - NBN_Assert(nbn_game_client.last_event.type == NBN_MESSAGE_RECEIVED); - - NBN_MessageInfo msg_info = nbn_game_client.last_event.data.message_info; - NBN_Assert(msg_info.length > 0 && msg_info.data != NULL); - - NBN_Reader *reader = &nbn_game_client.endpoint.message_reader; - - NBN_Reader_Init(reader, msg_info.data, msg_info.length); - - return reader; -} - -NBN_Connection *NBN_GameClient_CreateServerConnection(NBN_Driver_ID driver_id) { - NBN_Connection *server_connection = Endpoint_CreateConnection(&nbn_game_client.endpoint, 0, driver_id); - -#ifdef NBN_DEBUG - server_connection->OnMessageAddedToRecvQueue = nbn_game_client.endpoint.OnMessageAddedToRecvQueue; -#endif - - nbn_game_client.server_connection = server_connection; - - return server_connection; -} - -NBN_MessageInfo NBN_GameClient_GetMessageInfo(void) { - NBN_Assert(nbn_game_client.last_event.type == NBN_MESSAGE_RECEIVED); - - return nbn_game_client.last_event.data.message_info; -} - -NBN_ConnectionStats NBN_GameClient_GetStats(void) { return nbn_game_client.server_connection->stats; } - -int NBN_GameClient_GetServerCloseCode(void) { return nbn_game_client.closed_code; } - -bool NBN_GameClient_IsConnected(void) { return nbn_game_client.is_connected; } - -#ifdef NBN_DEBUG - -void NBN_GameClient_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, void *cb) { - switch (cb_type) { - case NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE: - nbn_game_client.endpoint.OnMessageAddedToRecvQueue = (void (*)(NBN_Connection *, NBN_Message *))cb; - break; - } -} - -#endif /* NBN_DEBUG */ - -static int GameClient_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *server_connection) { - NBN_Assert(nbn_game_client.server_connection == server_connection); - - NBN_Event ev; - - ev.type = NBN_MESSAGE_RECEIVED; - - NBN_MessageInfo msg_info; - - msg_info.type = message->header.type; - msg_info.channel_id = message->header.channel_id; - msg_info.length = message->header.length; - msg_info.sender = server_connection; - msg_info.data = message->data; - - ev.data.message_info = msg_info; - - if (!NBN_EventQueue_Enqueue(&nbn_game_client.endpoint.event_queue, ev)) - return NBN_ERROR; - - return 0; -} - -static int GameClient_HandleEvent(void) { - switch (nbn_game_client.last_event.type) { - case NBN_MESSAGE_RECEIVED: - return GameClient_HandleMessageReceivedEvent(); - - default: - return nbn_game_client.last_event.type; - } -} - -static int GameClient_HandleMessageReceivedEvent(void) { - NBN_MessageInfo message_info = nbn_game_client.last_event.data.message_info; - NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - - int ret = NBN_NO_EVENT; - - if (message_info.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE) { - nbn_game_client.is_connected = false; - NBN_Reader *reader = NBN_GameClient_GetMessageReader(); - - if (NBN_Reader_ReadInt32(reader, &nbn_game_client.closed_code) < 0) { - NBN_LogError("Failed to read code from client closed message"); - - return NBN_ERROR; - } - - ret = NBN_DISCONNECTED; - } else if (message_info.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE) { - if (message_info.length < 4) { - NBN_LogError("Accept message invalid length"); - - return NBN_ERROR; - } - - NBN_Reader *reader = NBN_GameClient_GetMessageReader(); - unsigned int data_length; - - if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) { - NBN_LogError("Failed to read client data length"); - - return NBN_ERROR; - } - - if (data_length > 0) { - if (data_length > sizeof(endpoint->server_initial_data_buffer)) { - NBN_LogError("Received invalid connection data from the server"); - - return NBN_ERROR; - } - - if (NBN_Reader_ReadBytes(reader, endpoint->server_initial_data_buffer, data_length) < 0) { - NBN_LogError("Failed to read server data"); - - return NBN_ERROR; - } - } - - endpoint->server_initial_data_len = data_length; - nbn_game_client.is_connected = true; - ret = NBN_CONNECTED; - } else { - ret = NBN_MESSAGE_RECEIVED; - } - - return ret; -} - -#pragma endregion /* NBN_GameClient */ - -#pragma region Game client driver - -static void ClientDriver_OnPacketReceived(NBN_Packet *packet) { - // packets from server should always be valid - if (Endpoint_ProcessReceivedPacket(&nbn_game_client.endpoint, packet, nbn_game_client.server_connection) < 0) { - NBN_LogError("Received invalid packet from server"); - NBN_Abort(); - } -} - -#pragma endregion /* Game Client driver */ - -#pragma region NBN_GameServer - -NBN_GameServer nbn_game_server; - -static int GameServer_EnqueueMessageFor(NBN_Connection *client, NBN_Message *message); -static void GameServer_AddClient(NBN_Connection *); -static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection); -static void GameServer_AddClientToClosedList(NBN_Connection *client); -static int GameServer_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); -static int GameServer_CloseStaleClientConnections(void); -static void GameServer_RemoveClosedClientConnections(void); -static int GameServer_HandleEvent(void); -static int GameServer_HandleMessageReceivedEvent(void); - -void NBN_GameServer_Init(const char *protocol_name, uint16_t port) { - nbn_game_server.config = (NBN_GameServer_Config){.protocol_name = protocol_name, .port = port}; - nbn_game_server.server_data_writer.position = 0; - - hmdefault(nbn_game_server.clients, NULL); -} - -int NBN_GameServer_Start(void) { - NBN_GameServer_Config config = nbn_game_server.config; - const char *protocol_name = config.protocol_name; - uint16_t port = config.port; - uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - - Endpoint_Init(&nbn_game_server.endpoint, protocol_id, true); - - nbn_game_server.closed_clients_head = NULL; - - int driver_count = 0; - -#ifdef NBN_UDP - if (nbn_udp_driver.impl.serv_start(&nbn_game_server, port) < 0) { - NBN_LogError("Failed to start driver %s", nbn_udp_driver.name); - return NBN_ERROR; - } - - driver_count++; -#endif // NBN_UDP - - if (driver_count < 1) { - NBN_LogError("At least one network driver has to be activated"); - NBN_Abort(); - } - - NBN_LogInfo("Started (channel count: %d)", NBN_CHANNEL_COUNT); - - return 0; -} - -void NBN_GameServer_Stop(void) { - // Poll remaning events to clear the event queue - while (NBN_GameServer_Poll() != NBN_NO_EVENT) { - } - - for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { - NBN_Connection *conn = nbn_game_server.clients[i].value; - - conn->driver->impl.serv_cleanup_connection(&nbn_game_server, conn); - free(conn); - } - - hmfree(nbn_game_server.clients); - -#ifdef NBN_UDP - nbn_udp_driver.impl.serv_stop(&nbn_game_server); -#endif // NBN_UDP - - // Free closed clients list - NBN_ConnectionListNode *current = nbn_game_server.closed_clients_head; - - while (current) { - NBN_ConnectionListNode *next = current->next; - - free(current); - - current = next; - } - - nbn_game_server.closed_clients_head = NULL; - Endpoint_Deinit(&nbn_game_server.endpoint); - - NBN_LogInfo("Stopped"); -} - -static NBN_Connection_ID NBN_BuildConnectionHash(NBN_Connection_ID id, NBN_Driver_ID driver_id) { - NBN_Assert(id <= UINT64_MAX - 0xFF); - uint8_t driver_byte = NBN_DRIVER_UDP; - - return ((NBN_Connection_ID)driver_byte << 56) | id; -} - -NBN_Connection *NBN_GameServer_FindConnection(NBN_Connection_ID id) { return hmget(nbn_game_server.clients, id); } - -unsigned int NBN_GameServer_GetClientCount(void) { return hmlen(nbn_game_server.clients); } - -NBN_Connection *NBN_GameServer_GetClientByIndex(unsigned int index) { return nbn_game_server.clients[index].value; } - -int NBN_GameServer_Poll(void) { - Endpoint_UpdateTime(&nbn_game_server.endpoint); - - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - - if (NBN_EventQueue_IsEmpty(&endpoint->event_queue)) { - if (GameServer_CloseStaleClientConnections() < 0) - return NBN_ERROR; - -#ifdef NBN_UDP - if (nbn_udp_driver.impl.serv_recv_packets(&nbn_game_server) < 0) { - NBN_LogError("Failed to read packets from driver %s", nbn_udp_driver.name); - return NBN_ERROR; - } -#endif // NBN_UDP - - nbn_game_server.stats.download_bandwidth = 0; - - for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { - NBN_Connection *client = nbn_game_server.clients[i].value; - - for (unsigned int i = 0; i < NBN_CHANNEL_COUNT; i++) { - NBN_Channel *channel = &client->channels[i]; - - if (channel) { - NBN_Message *msg; - - while ((msg = Channel_GetNextRecvedMessage(channel)) != NULL) { - if (GameServer_ProcessReceivedMessage(msg, client) < 0) { - NBN_LogError("Failed to process received message"); - - return NBN_ERROR; - } - } - } - } - - if (!client->is_closed) - Connection_UpdateAverageDownloadBandwidth(client, endpoint->time); - - nbn_game_server.stats.download_bandwidth += client->stats.download_bandwidth; - client->last_read_packets_time = endpoint->time; - } - - GameServer_RemoveClosedClientConnections(); - } - - while (NBN_EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_server.last_event)) { - int ev = GameServer_HandleEvent(); - - if (ev != NBN_SKIP_EVENT) - return ev; - } - - return NBN_NO_EVENT; -} - -int NBN_GameServer_Flush(void) { - nbn_game_server.stats.upload_bandwidth = 0; - - GameServer_RemoveClosedClientConnections(); - - for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { - NBN_Connection *client = nbn_game_server.clients[i].value; - - NBN_Assert(!(client->is_closed && client->is_stale)); - - if (!client->is_stale && - NBN_Connection_FlushChannels(&nbn_game_server.endpoint, client, nbn_game_server.endpoint.protocol_id, - nbn_game_server.endpoint.time) < 0) { - return NBN_ERROR; - } - - nbn_game_server.stats.upload_bandwidth += client->stats.upload_bandwidth; - } - - return 0; -} - -NBN_Connection *NBN_GameServer_CreateClientConnection(NBN_Driver_ID driver_id, NBN_Connection_ID conn_id) { - // write the driver ID to the first byte of the connection ID to avoid collisions between drivers - conn_id = NBN_BuildConnectionHash(conn_id, driver_id); - NBN_Connection *client = Endpoint_CreateConnection(&nbn_game_server.endpoint, conn_id, driver_id); - -#ifdef NBN_DEBUG - client->OnMessageAddedToRecvQueue = nbn_game_server.endpoint.OnMessageAddedToRecvQueue; -#endif - - return client; -} - -int NBN_GameServer_CloseClientWithCode(NBN_Connection *conn, int code) { - return GameServer_CloseClientWithCode(conn, code, false); -} - -int NBN_GameServer_CloseClient(NBN_Connection *conn) { return GameServer_CloseClientWithCode(conn, -1, false); } - -NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id) { - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - NBN_Writer *writer = &endpoint->message_writer; - - NBN_Writer_Init(writer, endpoint->write_message.data, sizeof(endpoint->write_message.data)); - Endpoint_CreateOutgoingMessage(endpoint, type, channel_id); - - return writer; -} - -NBN_Writer *NBN_GameServer_CreateUnreliableMessage(uint8_t type) { - return NBN_GameServer_CreateMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE); -} - -NBN_Writer *NBN_GameServer_CreateReliableMessage(uint8_t type) { - return NBN_GameServer_CreateMessage(type, NBN_CHANNEL_RESERVED_RELIABLE); -} - -int NBN_GameServer_EnqueueMessageFor(NBN_Connection *conn) { - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - NBN_Message *message = &endpoint->write_message; - message->header.length = endpoint->message_writer.position; - - int ret = GameServer_EnqueueMessageFor(conn, message); - - return ret; -} - -int NBN_GameServer_EnqueueBroadcastMessage(void) { - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - NBN_Message *message = &endpoint->write_message; - message->header.length = endpoint->message_writer.position; - - int ret = 0; - - for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { - NBN_Connection *conn = nbn_game_server.clients[i].value; - - if (!conn->is_accepted || conn->is_closed) - continue; - - if (GameServer_EnqueueMessageFor(conn, &endpoint->write_message) < 0) { - NBN_LogError("Failed to send message to client %d when broadcasting", conn->id); - ret = NBN_ERROR; - break; - } - } - - return ret; -} - -NBN_Reader *NBN_GameServer_GetMessageReader(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); - - NBN_MessageInfo msg_info = nbn_game_server.last_event.data.message_info; - NBN_Assert(msg_info.length > 0 && msg_info.data != NULL); - - NBN_Reader *reader = &nbn_game_server.endpoint.message_reader; - - NBN_Reader_Init(reader, msg_info.data, msg_info.length); - - return reader; -} - -NBN_Writer *NBN_GameServer_GetConnectionDataWriter(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); - NBN_Assert(nbn_game_server.last_event.data.connection != NULL); - - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - - NBN_Writer_Init(&nbn_game_server.server_data_writer, endpoint->server_initial_data_buffer, - sizeof(endpoint->server_initial_data_buffer)); - - return &nbn_game_server.server_data_writer; -} - -int NBN_GameServer_AcceptIncomingConnection(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); - NBN_Assert(nbn_game_server.last_event.data.connection != NULL); - - unsigned data_length = nbn_game_server.server_data_writer.position; - NBN_Connection *client = nbn_game_server.last_event.data.connection; - NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); - - if (data_length > 0) { - NBN_Assert(data_length <= sizeof(nbn_game_server.endpoint.server_initial_data_buffer)); - - NBN_Writer_WriteUInt32(writer, data_length); - NBN_Writer_WriteBytes(writer, nbn_game_server.endpoint.server_initial_data_buffer, data_length); - } else { - NBN_Writer_WriteUInt32(writer, 0); - } - - if (NBN_GameServer_EnqueueMessageFor(client) < 0) - return NBN_ERROR; - - client->is_accepted = true; - - NBN_LogTrace("Client %d has been accepted", client->id); - - return 0; -} - -int NBN_GameServer_RejectIncomingConnectionWithCode(int code) { - NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); - NBN_Assert(nbn_game_server.last_event.data.connection != NULL); - - NBN_Connection *conn = nbn_game_server.last_event.data.connection; - NBN_LogDebug("Rejecting incoming connection %d (code: %d)", conn->id, code); - - return GameServer_CloseClientWithCode(conn, code, false); -} - -int NBN_GameServer_RejectIncomingConnection(void) { return NBN_GameServer_RejectIncomingConnectionWithCode(-1); } - -NBN_Connection *NBN_GameServer_GetIncomingConnection(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); - NBN_Assert(nbn_game_server.last_event.data.connection != NULL); - - return nbn_game_server.last_event.data.connection; -} - -NBN_Reader *NBN_GameServer_GetConnectionRequestDataReader(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); - - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - - NBN_Reader_Init(&nbn_game_server.client_data_reader, endpoint->connection_request_data_buffer, - endpoint->client_connection_request_data_len); - - return &nbn_game_server.client_data_reader; -} - -NBN_DisconnectionInfo NBN_GameServer_GetDisconnectionInfo(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_DISCONNECTED); - - return nbn_game_server.last_event.data.disconnection; -} - -NBN_MessageInfo NBN_GameServer_GetMessageInfo(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); - - return nbn_game_server.last_event.data.message_info; -} - -NBN_GameServerStats NBN_GameServer_GetStats(void) { return nbn_game_server.stats; } - -#ifdef NBN_DEBUG - -void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, void *cb) { - switch (cb_type) { - case NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE: - nbn_game_server.endpoint.OnMessageAddedToRecvQueue = (void (*)(NBN_Connection *, NBN_Message *))cb; - break; - } -} - -#endif /* NBN_DEBUG */ - -static int GameServer_EnqueueMessageFor(NBN_Connection *client, NBN_Message *message) { - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - - /* Only NBN_CLIENT_ACCEPTED_MESSAGE_TYPE and NBN_CLIENT_CLOSED_MESSAGE_TYPE messages can be sent to an - * unaccapted client */ - NBN_Assert(client->is_accepted || message->header.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE || - message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); - - if (Endpoint_EnqueueOutgoingMessage(endpoint, client, message) < 0) { - NBN_LogError("Failed to create outgoing message for client %d", client->id); - - /* Do not close the client if we failed to send the close client message to avoid infinite loops */ - if (message->header.type != NBN_CLIENT_CLOSED_MESSAGE_TYPE) { - GameServer_CloseClientWithCode(client, -1, false); - - return NBN_ERROR; - } - } - - return 0; -} - -static void GameServer_AddClient(NBN_Connection *client) { - NBN_Assert(hmgeti(nbn_game_server.clients, client->id) == -1); - - hmput(nbn_game_server.clients, client->id, client); - NBN_LogDebug("Added client %llu", client->id); -} - -static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection) { - if (!client->is_closed && client->is_accepted) { - if (!disconnection) { - NBN_Event e; - - e.type = NBN_CLIENT_DISCONNECTED; - e.data.disconnection = (NBN_DisconnectionInfo){client->id, client->user_data}; - - if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, e)) - return NBN_ERROR; - } - } - - if (client->is_stale) { - NBN_LogDebug("Closing stale connection %d", client->id); - - GameServer_AddClientToClosedList(client); - client->is_closed = true; - - return 0; - } - - NBN_LogDebug("Closing active connection %d (will send a disconnection message)", client->id); - - GameServer_AddClientToClosedList(client); - client->is_closed = true; - - if (!disconnection) { - NBN_LogDebug("Send close message for client %d (code: %d)", client->id, code); - - NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE); - NBN_Writer_WriteInt32(writer, code); - NBN_GameServer_EnqueueMessageFor(client); - } - - return 0; -} - -static void GameServer_AddClientToClosedList(NBN_Connection *client) { - if (client->is_closed) - return; - - // TODO: do we need to use a linked list, maybe use stb dynamic array? - NBN_ConnectionListNode *node = (NBN_ConnectionListNode *)malloc(sizeof(NBN_ConnectionListNode)); - - node->conn = client; - node->next = NULL; - - if (nbn_game_server.closed_clients_head == NULL) { - // list is empty - nbn_game_server.closed_clients_head = node; - node->prev = NULL; - } else { - // list is not empty, add node at the end - NBN_ConnectionListNode *tail = nbn_game_server.closed_clients_head; - - while (tail->next != NULL) - tail = tail->next; - - node->prev = tail; - tail->next = node; - } -} - -static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *client) { - NBN_Event ev; - - ev.type = NBN_CLIENT_MESSAGE_RECEIVED; - - NBN_MessageInfo msg_info; - - msg_info.type = message->header.type; - msg_info.channel_id = message->header.channel_id; - msg_info.length = message->header.length; - msg_info.sender = client; - msg_info.data = message->data; - - NBN_LogDebug("Received message (type: %d, id: %d) from client %lld", message->header.type, message->header.id, - client->id); - ev.data.message_info = msg_info; - - if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, ev)) - return NBN_ERROR; - - return 0; -} - -static int GameServer_CloseStaleClientConnections(void) { - for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { - NBN_Connection *client = nbn_game_server.clients[i].value; - - if (!client->is_stale && NBN_Connection_CheckIfStale(client, nbn_game_server.endpoint.time)) { - NBN_LogInfo("Client %lld connection is stale, closing it.", client->id); - - client->is_stale = true; - - if (GameServer_CloseClientWithCode(client, -1, false) < 0) - return NBN_ERROR; - } - } - - return 0; -} - -static void GameServer_RemoveClosedClientConnections(void) { - NBN_ConnectionListNode *current = nbn_game_server.closed_clients_head; - - while (current) { - NBN_ConnectionListNode *prev = current->prev; - NBN_ConnectionListNode *next = current->next; - NBN_Connection *client = current->conn; - - NBN_Assert(client->id > 0); - - if (client->is_stale) { - NBN_LogDebug("Remove closed client connection (ID: %d)", client->id); - - client->driver->impl.serv_cleanup_connection(&nbn_game_server, - client); // Notify the driver to clean up the connection - - int ret = hmdel(nbn_game_server.clients, client->id); - NBN_Assert(ret == 1); - - // Destroy the connection - - free(client); - - // Remove the connection from the closed clients list - - free(current); - - if (current == nbn_game_server.closed_clients_head) { - // delete the head of the list - NBN_ConnectionListNode *new_head = next; - - if (new_head) { - new_head->prev = NULL; - } - - nbn_game_server.closed_clients_head = new_head; - } else { - // delete a node in the middle of the list - prev->next = next; - - if (next) - next->prev = prev; - } - } - - current = next; - } -} - -static int GameServer_HandleEvent(void) { - return nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED ? GameServer_HandleMessageReceivedEvent() - : nbn_game_server.last_event.type; -} - -// TODO: big ass function -static int GameServer_HandleMessageReceivedEvent(void) { - NBN_Event *last_event = &nbn_game_server.last_event; - NBN_MessageInfo message_info = last_event->data.message_info; - NBN_Connection *sender = message_info.sender; - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - - if (sender->is_closed || sender->is_stale) - return NBN_SKIP_EVENT; - - if (message_info.type == NBN_DISCONNECTION_MESSAGE_TYPE) { - NBN_LogInfo("Received disconnection message from client %d", sender->id); - - if (GameServer_CloseClientWithCode(sender, -1, true) < 0) - return NBN_ERROR; - - sender->is_stale = true; - - last_event->type = NBN_CLIENT_DISCONNECTED; - last_event->data.disconnection = (NBN_DisconnectionInfo){sender->id, sender->user_data}; - - GameServer_RemoveClosedClientConnections(); - - return NBN_CLIENT_DISCONNECTED; - } - - if (message_info.type != NBN_CONNECTION_REQUEST_MESSAGE_TYPE) { - nbn_game_server.server_data_writer.position = 0; - return NBN_CLIENT_MESSAGE_RECEIVED; - } - - // at this point we know it's a connection request - NBN_Assert(message_info.type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE); - - if (message_info.length < 4) { - NBN_LogError("Connection request invalid length"); - - return NBN_ERROR; - } - - NBN_Reader *reader = NBN_GameServer_GetMessageReader(); - unsigned int data_length; - - if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) { - NBN_LogError("Failed to read client data length"); - - return NBN_ERROR; - } - - if (data_length > 0) { - if (data_length > sizeof(endpoint->connection_request_data_buffer)) { - NBN_LogError("Received invalid connection request data"); - - return NBN_ERROR; - } - - if (NBN_Reader_ReadBytes(reader, nbn_game_server.endpoint.connection_request_data_buffer, data_length) < 0) { - NBN_LogError("Failed to read client data"); - - return NBN_ERROR; - } - } - - nbn_game_server.endpoint.client_connection_request_data_len = data_length; - - NBN_Event e; - - e.type = NBN_NEW_CONNECTION; - e.data.connection = sender; - - if (!NBN_EventQueue_Enqueue(&endpoint->event_queue, e)) - return NBN_ERROR; - - return NBN_NO_EVENT; -} - -#pragma endregion /* NBN_GameServer */ - -#pragma region Game server driver - -static void ServerDriver_OnClientConnected(NBN_Connection *client) { GameServer_AddClient(client); } - -static int ServerDriver_OnClientPacketReceived(NBN_Packet *packet) { - if (Endpoint_ProcessReceivedPacket(&nbn_game_server.endpoint, packet, packet->sender) < 0) { - NBN_LogError("An error occured while processing packet from client %d, closing the client", packet->sender->id); - - return GameServer_CloseClientWithCode(packet->sender, -1, false); - } - - return 0; -} - -#pragma endregion /* Game server driver */ - -#ifdef NBN_UDP - -#pragma region UDP driver - -#ifdef NBN_PLATFORM_WINDOWS - -static char err_msg[32]; - -#endif - -static int NBN_UDP_InitSocket(void); -static void NBN_UDP_DeinitSocket(void); -static int NBN_UDP_BindSocket(uint16_t); -static char *NBN_UDP_GetLastErrorMessage(void); - -static int NBN_UDP_InitSocket(void) { -#ifdef NBN_PLATFORM_WINDOWS - WSADATA wsa; - int err = WSAStartup(MAKEWORD(2, 2), &wsa); - if (err < 0) { - NBN_LogError("WSAStartup() failed"); - - return NBN_ERROR; - } -#endif - - if ((nbn_udp_sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) - return NBN_ERROR; - -#if defined(NBN_PLATFORM_WINDOWS) - DWORD non_blocking = 1; - - if (ioctlsocket(nbn_udp_sock, FIONBIO, &non_blocking) != 0) { - NBN_LogError("ioctlsocket() failed: %s", GetLastErrorMessage()); - - return NBN_ERROR; - } -#elif defined(NBN_PLATFORM_MAC) || defined(NBN_PLATFORM_UNIX) - int non_blocking = 1; - - if (fcntl(nbn_udp_sock, F_SETFL, O_NONBLOCK, non_blocking) < 0) { - NBN_LogError("fcntl() failed: %s", NBN_UDP_GetLastErrorMessage()); - - return NBN_ERROR; - } -#endif - - return 0; -} - -static void NBN_UDP_DeinitSocket(void) { - closesocket(nbn_udp_sock); - -#ifdef NBN_PLATFORM_WINDOWS - WSACleanup(); -#endif -} - -static int NBN_UDP_BindSocket(uint16_t port) { - SOCKADDR_IN sin; - - sin.sin_addr.s_addr = htonl(INADDR_ANY); - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - - if (bind(nbn_udp_sock, (SOCKADDR *)&sin, sizeof(sin)) < 0) { - NBN_LogError("bind() failed: %s", NBN_UDP_GetLastErrorMessage()); - - return NBN_ERROR; - } - - return 0; -} - -static NBN_Connection_ID NBN_UDP_BuildConnectionID(NBN_IPAddress address) { - return ((NBN_Connection_ID)address.host << 2) | address.port; -} - -static NBN_Connection *NBN_UDP_FindOrCreateClientConnectionByAddress(NBN_IPAddress address) { - NBN_Connection_ID conn_id = NBN_UDP_BuildConnectionID(address); - conn_id = NBN_BuildConnectionHash(conn_id, NBN_DRIVER_UDP); - NBN_Connection *conn = NBN_GameServer_FindConnection(conn_id); - - if (conn == NULL) { - // this is a new connection - - conn = NBN_GameServer_CreateClientConnection(NBN_DRIVER_UDP, conn_id); - conn->driver_data.udp.ip_address = address; - - NBN_LogDebug("New UDP connection (id: %llu, addr: %d, port: %d)", conn->id, address.host, address.port); - - if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, conn) < 0) { - NBN_LogError("Failed to raise game server event"); - - return NULL; - } - - return conn; - } - - return conn; -} - -static int NBN_UDP_ParseIpAddress(const char *host, uint16_t port, NBN_IPAddress *address) { - // TODO: need to malloc here? - size_t host_len = strlen(host); - char *dup_host = (char *)malloc(host_len + 1); - memcpy(dup_host, host, host_len + 1); - uint8_t arr[4]; - - for (int i = 0; i < 4; i++) { - char *s; - - // TODO: replace strtok with strsep - if ((s = strtok(i == 0 ? dup_host : NULL, ".")) == NULL) - return NBN_ERROR; - - char *end = NULL; - int v = strtol(s, &end, 10); - - if (end == s || v < 0 || v > 255) - return NBN_ERROR; - - arr[i] = (uint8_t)v; - } - - address->host = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3]; - address->port = port; - - free(dup_host); - - return 0; -} - -static char *NBN_UDP_GetLastErrorMessage(void) { -#ifdef NBN_PLATFORM_WINDOWS - snprintf(err_msg, sizeof(err_msg), "%d", WSAGetLastError()); - - return err_msg; -#else - return strerror(errno); -#endif -} - -int NBN_UDP_Server_Start(NBN_GameServer *server, uint16_t port) { - if (NBN_UDP_InitSocket() < 0) - return NBN_ERROR; - - if (NBN_UDP_BindSocket(port) < 0) - return NBN_ERROR; - - return 0; -} - -void NBN_UDP_Server_Stop(NBN_GameServer *server) { NBN_UDP_DeinitSocket(); } - -int NBN_UDP_Server_RecvPackets(NBN_GameServer *server) { - NBN_Packet packet = {0}; - SOCKADDR_IN src_addr; - socklen_t src_addr_len = sizeof(src_addr); - - while (true) { - int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, - &src_addr_len); - - if (bytes <= 0) - break; - - if (bytes <= NBN_PACKET_HEADER_SIZE) - continue; - - if (NBN_Packet_InitRead(&packet, server->endpoint.protocol_id, bytes) < 0) { - NBN_LogDebug("Discarded invalid packet"); - continue; - } - - NBN_IPAddress ip_address; - ip_address.host = ntohl(src_addr.sin_addr.s_addr); - ip_address.port = ntohs(src_addr.sin_port); - - NBN_LogDebug("Received valid UDP packet from %d:%d", ip_address.host, ip_address.port); - - packet.sender = NBN_UDP_FindOrCreateClientConnectionByAddress(ip_address); - - if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet) < 0) { - NBN_LogError("Failed to raise game server event"); - - return NBN_ERROR; - } - } - - return 0; -} - -void NBN_UDP_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *connection) {} - -int NBN_UDP_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *connection) { - NBN_IPAddress dest_address = connection->driver_data.udp.ip_address; - SOCKADDR_IN dest_addr; - - dest_addr.sin_addr.s_addr = htonl(dest_address.host); - dest_addr.sin_family = AF_INET; - dest_addr.sin_port = htons(dest_address.port); - - if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, - sizeof(dest_addr)) == SOCKET_ERROR) { - NBN_LogError("sendto() failed: %s", NBN_UDP_GetLastErrorMessage()); - - return NBN_ERROR; - } - - return 0; -} - -int NBN_UDP_Client_Start(NBN_GameClient *client, const char *host, uint16_t port) { - NBN_IPAddress *ip_address = &client->server_connection->driver_data.udp.ip_address; - - if (NBN_UDP_ParseIpAddress(host, port, ip_address) < 0) { - NBN_LogError("Failed to resolve IP address from %s", host); - - return NBN_ERROR; - } - - if (NBN_UDP_InitSocket() < 0) - return NBN_ERROR; - - if (NBN_UDP_BindSocket(0) < 0) - return NBN_ERROR; - - return 0; -} - -void NBN_UDP_Client_Stop(NBN_GameClient *client) { NBN_UDP_DeinitSocket(); } - -int NBN_UDP_Client_RecvPackets(NBN_GameClient *client) { - NBN_IPAddress server_address = client->server_connection->driver_data.udp.ip_address; - static NBN_Packet packet = {0}; - SOCKADDR_IN src_addr; - socklen_t src_addr_len = sizeof(src_addr); - - while (true) { - int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, - &src_addr_len); - - if (bytes <= 0) - break; - - if (bytes < NBN_PACKET_HEADER_SIZE) - continue; - - uint32_t host = ntohl(src_addr.sin_addr.s_addr); - uint16_t port = ntohs(src_addr.sin_port); - - if (host != server_address.host || port != server_address.port) - continue; - - if (NBN_Packet_InitRead(&packet, client->endpoint.protocol_id, bytes) < 0) { - NBN_LogDebug("Discarded invalid packet"); - continue; - } - - packet.sender = client->server_connection; - - NBN_Driver_RaiseEvent(NBN_DRIVER_CLI_PACKET_RECEIVED, &packet); - } - - return 0; -} - -int NBN_UDP_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet) { - NBN_IPAddress server_address = client->server_connection->driver_data.udp.ip_address; - SOCKADDR_IN dest_addr; - - dest_addr.sin_addr.s_addr = htonl(server_address.host); - dest_addr.sin_family = AF_INET; - dest_addr.sin_port = htons(server_address.port); - - if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, - sizeof(dest_addr)) == SOCKET_ERROR) { - NBN_LogError("sendto() failed: %s", NBN_UDP_GetLastErrorMessage()); - - return NBN_ERROR; - } - - return 0; -} - -#pragma enregion /* UDP driver */ - -#endif // NBN_UDP - -#pragma region Packet simulator - -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - -#define RAND_RATIO_BETWEEN(min, max) (((rand() % (int)((max * 100.f) - (min * 100.f) + 1)) + (min * 100.f)) / 100.f) -#define RAND_RATIO RAND_RATIO_BETWEEN(0, 1) - -#ifdef NBN_PLATFORM_WINDOWS -DWORD WINAPI PacketSimulator_Routine(LPVOID); -#else -static void *PacketSimulator_Routine(void *); -#endif - -static int PacketSimulator_SendPacket(NBN_PacketSimulator *, NBN_Packet *, NBN_Connection *receiver); -static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimulator *); - -void NBN_PacketSimulator_Init(NBN_PacketSimulator *packet_simulator, NBN_Endpoint *endpoint) { - packet_simulator->endpoint = endpoint; - packet_simulator->running = false; - packet_simulator->ping = 0; - packet_simulator->jitter = 0; - packet_simulator->packet_loss_ratio = 0; - packet_simulator->total_dropped_packets = 0; - packet_simulator->packet_duplication_ratio = 0; - packet_simulator->head_packet = NULL; - packet_simulator->tail_packet = NULL; - packet_simulator->packet_count = 0; - -#ifdef NBN_PLATFORM_WINDOWS - packet_simulator->queue_mutex = CreateMutex(NULL, FALSE, NULL); -#else - packet_simulator->queue_mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER; -#endif -} - -int NBN_PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN_Packet *packet, - NBN_Connection *receiver) { -#ifdef NBN_PLATFORM_WINDOWS - WaitForSingleObject(packet_simulator->queue_mutex, INFINITE); -#else - pthread_mutex_lock(&packet_simulator->queue_mutex); -#endif - - /* Compute jitter in range [ -jitter, +jitter ]. - * Jitter is converted from seconds to milliseconds for the random operation below. - */ - - int jitter = packet_simulator->jitter * 1000; - - jitter = (jitter > 0) ? (rand() % (jitter * 2)) - jitter : 0; - - NBN_PacketSimulatorEntry *entry = (NBN_PacketSimulatorEntry *)malloc(sizeof(NBN_PacketSimulatorEntry)); - - entry->delay = packet_simulator->ping + (double)jitter / 1000; /* and converted back to seconds */ - entry->receiver = receiver; - entry->enqueued_at = packet_simulator->endpoint->time; - - memcpy(&entry->packet, packet, sizeof(NBN_Packet)); - - if (packet_simulator->packet_count > 0) { - entry->prev = packet_simulator->tail_packet; - entry->next = NULL; - - packet_simulator->tail_packet->next = entry; - packet_simulator->tail_packet = entry; - } else // the list is empty - { - entry->prev = NULL; - entry->next = NULL; - - packet_simulator->head_packet = entry; - packet_simulator->tail_packet = entry; - } - - packet_simulator->packet_count++; - -#ifdef NBN_PLATFORM_WINDOWS - ReleaseMutex(packet_simulator->queue_mutex); -#else - pthread_mutex_unlock(&packet_simulator->queue_mutex); -#endif - - return 0; -} - -void NBN_PacketSimulator_Start(NBN_PacketSimulator *packet_simulator) { -#ifdef NBN_PLATFORM_WINDOWS - packet_simulator->thread = CreateThread(NULL, 0, PacketSimulator_Routine, packet_simulator, 0, NULL); -#else - pthread_create(&packet_simulator->thread, NULL, PacketSimulator_Routine, packet_simulator); -#endif - - packet_simulator->running = true; -} - -void NBN_PacketSimulator_Stop(NBN_PacketSimulator *packet_simulator) { - packet_simulator->running = false; - -#ifdef NBN_PLATFORM_WINDOWS - WaitForSingleObject(packet_simulator->thread, INFINITE); -#else - pthread_join(packet_simulator->thread, NULL); -#endif -} - -#ifdef NBN_PLATFORM_WINDOWS -DWORD WINAPI PacketSimulator_Routine(LPVOID arg) -#else -static void *PacketSimulator_Routine(void *arg) -#endif -{ - NBN_PacketSimulator *packet_simulator = (NBN_PacketSimulator *)arg; - - while (packet_simulator->running) { -#ifdef NBN_PLATFORM_WINDOWS - WaitForSingleObject(packet_simulator->queue_mutex, INFINITE); -#else - pthread_mutex_lock(&packet_simulator->queue_mutex); -#endif - - NBN_PacketSimulatorEntry *entry = packet_simulator->head_packet; - - while (entry) { - NBN_PacketSimulatorEntry *next = entry->next; - - if (packet_simulator->endpoint->time - entry->enqueued_at < entry->delay) { - entry = next; - - continue; - } - - PacketSimulator_SendPacket(packet_simulator, &entry->packet, entry->receiver); - - for (unsigned int i = 0; i < PacketSimulator_GetRandomDuplicatePacketCount(packet_simulator); i++) { - NBN_LogDebug("Duplicate packet %d (count: %d)", entry->packet.header.seq_number, i + 1); - - PacketSimulator_SendPacket(packet_simulator, &entry->packet, entry->receiver); - } - - // remove the entry from the packet list - if (entry == packet_simulator->head_packet) // it's the head of the list - { - NBN_PacketSimulatorEntry *new_head = entry->next; - - if (new_head) - new_head->prev = NULL; - else - packet_simulator->tail_packet = NULL; - - packet_simulator->head_packet = new_head; - } else if (entry == packet_simulator->tail_packet) // it's the tail of the list - { - NBN_PacketSimulatorEntry *new_tail = entry->prev; - - new_tail->next = NULL; - packet_simulator->tail_packet = new_tail; - } else // it's in the middle of the list - { - entry->prev->next = entry->next; - entry->next->prev = entry->prev; - } - - packet_simulator->packet_count--; - - // release the memory allocated for the entry - free(entry); - - entry = next; - } - -#ifdef NBN_PLATFORM_WINDOWS - ReleaseMutex(packet_simulator->queue_mutex); -#else - pthread_mutex_unlock(&packet_simulator->queue_mutex); -#endif - } - -#ifdef NBN_PLATFORM_WINDOWS - return 0; -#else - return NULL; -#endif -} - -static int PacketSimulator_SendPacket(NBN_PacketSimulator *packet_simulator, NBN_Packet *packet, - NBN_Connection *receiver) { - if (RAND_RATIO < packet_simulator->packet_loss_ratio) { - packet_simulator->total_dropped_packets++; - NBN_LogDebug("Drop packet %d (Total dropped packets: %d)", packet->header.seq_number, - packet_simulator->total_dropped_packets); - - return 0; - } - - NBN_Driver *driver = receiver->driver; - - if (receiver->endpoint->is_server) { - if (receiver->is_stale) - return 0; - - return driver->impl.serv_send_packet_to(&nbn_game_server, packet, receiver); - } else { - return driver->impl.cli_send_packet(&nbn_game_client, packet); - } -} - -static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimulator *packet_simulator) { - if (RAND_RATIO < packet_simulator->packet_duplication_ratio) - return rand() % 10 + 1; - - return 0; -} - -#endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ - -#pragma endregion /* Packet simulator */ - -#endif /* NBN_IMPLEMENTATION */ - -#pragma endregion /* Implementations */ diff --git a/soak/CMakeLists.txt b/soak/CMakeLists.txt index bcbe56d..e779cf9 100644 --- a/soak/CMakeLists.txt +++ b/soak/CMakeLists.txt @@ -12,15 +12,29 @@ endif (CPP_COMPILE) unset(CPP_COMPILE) -add_executable(client client.c soak.c logging.c cargs.c) -add_executable(server server.c soak.c logging.c cargs.c) +add_executable(client client.c soak.c logging.c cargs.c ../nbnet.c) +add_executable(server server.c soak.c logging.c cargs.c ../nbnet.c) add_compile_options(-Wall -Wextra -Wpedantic -std=c99) -target_compile_definitions(client PUBLIC NBN_DEBUG NBN_DISABLE_STALE_CONNECTION_DETECTION NBN_USE_PACKET_SIMULATOR SOAK_CLIENT) -target_compile_definitions(server PUBLIC NBN_DEBUG NBN_DISABLE_STALE_CONNECTION_DETECTION NBN_USE_PACKET_SIMULATOR SOAK_SERVER) +target_compile_definitions(client PUBLIC NBN_DEBUG SOAK_CLIENT) +target_compile_definitions(server PUBLIC NBN_DEBUG SOAK_SERVER) option(WEBRTC_NATIVE OFF) +option(UDP OFF) + +# compile with UDP driver +if (UDP) + # can't compile UDP driver with emscripten + if (EMSCRIPTEN) + message(SEND_ERROR "Can't compile UDP driver with emscripten") + endif (EMSCRIPTEN) + + message("Compiling with UDP driver") + + target_compile_definitions(server PUBLIC NBN_UDP) + target_compile_definitions(client PUBLIC NBN_UDP) +endif (UDP) # compile with C WebRTC driver if (WEBRTC_NATIVE) diff --git a/soak/client.c b/soak/client.c index 778e830..677ebde 100644 --- a/soak/client.c +++ b/soak/client.c @@ -21,11 +21,7 @@ */ -#define NBN_UDP -#define NBN_IMPLEMENTATION - #include "soak.h" - #include typedef struct { @@ -63,12 +59,12 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { // number of messages sent but not yet to be acked unsigned int pending_message_count = channel->last_sent_message_id - channel->last_recved_message_id; - Soak_LogInfo("Compute number of soak messages to send (sent: %d, pending: %d, remaining: %d)", - channel->sent_message_count, pending_message_count, remaining_message_count); + LogInfo("Compute number of soak messages to send (sent: %d, pending: %d, remaining: %d)", + channel->sent_message_count, pending_message_count, remaining_message_count); // don't send anything on this tick if we have reached the max number of unacked messages if (pending_message_count >= SOAK_CLIENT_MAX_PENDING_MESSAGES) { - Soak_LogInfo("Max number of pending messages has been reached, not sending anything this tick"); + LogInfo("Max number of pending messages has been reached, not sending anything this tick"); return 0; } @@ -77,7 +73,7 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { unsigned int send_message_count = MIN(SOAK_CLIENT_MAX_PENDING_MESSAGES - pending_message_count, remaining_message_count); - Soak_LogInfo("Will send %d soak messages this tick", send_message_count); + LogInfo("Will send %d soak messages this tick", send_message_count); for (int i = 0; i < send_message_count; i++) { int percent = rand() % 100 + 1; @@ -105,7 +101,7 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { entry->free = false; entry->channel_id = channel_id; - Soak_LogInfo("Send soak message (id: %d, data length: %d)", msg_id, data_length); + LogInfo("Send soak message (id: %d, data length: %d)", msg_id, data_length); // TODO: support big messages NBN_Writer *writer = NBN_GameClient_CreateMessage(SOAK_MESSAGE_SMALL, channel->id); @@ -132,14 +128,14 @@ static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) NBN_Reader *reader = NBN_GameClient_GetMessageReader(); if (SoakMessage_Read(reader, &msg_id, recv_buffer, &data_length) < 0) { - Soak_LogError("Failed to read soak message"); + LogError("Failed to read soak message"); return -1; } if (msg_id != channel->last_recved_message_id + 1) { - Soak_LogError("Expected to receive message %d but received message %d (channel_id: %d)", - channel->last_recved_message_id + 1, msg_id, channel_id); + LogError("Expected to receive message %d but received message %d (channel_id: %d)", + channel->last_recved_message_id + 1, msg_id, channel_id); return -1; } @@ -150,15 +146,15 @@ static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) assert(entry->channel_id == channel_id); if (data_length != entry->length) { - Soak_LogError("Expected message %d to have length %d but was %d (channel_id: %d)", msg_id, entry->length, - data_length); + LogError("Expected message %d to have length %d but was %d (channel_id: %d)", msg_id, entry->length, + data_length); return -1; } if (memcmp(recv_buffer, entry->data, data_length) != 0) { - Soak_LogError("Received invalid data for message %d (data length: %d, channel_id: %d)", msg_id, data_length, - channel_id); + LogError("Received invalid data for message %d (data length: %d, channel_id: %d)", msg_id, data_length, + channel_id); return -1; } @@ -169,16 +165,16 @@ static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) SoakOptions options = Soak_GetOptions(); - Soak_LogInfo("Received soak message (length: %d, %d/%d) on channel %d", data_length, msg_id, channel->message_count, - channel_id); + LogInfo("Received soak message (length: %d, %d/%d) on channel %d", data_length, msg_id, channel->message_count, + channel_id); if (channel->last_recved_message_id == channel->message_count) { - Soak_LogInfo("Received all soak message echoes on channel %d", channel_id); + LogInfo("Received all soak message echoes on channel %d", channel_id); done_channel_count++; } if (done_channel_count >= NBN_CHANNEL_COUNT) { - Soak_LogInfo("Received all soak message echoes on all channels"); + LogInfo("Received all soak message echoes on all channels"); Soak_Stop(); return SOAK_DONE; @@ -195,7 +191,7 @@ static int HandleReceivedMessage(SoakChannel *channels) { if (msg.type == SOAK_MESSAGE_SMALL) { ret = HandleReceivedSoakMessage(msg.channel_id, channels); } else { - Soak_LogError("Received unexpected message (type: %d, channel_id: %d)", msg.type, msg.channel_id); + LogError("Received unexpected message (type: %d, channel_id: %d)", msg.type, msg.channel_id); ret = -1; } @@ -216,12 +212,12 @@ static int Tick(void *data) { case NBN_DISCONNECTED: connected = false; - Soak_LogInfo("Disconnected from server (code: %d)", NBN_GameClient_GetServerCloseCode()); + LogInfo("Disconnected from server (code: %d)", NBN_GameClient_GetServerCloseCode()); Soak_Stop(); return 0; case NBN_CONNECTED: - Soak_LogInfo("Connected to server"); + LogInfo("Connected to server"); connected = true; break; @@ -237,14 +233,14 @@ static int Tick(void *data) { SoakChannel *channel = &channels[c]; if (SendSoakMessages(channel, channel->id) < 0) { - Soak_LogError("An error occured while sending messages on channel %d", c); + LogError("An error occured while sending messages on channel %d", c); return -1; } } } if (NBN_GameClient_Flush() < 0) { - Soak_LogError("Failed to flush game client send queue. Exit"); + LogError("Failed to flush game client send queue. Exit"); return -1; } @@ -253,7 +249,8 @@ static int Tick(void *data) { } int main(int argc, char *argv[]) { - Soak_SetLogLevel(LOG_TRACE); + NBN_SetLogFunction(Log); + SetLogLevel(NBN_LOG_DEBUG); if (Soak_ReadCommandLine(argc, argv) < 0) return -1; @@ -291,7 +288,7 @@ int main(int argc, char *argv[]) { NBN_GameClient_Init(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT); if (NBN_GameClient_Start() < 0) { - Soak_LogError("Failed to start game client. Exit"); + LogError("Failed to start game client. Exit"); #ifdef __EMSCRIPTEN__ emscripten_force_exit(1); @@ -301,7 +298,7 @@ int main(int argc, char *argv[]) { } if (Soak_Init(argc, argv) < 0) { - Soak_LogError("Failed to initialize soak test"); + LogError("Failed to initialize soak test"); return 1; } diff --git a/soak/logging.c b/soak/logging.c index a852645..949936c 100644 --- a/soak/logging.c +++ b/soak/logging.c @@ -33,88 +33,41 @@ * under the terms of the MIT license. See `log.c` for details. */ -static struct -{ +static struct { void *udata; - log_LockFn lock; FILE *fp; int level; int quiet; } L; -static const char *level_names[] = { - "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}; +static const char *level_names[] = {"ERROR", "INFO", "WARNING", "DEBUG"}; #ifdef LOG_USE_COLOR -static const char *level_colors[] = { - "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"}; +static const char *level_colors[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"}; #endif -static void lock(void) -{ - if (L.lock) - { - L.lock(L.udata, 1); - } -} - -static void unlock(void) -{ - if (L.lock) - { - L.lock(L.udata, 0); - } -} - -void log_set_udata(void *udata) -{ - L.udata = udata; -} - -void log_set_lock(log_LockFn fn) -{ - L.lock = fn; -} - -void log_set_fp(FILE *fp) -{ - L.fp = fp; -} - -void log_set_level(int level) -{ +void SetLogLevel(NBN_LogLevel level) { L.level = level; + NBN_SetLogLevel(level); } -void log_set_quiet(int enable) -{ - L.quiet = enable ? 1 : 0; -} - -void log_log(int level, const char *file, int line, const char *fmt, ...) -{ - if (level < L.level) - { +void Log(NBN_LogLevel level, const char *file, int line, const char *fmt, ...) { + if (L.level < level) { return; } - /* Acquire lock */ - lock(); - /* Get current time */ time_t t = time(NULL); struct tm *lt = localtime(&t); /* Log to stderr */ - if (!L.quiet) - { + if (!L.quiet) { va_list args; char buf[16]; buf[strftime(buf, sizeof(buf), "%H:%M:%S", lt)] = '\0'; #ifdef LOG_USE_COLOR - fprintf( - stderr, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", - buf, level_colors[level], level_names[level], file, line); + fprintf(stderr, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buf, level_colors[level], level_names[level], file, + line); #else fprintf(stderr, "%s %-5s %s:%d: ", buf, level_names[level], file, line); #endif @@ -126,8 +79,7 @@ void log_log(int level, const char *file, int line, const char *fmt, ...) } /* Log to file */ - if (L.fp) - { + if (L.fp) { va_list args; char buf[32]; buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; @@ -138,7 +90,4 @@ void log_log(int level, const char *file, int line, const char *fmt, ...) fprintf(L.fp, "\n"); fflush(L.fp); } - - /* Release lock */ - unlock(); } diff --git a/soak/logging.h b/soak/logging.h index 0e7291f..300f18f 100644 --- a/soak/logging.h +++ b/soak/logging.h @@ -37,29 +37,18 @@ #include #include #include +#include "../nbnet.h" #define LOG_VERSION "0.1.0" -typedef void (*log_LockFn)(void *udata, int lock); - -enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; - #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) -#define Soak_LogTrace(...) log_log(LOG_TRACE, __FILENAME__, __LINE__, __VA_ARGS__) -#define Soak_LogDebug(...) log_log(LOG_DEBUG, __FILENAME__, __LINE__, __VA_ARGS__) -#define Soak_LogInfo(...) log_log(LOG_INFO, __FILENAME__, __LINE__, __VA_ARGS__) -#define Soak_LogWarn(...) log_log(LOG_WARN, __FILENAME__, __LINE__, __VA_ARGS__) -#define Soak_LogError(...) log_log(LOG_ERROR, __FILENAME__, __LINE__, __VA_ARGS__) -#define Soak_LogFatal(...) log_log(LOG_FATAL, __FILENAME__, __LINE__, __VA_ARGS__) -#define Soak_SetLogLevel(level) log_set_level(level) - -void log_set_udata(void *udata); -void log_set_lock(log_LockFn fn); -void log_set_fp(FILE *fp); -void log_set_level(int level); -void log_set_quiet(int enable); - -void log_log(int level, const char *file, int line, const char *fmt, ...); +#define LogDebug(...) Log(NBN_LOG_DEBUG, __FILENAME__, __LINE__, __VA_ARGS__) +#define LogInfo(...) Log(NBN_LOG_INFO, __FILENAME__, __LINE__, __VA_ARGS__) +#define LogWarn(...) Log(NBN_LOG_WARN, __FILENAME__, __LINE__, __VA_ARGS__) +#define LogError(...) Log(NBN_LOG_ERROR, __FILENAME__, __LINE__, __VA_ARGS__) + +void SetLogLevel(NBN_LogLevel level); +void Log(NBN_LogLevel level, const char *file, int line, const char *fmt, ...); #endif /* SOAK_LOGGING_H */ diff --git a/soak/server.c b/soak/server.c index d685b6d..ed332d2 100644 --- a/soak/server.c +++ b/soak/server.c @@ -24,10 +24,7 @@ #include #include - -#define NBN_UDP -#define NBN_IMPLEMENTATION - +#include #include "soak.h" typedef struct { @@ -84,7 +81,7 @@ static void HandleNewConnection(void) { conn->user_data = soak_client; - Soak_LogInfo("Client has connected (ID: %llu)", conn->id); + LogInfo("Client has connected (ID: %llu)", conn->id); } static void HandleClientDisconnection(NBN_DisconnectionInfo info) { @@ -92,7 +89,7 @@ static void HandleClientDisconnection(NBN_DisconnectionInfo info) { assert(soak_client != NULL); - Soak_LogInfo("Client has disconnected (ID: %d)", info.conn_id); + LogInfo("Client has disconnected (ID: %d)", info.conn_id); free(soak_client->channels); free(soak_client); @@ -121,14 +118,14 @@ static void EchoReceivedSoakMessages(void) { SoakMessage_Write(writer, msg_entry->msg_id, msg_entry->data, msg_entry->length); - Soak_LogInfo("Send soak message %d's echo (length: %d) to client %llu", msg_entry->msg_id, - msg_entry->length, conn->id); + LogInfo("Send soak message %d's echo (length: %d) to client %llu", msg_entry->msg_id, msg_entry->length, + conn->id); if (NBN_GameServer_EnqueueMessageFor(conn) < 0) { - Soak_LogError("Failed to send soak message to client %llu, closing client", conn->id); + LogError("Failed to send soak message to client %llu, closing client", conn->id); if (NBN_GameServer_CloseClient(conn) < 0) { - Soak_LogError("Failed to close client %llu", conn->id); + LogError("Failed to close client %llu", conn->id); abort(); } @@ -159,22 +156,22 @@ static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_Connection *sender, static uint8_t recv_buffer[SOAK_MESSAGE_BIG_MAX_DATA_LENGTH]; if (SoakMessage_Read(reader, &msg_id, recv_buffer, &data_length) < 0) { - Soak_LogError("Failed to read soak message"); + LogError("Failed to read soak message"); return -1; } if (msg_id != channel->last_recved_message_id + 1) { - Soak_LogError("Expected to receive message %d but received message %d (from client: %d)", - channel->last_recved_message_id + 1, msg_id, sender); + LogError("Expected to receive message %d but received message %d (from client: %d)", + channel->last_recved_message_id + 1, msg_id, sender); soak_client->error = true; return -1; } - Soak_LogInfo("Received soak message %d (length: %d) from client %llu on channel %d", msg_id, data_length, - sender->id, channel_id); + LogInfo("Received soak message %d (length: %d) from client %llu on channel %d", msg_id, data_length, sender->id, + channel_id); channel->recved_messages_count++; channel->last_recved_message_id = msg_id; @@ -184,7 +181,7 @@ static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_Connection *sender, assert(channel->echo_queue.count < SOAK_CLIENT_MAX_PENDING_MESSAGES); assert(msg_entry->length == 0); - Soak_LogInfo("Enqueue soak message %d's echo for client %llu on channel %d", msg_id, sender->id, channel_id); + LogInfo("Enqueue soak message %d's echo for client %llu on channel %d", msg_id, sender->id, channel_id); memcpy(msg_entry->data, recv_buffer, data_length); msg_entry->msg_id = msg_id; @@ -206,7 +203,7 @@ static void HandleReceivedMessage(void) { case SOAK_MESSAGE_SMALL: if (HandleReceivedSoakMessage(reader, msg_info.sender, msg_info.channel_id) < 0) { if (NBN_GameServer_CloseClient(msg_info.sender) < 0) { - Soak_LogError("Failed to close client %llu", msg_info.sender->id); + LogError("Failed to close client %llu", msg_info.sender->id); abort(); } @@ -217,10 +214,10 @@ static void HandleReceivedMessage(void) { // TODO: support big messages default: - Soak_LogError("Received unexpected message (type: %d, channel_id: %d)", msg_info.type, msg_info.channel_id); + LogError("Received unexpected message (type: %d, channel_id: %d)", msg_info.type, msg_info.channel_id); if (NBN_GameServer_CloseClient(msg_info.sender) < 0) { - Soak_LogError("Failed to close client %llu", msg_info.sender->id); + LogError("Failed to close client %llu", msg_info.sender->id); abort(); } @@ -256,7 +253,7 @@ static int Tick(void *data) { EchoReceivedSoakMessages(); if (NBN_GameServer_Flush() < 0) { - Soak_LogError("Failed to flush game server send queue. Exit"); + LogError("Failed to flush game server send queue. Exit"); return -1; } @@ -269,7 +266,8 @@ static void SigintHandler(int dummy) { Soak_Stop(); } int main(int argc, char *argv[]) { signal(SIGINT, SigintHandler); - Soak_SetLogLevel(LOG_DEBUG); + NBN_SetLogFunction(Log); + SetLogLevel(NBN_LOG_DEBUG); if (Soak_ReadCommandLine(argc, argv) < 0) return -1; @@ -298,13 +296,13 @@ int main(int argc, char *argv[]) { NBN_GameServer_Init(SOAK_PROTOCOL_NAME, SOAK_PORT); if (NBN_GameServer_Start()) { - Soak_LogError("Failed to start game server"); + LogError("Failed to start game server"); return 1; } if (Soak_Init(argc, argv) < 0) { - Soak_LogError("Failed to initialize soak test"); + LogError("Failed to initialize soak test"); return 1; } diff --git a/soak/soak.c b/soak/soak.c index 21e1c7f..947e99f 100644 --- a/soak/soak.c +++ b/soak/soak.c @@ -22,6 +22,7 @@ */ +#include "logging.h" #include #include #include @@ -68,8 +69,8 @@ int Soak_Init(int argc, char *argv[]) { SoakOptions options = Soak_GetOptions(); - Soak_LogInfo("Soak test initialized (Packet loss: %f, Packet duplication: %f, Ping: %f, Jitter: %f)", - options.packet_loss, options.packet_duplication, options.ping, options.jitter); + LogInfo("Soak test initialized (Packet loss: %f, Packet duplication: %f, Ping: %f, Jitter: %f)", + options.packet_loss, options.packet_duplication, options.ping, options.jitter); /* Packet simulator configuration */ #ifdef SOAK_CLIENT @@ -90,8 +91,8 @@ int Soak_Init(int argc, char *argv[]) { } void Soak_Deinit(void) { - Soak_LogInfo("Done."); - Soak_LogInfo("Memory report:\n"); + LogInfo("Done."); + LogInfo("Memory report:\n"); // TODO } @@ -184,7 +185,7 @@ int Soak_MainLoop(int (*Tick)(void *), void *data) { void Soak_Stop(void) { running = false; - Soak_LogInfo("Soak test stopped"); + LogInfo("Soak test stopped"); } SoakOptions Soak_GetOptions(void) { return soak_options; } diff --git a/soak/soak.h b/soak/soak.h index 3c92d53..3bf6df6 100644 --- a/soak/soak.h +++ b/soak/soak.h @@ -34,17 +34,7 @@ #include #include - #include "logging.h" - -/* nbnet logging */ -#define NBN_LogInfo Soak_LogInfo -#define NBN_LogTrace Soak_LogTrace -#define NBN_LogDebug Soak_LogDebug -#define NBN_LogError Soak_LogError -#define NBN_LogWarning Soak_LogWarn - -#define NBN_CHANNEL_COUNT 4 #include "../nbnet.h" #define SOAK_PROTOCOL_NAME "nbnet_soak" From b3c41af2ed7a6877ca46d0a024ed226421438017 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Fri, 30 Jan 2026 14:46:50 +0100 Subject: [PATCH 36/85] fix issue with unreliable channel --- examples/echo/CMakeLists.txt | 42 ++++++++---------- examples/echo/client.c | 7 +-- examples/echo/server.c | 12 ++---- examples/echo/shared.h | 16 ------- nbnet.c | 83 ++++++++++++++++-------------------- nbnet.h | 41 +----------------- soak/CMakeLists.txt | 2 + soak/client.c | 5 ++- 8 files changed, 65 insertions(+), 143 deletions(-) diff --git a/examples/echo/CMakeLists.txt b/examples/echo/CMakeLists.txt index 80ab0df..3856f30 100644 --- a/examples/echo/CMakeLists.txt +++ b/examples/echo/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.5) project(echo) option(CPP_COMPILE OFF) -option(ENABLE_TLS OFF) +option(UDP OFF) option(WEBRTC_NATIVE OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -23,44 +23,38 @@ if(CMAKE_COMPILER_IS_GNUCXX) add_compile_options(-Wextra -Wpedantic) endif (CMAKE_COMPILER_IS_GNUCXX) -add_executable(echo_client client.c shared.c) -add_executable(echo_server server.c shared.c) +add_executable(echo_client client.c shared.c ../../nbnet.c) +add_executable(echo_server server.c shared.c ../../nbnet.c) -if (ENABLE_TLS) - message("Compile with TLS enabled") - target_compile_definitions(echo_server PUBLIC NBN_TLS) - target_compile_definitions(echo_client PUBLIC NBN_TLS) -endif (ENABLE_TLS) +if (UDP) + # can't compile UDP driver with emscripten + if (EMSCRIPTEN) + message(SEND_ERROR "Can't compile UDP driver with emscripten") + endif (EMSCRIPTEN) -unset(ENABLE_TLS) + message("Compiling with UDP driver") -if (WEBRTC_NATIVE_SERVER) + target_compile_definitions(echo_server PUBLIC NBN_UDP) + target_compile_definitions(echo_client PUBLIC NBN_UDP) +endif (UDP) + +if (WEBRTC_NATIVE) if (EMSCRIPTEN) - message(FATAL_ERROR "Cannot compile native webrtc driver with emscripten") + message(FATAL_ERROR "Cannot compile native WebRTC driver with emscripten") endif (EMSCRIPTEN) - message("Compile server with webrtc native driver") + message("Compile with webrtc native driver") target_compile_definitions(echo_server PUBLIC NBN_WEBRTC_NATIVE) target_link_libraries(echo_server ${LIBDATACHANNEL_LIBRARY_PATH} m) target_include_directories(echo_server PUBLIC "${LIBDATACHANNEL_INCLUDE_PATH}") -endif (WEBRTC_NATIVE_SERVER) - -unset(WEBRTC_NATIVE_SERVER) - -if (WEBRTC_NATIVE_CLIENT) - if (EMSCRIPTEN) - message(FATAL_ERROR "Cannot compile native webrtc driver with emscripten") - endif (EMSCRIPTEN) - - message("Compile client with webrtc native driver") target_compile_definitions(echo_client PUBLIC NBN_WEBRTC_NATIVE) target_link_libraries(echo_client ${LIBDATACHANNEL_LIBRARY_PATH} m) target_include_directories(echo_client PUBLIC "${LIBDATACHANNEL_INCLUDE_PATH}") -endif (WEBRTC_NATIVE_CLIENT) +endif (WEBRTC_NATIVE) -unset(WEBRTC_NATIVE_CLIENT) +unset(WEBRTC_NATIVE) target_compile_definitions(echo_client PUBLIC NBN_DEBUG) target_compile_definitions(echo_server PUBLIC NBN_DEBUG) diff --git a/examples/echo/client.c b/examples/echo/client.c index 2dcdaae..d748209 100644 --- a/examples/echo/client.c +++ b/examples/echo/client.c @@ -23,6 +23,7 @@ #include #include #include +#include // Has to be defined in exactly *one* source file before including the nbnet header #define NBNET_IMPL @@ -74,7 +75,7 @@ void OnMessageReceived(void) { } int SendEcho(const char *msg) { - NBN_Writer *writer = NBN_GameClient_CreateReliableMessage(ECHO_MESSAGE_TYPE); + NBN_Writer *writer = NBN_GameClient_CreateMessage(ECHO_MESSAGE_TYPE, 0); unsigned int length = strlen(msg); NBN_Writer_WriteUInt32(writer, length); @@ -142,10 +143,6 @@ int main(int argc, char *argv[]) { #endif // NBN_WEBRTC_NATIVE -#if !defined(__EMSCRIPTEN__) && !defined(NBN_WEBRTC_NATIVE) - NBN_UDP_Register(); // Register the UDP driver -#endif // __EMSCRIPTEN__ - // Initialize the client // Start the client with a protocol name (must be the same than the one used by the server) diff --git a/examples/echo/server.c b/examples/echo/server.c index 8012eb2..fae09aa 100644 --- a/examples/echo/server.c +++ b/examples/echo/server.c @@ -23,14 +23,12 @@ #include #include #include - -// Has to be defined in exactly *one* source file before including the nbnet header -#define NBNET_IMPL +#include #include "shared.h" static NBN_Connection *connection = NULL; -static uint32_t conn_id; +static NBN_Connection_ID conn_id; // Echo the received message static int EchoReceivedMessage(void) { @@ -57,7 +55,7 @@ static int EchoReceivedMessage(void) { msg_info.channel_id); // create and send an echo of the received message - NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(ECHO_MESSAGE_TYPE); + NBN_Writer *writer = NBN_GameServer_CreateMessage(ECHO_MESSAGE_TYPE, 0); NBN_Writer_WriteUInt32(writer, length); NBN_Writer_WriteBytes(writer, (uint8_t *)msg_str, length); @@ -110,10 +108,6 @@ int main(int argc, const char **argv) { NBN_WebRTC_C_Register(cfg); #endif // NBN_WEBRTC_NATIVE -#if !defined(__EMSCRIPTEN__) && !defined(NBN_WEBRTC_NATIVE) - NBN_UDP_Register(); // Register the UDP driver -#endif - // Start the server with a protocol name and a port NBN_GameServer_Init(ECHO_PROTOCOL_NAME, ECHO_EXAMPLE_PORT); diff --git a/examples/echo/shared.h b/examples/echo/shared.h index a683e09..b610b63 100644 --- a/examples/echo/shared.h +++ b/examples/echo/shared.h @@ -46,22 +46,6 @@ void Log(int, const char *, ...); #include "../../nbnet.h" -#ifdef __EMSCRIPTEN__ - -#include "../../net_drivers/webrtc.h" - -#else - -#include "../../net_drivers/udp.h" - -#ifdef NBN_WEBRTC_NATIVE - -#include "../../net_drivers/webrtc_c.h" - -#endif // NBN_WEBRTC_NATIVE - -#endif // __EMSCRIPTEN__ - void EchoSleep(double); #endif /* ECHO_EXAMPLE_SHARED_H */ diff --git a/nbnet.c b/nbnet.c index cca6927..828b6e6 100644 --- a/nbnet.c +++ b/nbnet.c @@ -22,6 +22,7 @@ */ +#include #define STB_DS_IMPLEMENTATION #include "stb_ds.h" @@ -54,24 +55,16 @@ void NBN_SetLogFunction(NBN_LogFunc func) { nbn_log_func = func; } void NBN_SetLogLevel(NBN_LogLevel level) { nbn_log_level = level; } #define NBN_Abort abort +#define NBN_Assert(cond) assert(cond) -#ifdef NBN_DISABLE_ASSERTS - -#define NBN_Assert(cond) \ - do { \ - } while (0); - -#else - -// TODO: log -#define NBN_Assert(cond) \ - do { \ - if (!(cond)) { \ - NBN_Abort(); \ - } \ - } while (0); - -#endif +#define SEQUENCE_NUMBER_GT(seq1, seq2) \ + ((seq1 > seq2 && (seq1 - seq2) <= 32767) || (seq1 < seq2 && (seq2 - seq1) >= 32767)) +#define SEQUENCE_NUMBER_GTE(seq1, seq2) \ + ((seq1 >= seq2 && (seq1 - seq2) <= 32767) || (seq1 <= seq2 && (seq2 - seq1) >= 32767)) +#define SEQUENCE_NUMBER_LT(seq1, seq2) \ + ((seq1 < seq2 && (seq2 - seq1) <= 32767) || (seq1 > seq2 && (seq1 - seq2) >= 32767)) +#define SEQUENCE_NUMBER_LTE(seq1, seq2) \ + ((seq1 <= seq2 && (seq2 - seq1) <= 32767) || (seq1 >= seq2 && (seq1 - seq2) >= 32767)) #ifdef NBN_UDP @@ -402,7 +395,7 @@ static void Channel_Init(NBN_Channel *channel, uint8_t id, NBN_ChannelType type) channel->next_outgoing_message_id = 0; channel->next_recv_message_id = 0; channel->outgoing_message_count = 0; - channel->last_received_message_id = 0; + channel->last_received_message_id = -1; channel->next_outgoing_message_slot = 0; channel->oldest_unacked_message_id = 0; channel->most_recent_message_id = 0; @@ -503,22 +496,26 @@ static bool Channel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *messag } static NBN_Message *Channel_GetNextRecvedMessage(NBN_Channel *channel) { - NBN_IncomingMessage *inc_msg = - &channel->incoming_messages_buffer[channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; - if (channel->type == NBN_CHANNEL_UNRELIABLE) { - while (SEQUENCE_NUMBER_LT(channel->next_recv_message_id, channel->last_received_message_id)) { - if (!inc_msg->free && inc_msg->message.header.id == channel->next_recv_message_id) { + while (SEQUENCE_NUMBER_LTE(channel->next_recv_message_id, channel->last_received_message_id)) { + NBN_IncomingMessage *inc_msg = + &channel->incoming_messages_buffer[channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; + uint16_t msg_id = channel->next_recv_message_id; + + channel->next_recv_message_id++; + + if (!inc_msg->free && inc_msg->message.header.id == msg_id) { inc_msg->free = true; return &inc_msg->message; } - - channel->next_recv_message_id++; } return NULL; } else if (channel->type == NBN_CHANNEL_RELIABLE) { + NBN_IncomingMessage *inc_msg = + &channel->incoming_messages_buffer[channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; + if (!inc_msg->free && inc_msg->message.header.id == channel->next_recv_message_id) { inc_msg->free = true; channel->next_recv_message_id++; @@ -703,7 +700,7 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection connection->OnMessageAddedToRecvQueue(connection, &message); #endif } else { - LogDebug("Received message %d : discarded", message.header.id); + LogDebug("Message %d was discarded by channel %d", message.header.id, channel->id); } } @@ -1172,7 +1169,8 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, NBN_Con for (int i = 0; i < NBN_CHANNEL_COUNT; i++) { // channels are created in unreliable mode by default // TODO: expose a method to change the reliability mode of a channel - Channel_Init(&connection->channels[i], i, NBN_CHANNEL_RELIABLE); + // make sure we have at least one reliable channel for library messages + Channel_Init(&connection->channels[i], i, NBN_CHANNEL_UNRELIABLE); } switch (driver_id) { @@ -1341,7 +1339,8 @@ int NBN_GameClient_Start(void) { unsigned int connection_data_len = nbn_game_client.client_data_writer.position; - NBN_GameClient_CreateReliableMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE); + // TODO: make sure it's a reliable channel + NBN_GameClient_CreateMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE, 0); NBN_Writer *writer = NBN_GameClient_GetMessageWriter(); if (connection_data_len > 0) { @@ -1370,7 +1369,8 @@ void NBN_GameClient_Stop(void) { if (!nbn_game_client.server_connection->is_closed && !nbn_game_client.server_connection->is_stale) { LogInfo("Disconnecting..."); - NBN_GameClient_CreateReliableMessage(NBN_DISCONNECTION_MESSAGE_TYPE); + // TODO: make sure it's a reliable channel + NBN_GameClient_CreateMessage(NBN_DISCONNECTION_MESSAGE_TYPE, 0); if (NBN_GameClient_EnqueueMessage() < 0) { LogError("Failed to send disconnection message"); @@ -1479,6 +1479,8 @@ int NBN_GameClient_Flush(void) { } NBN_Writer *NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id) { + NBN_Assert(channel_id < NBN_CHANNEL_COUNT); + NBN_Endpoint *endpoint = &nbn_game_client.endpoint; NBN_Writer *writer = &endpoint->message_writer; @@ -1488,14 +1490,6 @@ NBN_Writer *NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id) { return writer; } -NBN_Writer *NBN_GameClient_CreateUnreliableMessage(uint8_t type) { - return NBN_GameClient_CreateMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE); -} - -NBN_Writer *NBN_GameClient_CreateReliableMessage(uint8_t type) { - return NBN_GameClient_CreateMessage(type, NBN_CHANNEL_RESERVED_RELIABLE); -} - int NBN_GameClient_EnqueueMessage(void) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; NBN_Message *message = &endpoint->write_message; @@ -1869,6 +1863,7 @@ int NBN_GameServer_CloseClientWithCode(NBN_Connection *conn, int code) { int NBN_GameServer_CloseClient(NBN_Connection *conn) { return GameServer_CloseClientWithCode(conn, -1, false); } NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id) { + NBN_Assert(channel_id < NBN_CHANNEL_COUNT); NBN_Endpoint *endpoint = &nbn_game_server.endpoint; NBN_Writer *writer = &endpoint->message_writer; @@ -1878,14 +1873,6 @@ NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id) { return writer; } -NBN_Writer *NBN_GameServer_CreateUnreliableMessage(uint8_t type) { - return NBN_GameServer_CreateMessage(type, NBN_CHANNEL_RESERVED_UNRELIABLE); -} - -NBN_Writer *NBN_GameServer_CreateReliableMessage(uint8_t type) { - return NBN_GameServer_CreateMessage(type, NBN_CHANNEL_RESERVED_RELIABLE); -} - int NBN_GameServer_EnqueueMessageFor(NBN_Connection *conn) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; NBN_Message *message = &endpoint->write_message; @@ -1950,7 +1937,8 @@ int NBN_GameServer_AcceptIncomingConnection(void) { unsigned data_length = nbn_game_server.server_data_writer.position; NBN_Connection *client = nbn_game_server.last_event.data.connection; - NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); + // TODO: make sure it's a reliable channel + NBN_Writer *writer = NBN_GameServer_CreateMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE, 0); if (data_length > 0) { NBN_Assert(data_length <= sizeof(nbn_game_server.endpoint.server_initial_data_buffer)); @@ -2086,7 +2074,8 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool if (!disconnection) { LogDebug("Send close message for client %d (code: %d)", client->id, code); - NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE); + // TODO: make sure it's a reliable channel + NBN_Writer *writer = NBN_GameServer_CreateMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE, 0); NBN_Writer_WriteInt32(writer, code); NBN_GameServer_EnqueueMessageFor(client); } diff --git a/nbnet.h b/nbnet.h index 234cf94..862cbd6 100644 --- a/nbnet.h +++ b/nbnet.h @@ -255,20 +255,11 @@ int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); #pragma region NBN_Channel #ifndef NBN_CHANNEL_COUNT -/** - * Number of channels per connection. - */ #define NBN_CHANNEL_COUNT 2 #endif #define NBN_CHANNEL_BUFFER_SIZE 256 -/* Library reserved unreliable ordered channel */ -#define NBN_CHANNEL_RESERVED_UNRELIABLE 0 - -/* Library reserved reliable ordered channel */ -#define NBN_CHANNEL_RESERVED_RELIABLE 1 - typedef enum NBN_ChannelType { NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_RELIABLE } NBN_ChannelType; struct NBN_Channel { @@ -342,6 +333,7 @@ typedef struct NBN_IPAddress { typedef uint64_t NBN_Connection_ID; struct NBN_Connection { + // TODO: use 32 bits ID and 64 bits hash for the hashtable NBN_Connection_ID id; double last_recv_packet_time; /* Used to detect stale connections */ double last_flush_time; /* Last time the send queue was flushed */ @@ -623,10 +615,6 @@ int NBN_GameClient_Flush(void); // TODO: doc NBN_Writer *NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id); -// TODO: doc -NBN_Writer *NBN_GameClient_CreateUnreliableMessage(uint8_t type); -// TODO: doc -NBN_Writer *NBN_GameClient_CreateReliableMessage(uint8_t type); // TODO: doc int NBN_GameClient_EnqueueMessage(void); @@ -796,10 +784,6 @@ int NBN_GameServer_CloseClientWithCode(NBN_Connection *conn, int code); // TODO: doc NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id); // TODO: doc -NBN_Writer *NBN_GameServer_CreateUnreliableMessage(uint8_t type); -// TODO: doc -NBN_Writer *NBN_GameServer_CreateReliableMessage(uint8_t type); -// TODO: doc int NBN_GameServer_EnqueueMessageFor(NBN_Connection *conn); // TODO: doc int NBN_GameServer_EnqueueBroadcastMessage(void); @@ -936,27 +920,4 @@ int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data); #pragma endregion /* Network driver */ -#pragma region Utils - -#ifndef MIN -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -#ifndef MAX -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - -#ifndef ABS -#define ABS(v) (((v) > 0) ? (v) : -(v)) -#endif - -#define SEQUENCE_NUMBER_GT(seq1, seq2) \ - ((seq1 > seq2 && (seq1 - seq2) <= 32767) || (seq1 < seq2 && (seq2 - seq1) >= 32767)) -#define SEQUENCE_NUMBER_GTE(seq1, seq2) \ - ((seq1 >= seq2 && (seq1 - seq2) <= 32767) || (seq1 <= seq2 && (seq2 - seq1) >= 32767)) -#define SEQUENCE_NUMBER_LT(seq1, seq2) \ - ((seq1 < seq2 && (seq2 - seq1) <= 32767) || (seq1 > seq2 && (seq1 - seq2) >= 32767)) - -#pragma endregion /* Utils */ - #endif /* NBNET_H */ diff --git a/soak/CMakeLists.txt b/soak/CMakeLists.txt index e779cf9..57c55a5 100644 --- a/soak/CMakeLists.txt +++ b/soak/CMakeLists.txt @@ -19,6 +19,8 @@ add_compile_options(-Wall -Wextra -Wpedantic -std=c99) target_compile_definitions(client PUBLIC NBN_DEBUG SOAK_CLIENT) target_compile_definitions(server PUBLIC NBN_DEBUG SOAK_SERVER) +target_compile_definitions(client PUBLIC NBN_CHANNEL_COUNT=4) +target_compile_definitions(server PUBLIC NBN_CHANNEL_COUNT=4) option(WEBRTC_NATIVE OFF) option(UDP OFF) diff --git a/soak/client.c b/soak/client.c index 677ebde..87f9853 100644 --- a/soak/client.c +++ b/soak/client.c @@ -21,8 +21,9 @@ */ -#include "soak.h" #include +#include +#include "soak.h" typedef struct { uint8_t data[SOAK_MESSAGE_BIG_MAX_LENGTH]; @@ -71,7 +72,7 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { // number of messages to send on this tick unsigned int send_message_count = - MIN(SOAK_CLIENT_MAX_PENDING_MESSAGES - pending_message_count, remaining_message_count); + fmin(SOAK_CLIENT_MAX_PENDING_MESSAGES - pending_message_count, remaining_message_count); LogInfo("Will send %d soak messages this tick", send_message_count); From 85acfc711f90b44ad8ecf2ee61f1c3af8253110a Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Fri, 6 Feb 2026 08:53:21 +0100 Subject: [PATCH 37/85] rework API wip --- examples/echo/client.c | 10 +- examples/echo/server.c | 12 +- nbnet.c | 761 +++++++++++++++++++++++++++++++---------- nbnet.h | 736 ++++++++++----------------------------- soak/client.c | 7 +- soak/server.c | 5 + soak/soak.c | 18 - soak/soak.h | 1 - 8 files changed, 777 insertions(+), 773 deletions(-) diff --git a/examples/echo/client.c b/examples/echo/client.c index d748209..653d6f3 100644 --- a/examples/echo/client.c +++ b/examples/echo/client.c @@ -59,7 +59,7 @@ void OnMessageReceived(void) { assert(msg_info.type == ECHO_MESSAGE_TYPE); - NBN_Reader *reader = NBN_GameClient_GetMessageReader(); + NBN_Reader *reader = NBN_GameClient_ReadMessage(); unsigned int length; int res; @@ -168,7 +168,7 @@ int main(int argc, char *argv[]) { int ev; // Poll for client events - while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) { + while ((ev = NBN_GameClient_Poll()) != NBN_CLIENT_NO_EVENT) { if (ev < 0) { Log(LOG_ERROR, "An error occured while polling client events. Exit"); @@ -179,17 +179,17 @@ int main(int argc, char *argv[]) { switch (ev) { // Client is connected to the server - case NBN_CONNECTED: + case NBN_CLIENT_CONNECTED: OnConnected(); break; // Client has disconnected from the server - case NBN_DISCONNECTED: + case NBN_CLIENT_DISCONNECTED: OnDisconnected(); break; // A message has been received from the server - case NBN_MESSAGE_RECEIVED: + case NBN_CLIENT_MESSAGE_RECEIVED: OnMessageReceived(); break; } diff --git a/examples/echo/server.c b/examples/echo/server.c index fae09aa..a61dc1d 100644 --- a/examples/echo/server.c +++ b/examples/echo/server.c @@ -27,7 +27,7 @@ #include "shared.h" -static NBN_Connection *connection = NULL; +static NBN_ConnectionHandle *connection = NULL; static NBN_Connection_ID conn_id; // Echo the received message @@ -39,7 +39,7 @@ static int EchoReceivedMessage(void) { assert(msg_info.type == ECHO_MESSAGE_TYPE); // read message data - NBN_Reader *reader = NBN_GameServer_GetMessageReader(); + NBN_Reader *reader = NBN_GameServer_ReadMessage(); unsigned int length; int res; @@ -131,7 +131,7 @@ int main(int argc, const char **argv) { NBN_DisconnectionInfo disconnect_info; // Poll for server events - while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) { + while ((ev = NBN_GameServer_Poll()) != NBN_SERVER_NO_EVENT) { if (ev < 0) { Log(LOG_ERROR, "Something went wrong"); @@ -142,7 +142,7 @@ int main(int argc, const char **argv) { switch (ev) { // New connection request... - case NBN_NEW_CONNECTION: + case NBN_SERVER_NEW_CONNECTION: // Echo server work with one single client at a time if (connection) { NBN_GameServer_RejectIncomingConnectionWithCode(ECHO_SERVER_BUSY_CODE); @@ -155,7 +155,7 @@ int main(int argc, const char **argv) { break; // The client has disconnected - case NBN_CLIENT_DISCONNECTED: + case NBN_SERVER_DISCONNECTION: disconnect_info = NBN_GameServer_GetDisconnectionInfo(); assert(disconnect_info.conn_id == conn_id); @@ -163,7 +163,7 @@ int main(int argc, const char **argv) { break; // A message has been received from the client - case NBN_CLIENT_MESSAGE_RECEIVED: + case NBN_SERVER_MESSAGE_RECEIVED: if (EchoReceivedMessage() < 0) { Log(LOG_ERROR, "Failed to echo received message"); diff --git a/nbnet.c b/nbnet.c index 828b6e6..031f178 100644 --- a/nbnet.c +++ b/nbnet.c @@ -22,14 +22,66 @@ */ +// TODO: make functions that are not part of the public API static + #include +#include "examples/echo/shared.h" +#include "nbnet.h" + #define STB_DS_IMPLEMENTATION #include "stb_ds.h" -#include "nbnet.h" +#include +#include +#include +#include +#include +#include -static NBN_GameServer nbn_game_server; -static NBN_GameClient nbn_game_client; +#if defined(_WIN32) || defined(_WIN64) + +#define NBN_PLATFORM_WINDOWS +#define WIN32_LEAN_AND_MEAN +// prevent inclusion of winnt.h in windows.h +#define _WINNT_ + +#include +#include + +#elif (defined(__APPLE__) && defined(__MACH__)) + +#define NBN_PLATFORM_MAC + +#else + +#define NBN_PLATFORM_UNIX + +#endif + +#ifdef __EMSCRIPTEN__ + +#include + +#endif + +#ifndef NBN_PLATFORM_WINDOWS + +#include +#include +#include + +#ifndef CLOCK_MONOTONIC_RAW +#define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC +#endif + +#endif + +typedef struct NBN_Connection NBN_Connection; +typedef struct NBN_Endpoint NBN_Endpoint; +typedef struct NBN_Channel NBN_Channel; +typedef struct NBN_Driver NBN_Driver; + +typedef enum NBN_Driver_ID NBN_Driver_ID; static NBN_LogFunc nbn_log_func = NULL; @@ -66,6 +118,333 @@ void NBN_SetLogLevel(NBN_LogLevel level) { nbn_log_level = level; } #define SEQUENCE_NUMBER_LTE(seq1, seq2) \ ((seq1 <= seq2 && (seq2 - seq1) <= 32767) || (seq1 >= seq2 && (seq1 - seq2) >= 32767)) +#define B_MASK(n) (1u << (n)) +#define B_SET(mask, n) (mask |= B_MASK(n)) +#define B_UNSET(mask, n) (mask &= ~B_MASK(n)) +#define B_IS_SET(mask, n) ((B_MASK(n) & mask) == B_MASK(n)) +#define B_IS_UNSET(mask, n) ((B_MASK(n) & mask) == 0) + +#define HANDLE_TO_CONN(h) ((NBN_Connection *)(void *)h) + +#define NBN_EVENT_QUEUE_CAPACITY 1024 + +#define NBN_MAX_MESSAGE_TYPES UINT8_MAX +// IMPORTANT: DO NOT FORGET TO MODIFY 'NBN_RESERVED_MESSAGE_TYPES' if you add or remove library messages +#define NBN_RESERVED_MESSAGE_TYPES 4 /* Number of message types reserved for the library */ + +#define NBN_CLIENT_CLOSED_MESSAGE_TYPE NBN_MAX_MESSAGE_TYPES +#define NBN_CLIENT_ACCEPTED_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 1) +#define NBN_DISCONNECTION_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 2) +#define NBN_CONNECTION_REQUEST_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 3) + +/* + * Maximum allowed packet size (including header) in bytes. + * The 1400 value has been chosen based on this statement: + * + * With the IPv4 header being 20 bytes and the UDP header being 8 bytes, the payload + * of a UDP packet should be no larger than 1500 - 20 - 8 = 1472 bytes to avoid fragmentation. + */ +#define NBN_PACKET_MAX_SIZE 1400 + +#define NBN_MAX_MESSAGES_PER_PACKET UINT8_MAX +#define NBN_MESSAGE_HEADER_SIZE 6 /* See NBN_MessageHeader struct */ +#define NBN_PACKET_HEADER_SIZE 13 + +/* Maximum size of packet's data (NBN_PACKET_MAX_DATA_SIZE + NBN_PACKET_HEADER_SIZE = NBN_PACKET_MAX_SIZE) */ +#define NBN_PACKET_MAX_DATA_SIZE (NBN_PACKET_MAX_SIZE - NBN_PACKET_HEADER_SIZE) + +/* Maximum number of packets that can be sent in a single flush + * + * IMPORTANT: DO NOT INCREASE THIS, it will break packet acks + */ +#define NBN_CONNECTION_MAX_SENT_PACKET_COUNT 16 + +#define NBN_MAX_PACKET_ENTRIES 1024 + +#define NBN_RESERVED_UNRELIABLE_CHANNEL_ID 0 +#define NBN_RESERVED_RELIABLE_CHANNEL_ID 1 + +typedef enum NBN_PacketResult { + NBN_PACKET_WRITE_ERROR = -1, + NBN_PACKET_WRITE_OK, + NBN_PACKET_WRITE_NO_SPACE, +} NBN_PacketResult; + +typedef enum NBN_PacketMode { NBN_PACKET_MODE_WRITE = 1, NBN_PACKET_MODE_READ } NBN_PacketMode; + +// IMPORTANT: don't forget to update NBN_PACKET_HEADER_SIZE after modifying this structure +typedef struct NBN_PacketHeader { + uint32_t protocol_id; + uint32_t ack_bits; + uint16_t seq_number; + uint16_t ack; + uint8_t messages_count; +} NBN_PacketHeader; + +typedef struct NBN_Packet { + NBN_PacketHeader header; + NBN_PacketMode mode; + struct NBN_Connection *sender; /* not serialized, filled by the network driver upon reception */ + uint8_t buffer[NBN_PACKET_MAX_SIZE]; + unsigned int size; /* in bytes */ + bool sealed; +} NBN_Packet; + +typedef struct NBN_MessageEntry { + uint16_t id; + uint8_t channel_id; +} NBN_MessageEntry; + +typedef struct NBN_PacketEntry { + bool acked; + bool flagged_as_lost; + unsigned int messages_count; + double send_time; + NBN_MessageEntry messages[NBN_MAX_MESSAGES_PER_PACKET]; +} NBN_PacketEntry; + +// IMPORTANT: make sure you update NBN_MESSAGE_HEADER_SIZE if you modify NBN_MessageHeader struct +typedef struct NBN_MessageHeader { + uint16_t id; + uint16_t length; + uint8_t type; + uint8_t channel_id; +} NBN_MessageHeader; + +typedef enum { NBN_OUTGOING_MESSAGE, NBN_INCOMING_MESSAGE } NBN_MessageType; + +typedef struct NBN_Message { + NBN_MessageHeader header; + NBN_MessageType type; + NBN_Connection *sender; + uint8_t data[NBN_MESSAGE_MAX_SIZE]; +} NBN_Message; + +typedef struct NBN_OutgoingMessage { + NBN_Message message; + uint16_t id; // TODO: needed? + double last_send_time; + bool free; +} NBN_OutgoingMessage; + +typedef struct NBN_IncomingMessage { + NBN_Message message; + bool free; +} NBN_IncomingMessage; + +struct NBN_Channel { + uint8_t id; + NBN_ChannelMode type; + uint16_t next_outgoing_message_id; + uint16_t next_recv_message_id; + uint16_t oldest_unacked_message_id; + uint16_t most_recent_message_id; + uint16_t last_received_message_id; + unsigned int next_outgoing_message_slot; + unsigned int outgoing_message_count; + NBN_OutgoingMessage outgoing_messages_buffer[NBN_CHANNEL_BUFFER_SIZE]; + NBN_IncomingMessage incoming_messages_buffer[NBN_CHANNEL_BUFFER_SIZE]; + bool ack_buffer[NBN_CHANNEL_BUFFER_SIZE]; +}; + +void NBN_Channel_Destroy(NBN_Endpoint *, NBN_Channel *); + +typedef struct NBN_UnreliableOrderedChannel { + NBN_Channel base; +} NBN_UnreliableOrderedChannel; + +typedef struct NBN_IPAddress { + uint32_t host; + uint16_t port; +} NBN_IPAddress; + +struct NBN_Connection { + NBN_ConnectionHandle handle; + double last_recv_packet_time; /* Used to detect stale connections */ + double last_flush_time; /* Last time the send queue was flushed */ + double last_read_packets_time; /* Last time packets were read from the network driver */ + /* Keep track of bytes read from the socket (used for download bandwith calculation) */ + unsigned int downloaded_bytes; + uint8_t is_accepted : 1; + uint8_t is_stale : 1; + uint8_t is_closed : 1; + struct NBN_Endpoint *endpoint; + NBN_Driver *driver; /* Network driver used for that connection */ + NBN_Channel channels[NBN_CHANNEL_COUNT]; /* Message channels (sending & receiving) */ + NBN_ConnectionStats stats; + void *user_data; /* Pointer to user-defined data */ + + /* Driver-related data attached to the connection */ + union { + struct { + NBN_IPAddress ip_address; + } udp; + } driver_data; + + /* + * Packet sequencing & acking + */ + uint16_t next_packet_seq_number; + uint16_t last_received_packet_seq_number; + uint32_t packet_send_seq_buffer[NBN_MAX_PACKET_ENTRIES]; + NBN_PacketEntry packet_send_buffer[NBN_MAX_PACKET_ENTRIES]; + uint32_t packet_recv_seq_buffer[NBN_MAX_PACKET_ENTRIES]; +}; + +typedef struct NBN_ConnectionListNode NBN_ConnectionListNode; + +/* Linked list of connections */ +struct NBN_ConnectionListNode { + NBN_Connection *conn; + NBN_ConnectionListNode *next; + NBN_ConnectionListNode *prev; +}; + +typedef union NBN_EventData { + NBN_MessageInfo message_info; + NBN_DisconnectionInfo disconnection; + NBN_Connection *connection; +} NBN_EventData; + +typedef struct NBN_Event { + int type; + NBN_EventData data; +} NBN_Event; + +typedef struct NBN_EventQueue { + NBN_Event events[NBN_EVENT_QUEUE_CAPACITY]; + unsigned int head; + unsigned int tail; + unsigned int count; +} NBN_EventQueue; + +NBN_EventQueue *NBN_EventQueue_Create(void); +void NBN_EventQueue_Destroy(NBN_EventQueue *); +bool NBN_EventQueue_Enqueue(NBN_EventQueue *, NBN_Event); +bool NBN_EventQueue_Dequeue(NBN_EventQueue *, NBN_Event *); +bool NBN_EventQueue_IsEmpty(NBN_EventQueue *); + +struct NBN_Endpoint { + NBN_EventQueue event_queue; + uint32_t protocol_id; + bool is_server; + double time; + NBN_Message write_message; + NBN_Writer message_writer; + NBN_Reader message_reader; + uint8_t server_initial_data_buffer[NBN_SERVER_INITIAL_DATA_MAX_SIZE]; + uint8_t connection_request_data_buffer[NBN_CONNECTION_REQUEST_DATA_MAX_SIZE]; + unsigned int client_connection_request_data_len; + unsigned int server_initial_data_len; + NBN_ChannelMode channel_modes[NBN_CHANNEL_COUNT]; +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + NBN_PacketSimulator packet_simulator; +#endif +}; + +typedef struct NBN_GameServer_Config { + const char *protocol_name; + uint16_t port; + NBN_ChannelMode channel_modes[NBN_CHANNEL_COUNT]; +} NBN_GameServer_Config; + +typedef struct NBN_GameServer { + NBN_Endpoint endpoint; + NBN_GameServer_Config config; + struct { + NBN_Connection_ID key; + NBN_Connection *value; + } *clients; + NBN_ConnectionListNode *closed_clients_head; + NBN_GameServerStats stats; + NBN_Event last_event; + NBN_Writer server_data_writer; + NBN_Reader client_data_reader; +} NBN_GameServer; + +typedef struct NBN_GameClient_Config { + const char *protocol_name; + const char *host; + uint16_t port; + NBN_ChannelMode channel_modes[NBN_CHANNEL_COUNT]; +} NBN_GameClient_Config; + +typedef struct NBN_GameClient { + NBN_Endpoint endpoint; + NBN_GameClient_Config config; + NBN_Connection *server_connection; + bool is_connected; + NBN_Event last_event; + int closed_code; + NBN_Writer client_data_writer; + NBN_Reader server_data_reader; +} NBN_GameClient; + +static NBN_GameServer nbn_game_server; +static NBN_GameClient nbn_game_client; + +typedef enum NBN_DriverEvent { + // Client events + NBN_DRIVER_CLI_PACKET_RECEIVED, + + // Server events + NBN_DRIVER_SERV_CLIENT_CONNECTED, + NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, +} NBN_DriverEvent; + +typedef int (*NBN_Driver_Func_ClientStart)(NBN_GameClient *, const char *, uint16_t); +typedef void (*NBN_Driver_Func_ClientStop)(NBN_GameClient *); +typedef int (*NBN_Driver_Func_ClientSendPacket)(NBN_GameClient *, NBN_Packet *); +typedef int (*NBN_Driver_Func_ClientRecvPackets)(NBN_GameClient *); + +typedef int (*NBN_Driver_Func_ServerStart)(NBN_GameServer *, uint16_t); +typedef void (*NBN_Driver_Func_ServerStop)(NBN_GameServer *); +typedef int (*NBN_Driver_Func_ServerSendPacketTo)(NBN_GameServer *, NBN_Packet *, NBN_Connection *); +typedef void (*NBN_Driver_Func_ServerCleanupConnection)(NBN_GameServer *, NBN_Connection *); +typedef int (*NBN_Driver_Func_ServerRecvPackets)(NBN_GameServer *); + +typedef struct NBN_Driver_Implementation { + /* Client functions */ + NBN_Driver_Func_ClientStart cli_start; + NBN_Driver_Func_ClientStop cli_stop; + NBN_Driver_Func_ClientRecvPackets cli_recv_packets; + NBN_Driver_Func_ClientSendPacket cli_send_packet; + + /* Server functions */ + NBN_Driver_Func_ServerStart serv_start; + NBN_Driver_Func_ServerStop serv_stop; + NBN_Driver_Func_ServerRecvPackets serv_recv_packets; + NBN_Driver_Func_ServerSendPacketTo serv_send_packet_to; + NBN_Driver_Func_ServerCleanupConnection serv_cleanup_connection; +} NBN_Driver_Implementation; + +enum NBN_Driver_ID { NBN_DRIVER_UDP = 0x01 }; + +struct NBN_Driver { + int id; + const char *name; + NBN_Driver_Implementation impl; +}; + +/** + * Let nbnet know about specific network events happening within a network driver. + * + * @param ev Event type + * @param data Arbitrary data about the event + */ +int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data); + +void NBN_Packet_InitWrite(NBN_Packet *, uint32_t, uint16_t, uint16_t, uint32_t); +NBN_PacketResult NBN_Packet_WriteMessage(NBN_Packet *, NBN_OutgoingMessage *); +int NBN_Packet_Seal(NBN_Packet *); +int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); + +int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); +int NBN_Connection_FlushChannels(NBN_Endpoint *, NBN_Connection *, uint32_t, double); +bool NBN_Connection_CheckIfStale(NBN_Connection *, double); + +#pragma endregion /* NBN_Packet */ + #ifdef NBN_UDP #if defined(NBN_PLATFORM_WINDOWS) @@ -275,7 +654,7 @@ void NBN_Packet_InitWrite(NBN_Packet *packet, uint32_t protocol_id, uint16_t seq memset(packet->buffer, 0, sizeof(packet->buffer)); } -int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_OutgoingMessage *out_msg) { +NBN_PacketResult NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_OutgoingMessage *out_msg) { NBN_Message *message = &out_msg->message; LogDebug("Write message %d (type: %d, length: %d) to packet %d", out_msg->id, message->header.type, @@ -389,7 +768,7 @@ int NBN_Packet_InitRead(NBN_Packet *packet, uint32_t protocol_id, unsigned int s static unsigned int Channel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2); -static void Channel_Init(NBN_Channel *channel, uint8_t id, NBN_ChannelType type) { +static void Channel_Init(NBN_Channel *channel, uint8_t id, NBN_ChannelMode type) { channel->id = id; channel->type = type; channel->next_outgoing_message_id = 0; @@ -694,11 +1073,6 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection if (Channel_AddReceivedMessage(endpoint, channel, &message)) { LogDebug("Received message %d (type: %d) on channel %d", message.header.id, message.header.type, channel->id); - -#ifdef NBN_DEBUG - if (connection->OnMessageAddedToRecvQueue) - connection->OnMessageAddedToRecvQueue(connection, &message); -#endif } else { LogDebug("Message %d was discarded by channel %d", message.header.id, channel->id); } @@ -732,7 +1106,7 @@ int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connect NBN_Message *message = &out_msg.message; uint16_t msg_id = out_msg.id; bool message_sent = false; - int ret = NBN_Packet_WriteMessage(&packet, &out_msg); + NBN_PacketResult ret = NBN_Packet_WriteMessage(&packet, &out_msg); if (ret == NBN_PACKET_WRITE_OK) { message_sent = true; @@ -748,7 +1122,7 @@ int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connect Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); - int ret = NBN_Packet_WriteMessage(&packet, &out_msg); + NBN_PacketResult ret = NBN_Packet_WriteMessage(&packet, &out_msg); if (ret != NBN_PACKET_WRITE_OK) { LogError("Failed to send packet %d", packet.header.seq_number); @@ -780,7 +1154,7 @@ int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connect } if (Connection_SendPacket(connection, &packet, packet_entry, time) < 0) { - LogError("Failed to send packet %d to connection %d", packet.header.seq_number, connection->id); + LogError("Failed to send packet %d to connection %d", packet.header.seq_number, connection->handle.id); return NBN_ERROR; } @@ -853,7 +1227,7 @@ static int Connection_AckPacket(NBN_Endpoint *endpoint, NBN_Connection *connecti NBN_PacketEntry *packet_entry = Connection_FindSendPacketEntry(connection, ack_packet_seq_number); if (packet_entry && !packet_entry->acked) { - LogDebug("Packet %d acked (connection: %d)", ack_packet_seq_number, connection->id); + LogDebug("Packet %d acked (connection: %d)", ack_packet_seq_number, connection->handle.id); packet_entry->acked = true; @@ -933,7 +1307,7 @@ static bool Connection_IsPacketReceived(NBN_Connection *connection, uint16_t pac static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, NBN_PacketEntry *packet_entry, double time) { - LogDebug("Send packet %d to connection %d (messages count: %d)", packet->header.seq_number, connection->id, + LogDebug("Send packet %d to connection %d (messages count: %d)", packet->header.seq_number, connection->handle.id, packet->header.messages_count); NBN_Assert(packet_entry->messages_count == packet->header.messages_count); @@ -1106,7 +1480,7 @@ static void PacketSimulator_Stop(NBN_PacketSimulator *); #endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ -static void Endpoint_Init(NBN_Endpoint *, uint32_t, bool); +static void Endpoint_Init(NBN_Endpoint *, uint32_t, bool, NBN_ChannelMode[NBN_CHANNEL_COUNT]); static void Endpoint_Deinit(NBN_Endpoint *); static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, NBN_Connection_ID, NBN_Driver_ID); static uint32_t Endpoint_BuildProtocolId(const char *); @@ -1114,15 +1488,19 @@ static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *, NBN_Packet *, NBN_Conn static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN_Message *); static void Endpoint_UpdateTime(NBN_Endpoint *); -static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_server) { +static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_server, + NBN_ChannelMode channel_modes[NBN_CHANNEL_COUNT]) { + if (NBN_CHANNEL_COUNT < 2) { + LogError("At least 2 channels are necessary, check the NBN_CHANNEL_COUNT macro"); + NBN_Abort(); + } + endpoint->is_server = is_server; endpoint->protocol_id = protocol_id; - NBN_EventQueue_Init(&endpoint->event_queue); + memcpy(&endpoint->channel_modes, channel_modes, sizeof(NBN_ChannelMode) * NBN_CHANNEL_COUNT); -#ifdef NBN_DEBUG - endpoint->OnMessageAddedToRecvQueue = NULL; -#endif + NBN_EventQueue_Init(&endpoint->event_queue); #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) PacketSimulator_Init(&endpoint->packet_simulator, endpoint); @@ -1144,7 +1522,8 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, NBN_Con NBN_Driver_ID driver_id) { NBN_Connection *connection = (NBN_Connection *)malloc(sizeof(NBN_Connection)); - connection->id = id; + connection->handle.id = id; + connection->handle.user_data = NULL; connection->endpoint = endpoint; connection->last_recv_packet_time = endpoint->time; connection->next_packet_seq_number = 1; @@ -1155,7 +1534,6 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, NBN_Con connection->is_accepted = false; connection->is_stale = false; connection->is_closed = false; - connection->user_data = NULL; for (int i = 0; i < NBN_MAX_PACKET_ENTRIES; i++) { connection->packet_send_seq_buffer[i] = 0xFFFFFFFF; @@ -1167,10 +1545,7 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, NBN_Con connection->stats = stats; for (int i = 0; i < NBN_CHANNEL_COUNT; i++) { - // channels are created in unreliable mode by default - // TODO: expose a method to change the reliability mode of a channel - // make sure we have at least one reliable channel for library messages - Channel_Init(&connection->channels[i], i, NBN_CHANNEL_UNRELIABLE); + Channel_Init(&connection->channels[i], i, endpoint->channel_modes[i]); } switch (driver_id) { @@ -1201,8 +1576,8 @@ static uint32_t Endpoint_BuildProtocolId(const char *protocol_name) { static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *packet, NBN_Connection *connection) { (void)endpoint; - LogDebug("Received packet %d (conn id: %d, ack: %d, messages count: %d)", packet->header.seq_number, connection->id, - packet->header.ack, packet->header.messages_count); + LogDebug("Received packet %d (conn id: %d, ack: %d, messages count: %d)", packet->header.seq_number, + connection->handle.id, packet->header.ack, packet->header.messages_count); if (NBN_Connection_ProcessReceivedPacket(endpoint, connection, packet, endpoint->time) < 0) return NBN_ERROR; @@ -1227,9 +1602,11 @@ static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connectio NBN_Assert(!connection->is_closed || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); NBN_Assert(!connection->is_stale); - NBN_Channel *channel = &connection->channels[message->header.channel_id]; + uint8_t channel_id = message->header.channel_id; + NBN_Channel *channel = &connection->channels[channel_id]; NBN_Assert(channel); + NBN_Assert(channel_id != NBN_RESERVED_RELIABLE_CHANNEL_ID || channel->type == NBN_CHANNEL_RELIABLE); LogDebug("Enqueue message of type %d on channel %d", message->header.type, channel->id); @@ -1291,15 +1668,39 @@ int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data) { #pragma region NBN_GameClient static int GameClient_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); -static int GameClient_HandleEvent(void); +static NBN_Client_Event GameClient_HandleEvent(void); static int GameClient_HandleMessageReceivedEvent(void); +static NBN_Connection *CreateServerConnection(NBN_Driver_ID driver_id); + +static void InitChannelModes(NBN_ChannelMode channel_modes[NBN_CHANNEL_COUNT]) { + // channel 0 is always unreliable, channel 1 is always reliable + // additional channels are reliable by default + for (int i = 0; i < NBN_CHANNEL_COUNT; i++) { + if (i == NBN_RESERVED_UNRELIABLE_CHANNEL_ID) { + channel_modes[i] = NBN_CHANNEL_UNRELIABLE; + } else { + channel_modes[i] = NBN_CHANNEL_RELIABLE; + } + } +} void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port) { nbn_game_client.config = (NBN_GameClient_Config){.protocol_name = protocol_name, .host = host, .port = port}; + InitChannelModes(nbn_game_client.config.channel_modes); nbn_game_client.client_data_writer.position = 0; } +void NBN_GameClient_SetChannelMode(uint8_t channel_id, NBN_ChannelMode mode) { + if (channel_id == NBN_RESERVED_RELIABLE_CHANNEL_ID && mode != NBN_CHANNEL_RELIABLE) { + LogError("Cannot change the mode of channel %d as it's used internally by the library", + NBN_RESERVED_RELIABLE_CHANNEL_ID); + NBN_Abort(); + } + + nbn_game_client.endpoint.channel_modes[channel_id] = mode; +} + NBN_Writer *NBN_GameClient_GetConnectionRequestDataWriter(void) { NBN_Writer_Init(&nbn_game_client.client_data_writer, nbn_game_client.endpoint.connection_request_data_buffer, sizeof(nbn_game_client.endpoint.connection_request_data_buffer)); @@ -1314,12 +1715,12 @@ int NBN_GameClient_Start(void) { uint16_t port = config.port; uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - Endpoint_Init(&nbn_game_client.endpoint, protocol_id, false); + Endpoint_Init(&nbn_game_client.endpoint, protocol_id, false, config.channel_modes); int driver_count = 0; #ifdef NBN_UDP - nbn_game_client.server_connection = NBN_GameClient_CreateServerConnection(NBN_DRIVER_UDP); + nbn_game_client.server_connection = CreateServerConnection(NBN_DRIVER_UDP); if (nbn_udp_driver.impl.cli_start(&nbn_game_client, host, port) < 0) { LogError("Failed to start driver %s", nbn_udp_driver.name); @@ -1339,9 +1740,8 @@ int NBN_GameClient_Start(void) { unsigned int connection_data_len = nbn_game_client.client_data_writer.position; - // TODO: make sure it's a reliable channel - NBN_GameClient_CreateMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE, 0); - NBN_Writer *writer = NBN_GameClient_GetMessageWriter(); + NBN_Writer *writer = + NBN_GameClient_CreateMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE, NBN_RESERVED_RELIABLE_CHANNEL_ID); if (connection_data_len > 0) { NBN_Assert(connection_data_len <= sizeof(nbn_game_client.endpoint.connection_request_data_buffer)); @@ -1362,15 +1762,14 @@ int NBN_GameClient_Start(void) { void NBN_GameClient_Stop(void) { // Poll remaining events to clear the event queue - while (NBN_GameClient_Poll() != NBN_NO_EVENT) { + while (NBN_GameClient_Poll() != NBN_CLIENT_NO_EVENT) { } if (nbn_game_client.server_connection) { if (!nbn_game_client.server_connection->is_closed && !nbn_game_client.server_connection->is_stale) { LogInfo("Disconnecting..."); - // TODO: make sure it's a reliable channel - NBN_GameClient_CreateMessage(NBN_DISCONNECTION_MESSAGE_TYPE, 0); + NBN_GameClient_CreateMessage(NBN_DISCONNECTION_MESSAGE_TYPE, NBN_RESERVED_RELIABLE_CHANNEL_ID); if (NBN_GameClient_EnqueueMessage() < 0) { LogError("Failed to send disconnection message"); @@ -1413,13 +1812,13 @@ NBN_Reader *NBN_GameClient_GetServerDataReader(void) { return &nbn_game_client.server_data_reader; } -int NBN_GameClient_Poll(void) { +NBN_Client_Event NBN_GameClient_Poll(void) { Endpoint_UpdateTime(&nbn_game_client.endpoint); NBN_Endpoint *endpoint = &nbn_game_client.endpoint; if (nbn_game_client.server_connection->is_stale) - return NBN_NO_EVENT; + return NBN_CLIENT_NO_EVENT; if (NBN_EventQueue_IsEmpty(&endpoint->event_queue)) { if (NBN_Connection_CheckIfStale(nbn_game_client.server_connection, nbn_game_client.endpoint.time)) { @@ -1430,7 +1829,7 @@ int NBN_GameClient_Poll(void) { NBN_Event e; - e.type = NBN_DISCONNECTED; + e.type = NBN_CLIENT_DISCONNECTED; e.data.connection = (NBN_Connection *)NULL; if (!NBN_EventQueue_Enqueue(&endpoint->event_queue, e)) @@ -1470,7 +1869,7 @@ int NBN_GameClient_Poll(void) { bool ret = NBN_EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_client.last_event); - return ret ? GameClient_HandleEvent() : NBN_NO_EVENT; + return ret ? GameClient_HandleEvent() : NBN_CLIENT_NO_EVENT; } int NBN_GameClient_Flush(void) { @@ -1505,17 +1904,8 @@ int NBN_GameClient_EnqueueMessage(void) { return 0; } -NBN_Writer *NBN_GameClient_GetMessageWriter(void) { - NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - NBN_Writer *writer = &endpoint->message_writer; - - NBN_Writer_Init(writer, endpoint->write_message.data, sizeof(endpoint->write_message.data)); - - return writer; -} - -NBN_Reader *NBN_GameClient_GetMessageReader(void) { - NBN_Assert(nbn_game_client.last_event.type == NBN_MESSAGE_RECEIVED); +NBN_Reader *NBN_GameClient_ReadMessage(void) { + NBN_Assert(nbn_game_client.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); NBN_MessageInfo msg_info = nbn_game_client.last_event.data.message_info; NBN_Assert(msg_info.length > 0 && msg_info.data != NULL); @@ -1527,20 +1917,16 @@ NBN_Reader *NBN_GameClient_GetMessageReader(void) { return reader; } -NBN_Connection *NBN_GameClient_CreateServerConnection(NBN_Driver_ID driver_id) { +static NBN_Connection *CreateServerConnection(NBN_Driver_ID driver_id) { NBN_Connection *server_connection = Endpoint_CreateConnection(&nbn_game_client.endpoint, 0, driver_id); -#ifdef NBN_DEBUG - server_connection->OnMessageAddedToRecvQueue = nbn_game_client.endpoint.OnMessageAddedToRecvQueue; -#endif - nbn_game_client.server_connection = server_connection; return server_connection; } NBN_MessageInfo NBN_GameClient_GetMessageInfo(void) { - NBN_Assert(nbn_game_client.last_event.type == NBN_MESSAGE_RECEIVED); + NBN_Assert(nbn_game_client.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); return nbn_game_client.last_event.data.message_info; } @@ -1551,31 +1937,19 @@ int NBN_GameClient_GetServerCloseCode(void) { return nbn_game_client.closed_code bool NBN_GameClient_IsConnected(void) { return nbn_game_client.is_connected; } -#ifdef NBN_DEBUG - -void NBN_GameClient_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, void *cb) { - switch (cb_type) { - case NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE: - nbn_game_client.endpoint.OnMessageAddedToRecvQueue = (void (*)(NBN_Connection *, NBN_Message *))cb; - break; - } -} - -#endif /* NBN_DEBUG */ - static int GameClient_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *server_connection) { NBN_Assert(nbn_game_client.server_connection == server_connection); NBN_Event ev; - ev.type = NBN_MESSAGE_RECEIVED; + ev.type = NBN_CLIENT_MESSAGE_RECEIVED; NBN_MessageInfo msg_info; msg_info.type = message->header.type; msg_info.channel_id = message->header.channel_id; msg_info.length = message->header.length; - msg_info.sender = server_connection; + msg_info.sender = (NBN_ConnectionHandle *)server_connection; msg_info.data = message->data; ev.data.message_info = msg_info; @@ -1586,9 +1960,9 @@ static int GameClient_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio return 0; } -static int GameClient_HandleEvent(void) { +static NBN_Client_Event GameClient_HandleEvent(void) { switch (nbn_game_client.last_event.type) { - case NBN_MESSAGE_RECEIVED: + case NBN_CLIENT_MESSAGE_RECEIVED: return GameClient_HandleMessageReceivedEvent(); default: @@ -1596,15 +1970,15 @@ static int GameClient_HandleEvent(void) { } } -static int GameClient_HandleMessageReceivedEvent(void) { +static NBN_Client_Event GameClient_HandleMessageReceivedEvent(void) { NBN_MessageInfo message_info = nbn_game_client.last_event.data.message_info; NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - int ret = NBN_NO_EVENT; + int ret = NBN_CLIENT_NO_EVENT; if (message_info.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE) { nbn_game_client.is_connected = false; - NBN_Reader *reader = NBN_GameClient_GetMessageReader(); + NBN_Reader *reader = NBN_GameClient_ReadMessage(); if (NBN_Reader_ReadInt32(reader, &nbn_game_client.closed_code) < 0) { LogError("Failed to read code from client closed message"); @@ -1612,7 +1986,7 @@ static int GameClient_HandleMessageReceivedEvent(void) { return NBN_ERROR; } - ret = NBN_DISCONNECTED; + ret = NBN_CLIENT_DISCONNECTED; } else if (message_info.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE) { if (message_info.length < 4) { LogError("Accept message invalid length"); @@ -1620,7 +1994,7 @@ static int GameClient_HandleMessageReceivedEvent(void) { return NBN_ERROR; } - NBN_Reader *reader = NBN_GameClient_GetMessageReader(); + NBN_Reader *reader = NBN_GameClient_ReadMessage(); unsigned int data_length; if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) { @@ -1645,9 +2019,9 @@ static int GameClient_HandleMessageReceivedEvent(void) { endpoint->server_initial_data_len = data_length; nbn_game_client.is_connected = true; - ret = NBN_CONNECTED; + ret = NBN_CLIENT_CONNECTED; } else { - ret = NBN_MESSAGE_RECEIVED; + ret = NBN_CLIENT_MESSAGE_RECEIVED; } return ret; @@ -1676,23 +2050,34 @@ static void GameServer_AddClientToClosedList(NBN_Connection *client); static int GameServer_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); static int GameServer_CloseStaleClientConnections(void); static void GameServer_RemoveClosedClientConnections(void); -static int GameServer_HandleEvent(void); -static int GameServer_HandleMessageReceivedEvent(void); +static bool GameServer_HandleEvent(NBN_Server_Event *ev); +static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev); void NBN_GameServer_Init(const char *protocol_name, uint16_t port) { nbn_game_server.config = (NBN_GameServer_Config){.protocol_name = protocol_name, .port = port}; - nbn_game_server.server_data_writer.position = 0; + InitChannelModes(nbn_game_server.config.channel_modes); + nbn_game_server.server_data_writer.position = 0; hmdefault(nbn_game_server.clients, NULL); } +void NBN_GameServer_SetChannelMode(uint8_t channel_id, NBN_ChannelMode mode) { + if (channel_id == NBN_RESERVED_RELIABLE_CHANNEL_ID && mode != NBN_CHANNEL_RELIABLE) { + LogError("Cannot change the mode of channel %d as it's used internally by the library", + NBN_RESERVED_RELIABLE_CHANNEL_ID); + NBN_Abort(); + } + + nbn_game_server.endpoint.channel_modes[channel_id] = mode; +} + int NBN_GameServer_Start(void) { NBN_GameServer_Config config = nbn_game_server.config; const char *protocol_name = config.protocol_name; uint16_t port = config.port; uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - Endpoint_Init(&nbn_game_server.endpoint, protocol_id, true); + Endpoint_Init(&nbn_game_server.endpoint, protocol_id, true, config.channel_modes); nbn_game_server.closed_clients_head = NULL; @@ -1719,7 +2104,7 @@ int NBN_GameServer_Start(void) { void NBN_GameServer_Stop(void) { // Poll remaning events to clear the event queue - while (NBN_GameServer_Poll() != NBN_NO_EVENT) { + while (NBN_GameServer_Poll() != NBN_SERVER_NO_EVENT) { } for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { @@ -1759,13 +2144,17 @@ static NBN_Connection_ID NBN_BuildConnectionHash(NBN_Connection_ID id, NBN_Drive return ((NBN_Connection_ID)driver_byte << 56) | id; } -NBN_Connection *NBN_GameServer_FindConnection(NBN_Connection_ID id) { return hmget(nbn_game_server.clients, id); } +NBN_ConnectionHandle *NBN_GameServer_FindConnection(NBN_Connection_ID id) { + return (NBN_ConnectionHandle *)hmget(nbn_game_server.clients, id); +} unsigned int NBN_GameServer_GetClientCount(void) { return hmlen(nbn_game_server.clients); } -NBN_Connection *NBN_GameServer_GetClientByIndex(unsigned int index) { return nbn_game_server.clients[index].value; } +NBN_ConnectionHandle *NBN_GameServer_GetClientByIndex(unsigned int index) { + return (NBN_ConnectionHandle *)nbn_game_server.clients[index].value; +} -int NBN_GameServer_Poll(void) { +NBN_Server_Event NBN_GameServer_Poll(void) { Endpoint_UpdateTime(&nbn_game_server.endpoint); NBN_Endpoint *endpoint = &nbn_game_server.endpoint; @@ -1812,14 +2201,15 @@ int NBN_GameServer_Poll(void) { GameServer_RemoveClosedClientConnections(); } - while (NBN_EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_server.last_event)) { - int ev = GameServer_HandleEvent(); + NBN_Server_Event ev; - if (ev != NBN_SKIP_EVENT) + while (NBN_EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_server.last_event)) { + if (GameServer_HandleEvent(&ev)) { return ev; + } } - return NBN_NO_EVENT; + return NBN_SERVER_NO_EVENT; } int NBN_GameServer_Flush(void) { @@ -1844,23 +2234,21 @@ int NBN_GameServer_Flush(void) { return 0; } -NBN_Connection *NBN_GameServer_CreateClientConnection(NBN_Driver_ID driver_id, NBN_Connection_ID conn_id) { +static NBN_Connection *CreateClientConnection(NBN_Driver_ID driver_id, NBN_Connection_ID conn_id) { // write the driver ID to the first byte of the connection ID to avoid collisions between drivers conn_id = NBN_BuildConnectionHash(conn_id, driver_id); NBN_Connection *client = Endpoint_CreateConnection(&nbn_game_server.endpoint, conn_id, driver_id); -#ifdef NBN_DEBUG - client->OnMessageAddedToRecvQueue = nbn_game_server.endpoint.OnMessageAddedToRecvQueue; -#endif - return client; } -int NBN_GameServer_CloseClientWithCode(NBN_Connection *conn, int code) { - return GameServer_CloseClientWithCode(conn, code, false); +int NBN_GameServer_CloseClientWithCode(NBN_ConnectionHandle *conn, int code) { + return GameServer_CloseClientWithCode(HANDLE_TO_CONN(conn), code, false); } -int NBN_GameServer_CloseClient(NBN_Connection *conn) { return GameServer_CloseClientWithCode(conn, -1, false); } +int NBN_GameServer_CloseClient(NBN_ConnectionHandle *conn) { + return GameServer_CloseClientWithCode(HANDLE_TO_CONN(conn), -1, false); +} NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id) { NBN_Assert(channel_id < NBN_CHANNEL_COUNT); @@ -1873,12 +2261,12 @@ NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id) { return writer; } -int NBN_GameServer_EnqueueMessageFor(NBN_Connection *conn) { +int NBN_GameServer_EnqueueMessageFor(NBN_ConnectionHandle *conn) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; NBN_Message *message = &endpoint->write_message; message->header.length = endpoint->message_writer.position; - int ret = GameServer_EnqueueMessageFor(conn, message); + int ret = GameServer_EnqueueMessageFor(HANDLE_TO_CONN(conn), message); return ret; } @@ -1897,7 +2285,7 @@ int NBN_GameServer_EnqueueBroadcastMessage(void) { continue; if (GameServer_EnqueueMessageFor(conn, &endpoint->write_message) < 0) { - LogError("Failed to send message to client %d when broadcasting", conn->id); + LogError("Failed to send message to client %d when broadcasting", conn->handle.id); ret = NBN_ERROR; break; } @@ -1906,7 +2294,7 @@ int NBN_GameServer_EnqueueBroadcastMessage(void) { return ret; } -NBN_Reader *NBN_GameServer_GetMessageReader(void) { +NBN_Reader *NBN_GameServer_ReadMessage(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); NBN_MessageInfo msg_info = nbn_game_server.last_event.data.message_info; @@ -1920,7 +2308,7 @@ NBN_Reader *NBN_GameServer_GetMessageReader(void) { } NBN_Writer *NBN_GameServer_GetConnectionDataWriter(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); + NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); NBN_Endpoint *endpoint = &nbn_game_server.endpoint; @@ -1932,13 +2320,13 @@ NBN_Writer *NBN_GameServer_GetConnectionDataWriter(void) { } int NBN_GameServer_AcceptIncomingConnection(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); + NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); unsigned data_length = nbn_game_server.server_data_writer.position; NBN_Connection *client = nbn_game_server.last_event.data.connection; - // TODO: make sure it's a reliable channel - NBN_Writer *writer = NBN_GameServer_CreateMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE, 0); + NBN_Writer *writer = + NBN_GameServer_CreateMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE, NBN_RESERVED_RELIABLE_CHANNEL_ID); if (data_length > 0) { NBN_Assert(data_length <= sizeof(nbn_game_server.endpoint.server_initial_data_buffer)); @@ -1949,37 +2337,37 @@ int NBN_GameServer_AcceptIncomingConnection(void) { NBN_Writer_WriteUInt32(writer, 0); } - if (NBN_GameServer_EnqueueMessageFor(client) < 0) + if (NBN_GameServer_EnqueueMessageFor((NBN_ConnectionHandle *)client) < 0) return NBN_ERROR; client->is_accepted = true; - LogInfo("Client %d has been accepted into the server", client->id); + LogInfo("Client %d has been accepted into the server", client->handle.id); return 0; } int NBN_GameServer_RejectIncomingConnectionWithCode(int code) { - NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); + NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); NBN_Connection *conn = nbn_game_server.last_event.data.connection; - LogDebug("Rejecting incoming connection %d (code: %d)", conn->id, code); + LogDebug("Rejecting incoming connection %d (code: %d)", conn->handle.id, code); return GameServer_CloseClientWithCode(conn, code, false); } int NBN_GameServer_RejectIncomingConnection(void) { return NBN_GameServer_RejectIncomingConnectionWithCode(-1); } -NBN_Connection *NBN_GameServer_GetIncomingConnection(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); +NBN_ConnectionHandle *NBN_GameServer_GetIncomingConnection(void) { + NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); - return nbn_game_server.last_event.data.connection; + return (NBN_ConnectionHandle *)nbn_game_server.last_event.data.connection; } NBN_Reader *NBN_GameServer_GetConnectionRequestDataReader(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); + NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); NBN_Endpoint *endpoint = &nbn_game_server.endpoint; @@ -2003,18 +2391,6 @@ NBN_MessageInfo NBN_GameServer_GetMessageInfo(void) { NBN_GameServerStats NBN_GameServer_GetStats(void) { return nbn_game_server.stats; } -#ifdef NBN_DEBUG - -void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback cb_type, void *cb) { - switch (cb_type) { - case NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE: - nbn_game_server.endpoint.OnMessageAddedToRecvQueue = (void (*)(NBN_Connection *, NBN_Message *))cb; - break; - } -} - -#endif /* NBN_DEBUG */ - static int GameServer_EnqueueMessageFor(NBN_Connection *client, NBN_Message *message) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; @@ -2024,7 +2400,7 @@ static int GameServer_EnqueueMessageFor(NBN_Connection *client, NBN_Message *mes message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); if (Endpoint_EnqueueOutgoingMessage(endpoint, client, message) < 0) { - LogError("Failed to create outgoing message for client %d", client->id); + LogError("Failed to create outgoing message for client %d", client->handle.id); /* Do not close the client if we failed to send the close client message to avoid infinite loops */ if (message->header.type != NBN_CLIENT_CLOSED_MESSAGE_TYPE) { @@ -2038,10 +2414,10 @@ static int GameServer_EnqueueMessageFor(NBN_Connection *client, NBN_Message *mes } static void GameServer_AddClient(NBN_Connection *client) { - NBN_Assert(hmgeti(nbn_game_server.clients, client->id) == -1); + NBN_Assert(hmgeti(nbn_game_server.clients, client->handle.id) == -1); - hmput(nbn_game_server.clients, client->id, client); - LogDebug("New client %llu", client->id); + hmput(nbn_game_server.clients, client->handle.id, client); + LogDebug("New client %llu", client->handle.id); } static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection) { @@ -2050,7 +2426,7 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool NBN_Event e; e.type = NBN_CLIENT_DISCONNECTED; - e.data.disconnection = (NBN_DisconnectionInfo){client->id, client->user_data}; + e.data.disconnection = (NBN_DisconnectionInfo){client->handle.id, client->user_data}; if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, e)) return NBN_ERROR; @@ -2058,7 +2434,7 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool } if (client->is_stale) { - LogDebug("Closing stale connection %d", client->id); + LogDebug("Closing stale connection %d", client->handle.id); GameServer_AddClientToClosedList(client); client->is_closed = true; @@ -2066,18 +2442,18 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool return 0; } - LogDebug("Closing active connection %d (will send a disconnection message)", client->id); + LogDebug("Closing active connection %d (will send a disconnection message)", client->handle.id); GameServer_AddClientToClosedList(client); client->is_closed = true; if (!disconnection) { - LogDebug("Send close message for client %d (code: %d)", client->id, code); + LogDebug("Send close message for client %d (code: %d)", client->handle.id, code); - // TODO: make sure it's a reliable channel - NBN_Writer *writer = NBN_GameServer_CreateMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE, 0); + NBN_Writer *writer = + NBN_GameServer_CreateMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE, NBN_RESERVED_RELIABLE_CHANNEL_ID); NBN_Writer_WriteInt32(writer, code); - NBN_GameServer_EnqueueMessageFor(client); + NBN_GameServer_EnqueueMessageFor((NBN_ConnectionHandle *)client); } return 0; @@ -2119,11 +2495,11 @@ static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio msg_info.type = message->header.type; msg_info.channel_id = message->header.channel_id; msg_info.length = message->header.length; - msg_info.sender = client; + msg_info.sender = (NBN_ConnectionHandle *)client; msg_info.data = message->data; LogDebug("Received message (type: %d, id: %d) from client %lld", message->header.type, message->header.id, - client->id); + client->handle.id); ev.data.message_info = msg_info; if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, ev)) @@ -2137,7 +2513,7 @@ static int GameServer_CloseStaleClientConnections(void) { NBN_Connection *client = nbn_game_server.clients[i].value; if (!client->is_stale && NBN_Connection_CheckIfStale(client, nbn_game_server.endpoint.time)) { - LogInfo("Client %lld connection is stale, closing it.", client->id); + LogInfo("Client %lld connection is stale, closing it.", client->handle.id); client->is_stale = true; @@ -2157,15 +2533,15 @@ static void GameServer_RemoveClosedClientConnections(void) { NBN_ConnectionListNode *next = current->next; NBN_Connection *client = current->conn; - NBN_Assert(client->id > 0); + NBN_Assert(client->handle.id > 0); if (client->is_stale) { - LogDebug("Remove closed client connection (ID: %d)", client->id); + LogDebug("Remove closed client connection (ID: %d)", client->handle.id); client->driver->impl.serv_cleanup_connection(&nbn_game_server, client); // Notify the driver to clean up the connection - int ret = hmdel(nbn_game_server.clients, client->id); + int ret = hmdel(nbn_game_server.clients, client->handle.id); NBN_Assert(ret == 1); // Destroy the connection @@ -2198,40 +2574,50 @@ static void GameServer_RemoveClosedClientConnections(void) { } } -static int GameServer_HandleEvent(void) { - return nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED ? GameServer_HandleMessageReceivedEvent() - : nbn_game_server.last_event.type; +static bool GameServer_HandleEvent(NBN_Server_Event *ev) { + if (nbn_game_server.last_event.type == NBN_SERVER_MESSAGE_RECEIVED) { + return GameServer_HandleMessageReceivedEvent(ev); + } + + *ev = nbn_game_server.last_event.type; + return true; } // TODO: big ass function -static int GameServer_HandleMessageReceivedEvent(void) { +static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev) { NBN_Event *last_event = &nbn_game_server.last_event; NBN_MessageInfo message_info = last_event->data.message_info; - NBN_Connection *sender = message_info.sender; + NBN_Connection *sender = HANDLE_TO_CONN(message_info.sender); NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - if (sender->is_closed || sender->is_stale) - return NBN_SKIP_EVENT; + if (sender->is_closed || sender->is_stale) { + return false; + } if (message_info.type == NBN_DISCONNECTION_MESSAGE_TYPE) { - LogInfo("Received a disconnection request from client %d", sender->id); + LogInfo("Received a disconnection request from client %d", sender->handle.id); - if (GameServer_CloseClientWithCode(sender, -1, true) < 0) - return NBN_ERROR; + if (GameServer_CloseClientWithCode(sender, -1, true) < 0) { + *ev = NBN_ERROR; + return true; + } sender->is_stale = true; - last_event->type = NBN_CLIENT_DISCONNECTED; - last_event->data.disconnection = (NBN_DisconnectionInfo){sender->id, sender->user_data}; + last_event->type = NBN_SERVER_DISCONNECTION; + last_event->data.disconnection = (NBN_DisconnectionInfo){sender->handle.id, sender->user_data}; GameServer_RemoveClosedClientConnections(); - return NBN_CLIENT_DISCONNECTED; + *ev = NBN_SERVER_DISCONNECTION; + return true; } if (message_info.type != NBN_CONNECTION_REQUEST_MESSAGE_TYPE) { nbn_game_server.server_data_writer.position = 0; - return NBN_CLIENT_MESSAGE_RECEIVED; + + *ev = NBN_SERVER_MESSAGE_RECEIVED; + return true; } // at this point we know it's a connection request @@ -2240,29 +2626,33 @@ static int GameServer_HandleMessageReceivedEvent(void) { if (message_info.length < 4) { LogError("Connection request invalid length"); - return NBN_ERROR; + *ev = NBN_ERROR; + return true; } - NBN_Reader *reader = NBN_GameServer_GetMessageReader(); + NBN_Reader *reader = NBN_GameServer_ReadMessage(); unsigned int data_length; if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) { LogError("Failed to read client data length"); - return NBN_ERROR; + *ev = NBN_ERROR; + return true; } if (data_length > 0) { if (data_length > sizeof(endpoint->connection_request_data_buffer)) { LogError("Received invalid connection request data"); - return NBN_ERROR; + *ev = NBN_ERROR; + return true; } if (NBN_Reader_ReadBytes(reader, nbn_game_server.endpoint.connection_request_data_buffer, data_length) < 0) { LogError("Failed to read client data"); - return NBN_ERROR; + *ev = NBN_ERROR; + return true; } } @@ -2270,13 +2660,16 @@ static int GameServer_HandleMessageReceivedEvent(void) { NBN_Event e; - e.type = NBN_NEW_CONNECTION; + e.type = NBN_SERVER_NEW_CONNECTION; e.data.connection = sender; - if (!NBN_EventQueue_Enqueue(&endpoint->event_queue, e)) - return NBN_ERROR; + if (!NBN_EventQueue_Enqueue(&endpoint->event_queue, e)) { + *ev = NBN_ERROR; + return true; + } - return NBN_NO_EVENT; + *ev = NBN_SERVER_NO_EVENT; + return true; } #pragma endregion /* NBN_GameServer */ @@ -2287,7 +2680,8 @@ static void ServerDriver_OnClientConnected(NBN_Connection *client) { GameServer_ static int ServerDriver_OnClientPacketReceived(NBN_Packet *packet) { if (Endpoint_ProcessReceivedPacket(&nbn_game_server.endpoint, packet, packet->sender) < 0) { - LogError("An error occured while processing packet from client %d, closing the client", packet->sender->id); + LogError("An error occured while processing packet from client %d, closing the client", + packet->sender->handle.id); return GameServer_CloseClientWithCode(packet->sender, -1, false); } @@ -2378,23 +2772,22 @@ static NBN_Connection_ID UDP_BuildConnectionID(NBN_IPAddress address) { static NBN_Connection *UDP_FindOrCreateClientConnectionByAddress(NBN_IPAddress address) { NBN_Connection_ID conn_id = UDP_BuildConnectionID(address); conn_id = NBN_BuildConnectionHash(conn_id, NBN_DRIVER_UDP); - NBN_Connection *conn = NBN_GameServer_FindConnection(conn_id); - - if (conn == NULL) { - // this is a new connection + NBN_ConnectionHandle *handle = NBN_GameServer_FindConnection(conn_id); - conn = NBN_GameServer_CreateClientConnection(NBN_DRIVER_UDP, conn_id); - conn->driver_data.udp.ip_address = address; + if (handle) { + return HANDLE_TO_CONN(handle); + } - LogInfo("New UDP connection (id: %llu, addr: %d, port: %d)", conn->id, address.host, address.port); + // this is a new connection + NBN_Connection *conn = CreateClientConnection(NBN_DRIVER_UDP, conn_id); + conn->driver_data.udp.ip_address = address; - if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, conn) < 0) { - LogError("Failed to raise game server event"); + LogInfo("New UDP connection (id: %llu, addr: %d, port: %d)", conn->handle.id, address.host, address.port); - return NULL; - } + if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, conn) < 0) { + LogError("Failed to raise game server event"); - return conn; + return NULL; } return conn; diff --git a/nbnet.h b/nbnet.h index 862cbd6..426712b 100644 --- a/nbnet.h +++ b/nbnet.h @@ -25,73 +25,77 @@ #ifndef NBNET_H #define NBNET_H -#include -#include #include -#include -#include -#include - -#if defined(_WIN32) || defined(_WIN64) - -#define NBN_PLATFORM_WINDOWS -#define WIN32_LEAN_AND_MEAN -// prevent inclusion of winnt.h in windows.h -#define _WINNT_ - -#include -#include - -#elif (defined(__APPLE__) && defined(__MACH__)) - -#define NBN_PLATFORM_MAC +#include -#else +#define NBN_ERROR -1 -#define NBN_PLATFORM_UNIX +/** + * - CONFIGURATION - + * + * The macros below can be redefined to change how the library behaves. + */ +// TODO: doc +#ifndef NBN_SERVER_INITIAL_DATA_MAX_SIZE +#define NBN_SERVER_INITIAL_DATA_MAX_SIZE 256 #endif -#ifdef __EMSCRIPTEN__ - -#include - +// TODO: doc +#ifndef NBN_CONNECTION_REQUEST_DATA_MAX_SIZE +#define NBN_CONNECTION_REQUEST_DATA_MAX_SIZE 256 #endif -#ifndef NBN_PLATFORM_WINDOWS +// TODO: doc +#ifndef NBN_CHANNEL_COUNT +#define NBN_CHANNEL_COUNT 2 +#endif -#include -#include -#include +// TODO: doc +#ifndef NBN_MESSAGE_MAX_SIZE +#define NBN_MESSAGE_MAX_SIZE 256 +#endif -#ifndef CLOCK_MONOTONIC_RAW -#define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC +// TODO: doc +#ifndef NBN_CHANNEL_BUFFER_SIZE +#define NBN_CHANNEL_BUFFER_SIZE 256 #endif +// TODO: doc +#ifndef NBN_MESSAGE_RESEND_DELAY +#define NBN_MESSAGE_RESEND_DELAY 0.1 /* Number of seconds before a message is resent (reliable messages redundancy) */ #endif -#define NBN_ERROR -1 +/** + * Number of seconds before a connection is considered stale and closes + */ +#ifndef NBN_CONNECTION_STALE_TIME_THRESHOLD +#define NBN_CONNECTION_STALE_TIME_THRESHOLD 3 +#endif -typedef struct NBN_Endpoint NBN_Endpoint; -typedef struct NBN_Connection NBN_Connection; -typedef struct NBN_Channel NBN_Channel; -typedef struct NBN_Driver NBN_Driver; -typedef enum NBN_Driver_ID NBN_Driver_ID; +/* ========================================================================== */ typedef enum NBN_LogLevel { NBN_LOG_ERROR, NBN_LOG_INFO, NBN_LOG_WARNING, NBN_LOG_DEBUG } NBN_LogLevel; - typedef void (*NBN_LogFunc)(NBN_LogLevel level, const char *filename, int line, const char *msg, ...); void NBN_SetLogFunction(NBN_LogFunc); void NBN_SetLogLevel(NBN_LogLevel); -#pragma region Serialization - -#define B_MASK(n) (1u << (n)) -#define B_SET(mask, n) (mask |= B_MASK(n)) -#define B_UNSET(mask, n) (mask &= ~B_MASK(n)) -#define B_IS_SET(mask, n) ((B_MASK(n) & mask) == B_MASK(n)) -#define B_IS_UNSET(mask, n) ((B_MASK(n) & mask) == 0) +/** + * - SERIALIZATION API - + * + * Functions to read and write nbnet messages. + * + * You'll never need to create a reader or writer yourself - nbnet will provide pointers to + * NBN_Reader or NBN_Writer, see the client and server API sections. + * + * NBN_Writer_Write* functions will assert if trying to write outside the buffer. + * + * NBN_Reader_Read* functions returns NBN_ERROR if trying to read outside the buffer. + * If read successully, 0 is returned and the value pointer points to the read value. + * + * Write and Read functions account for endianness. + */ typedef struct NBN_Writer { uint8_t *buffer; @@ -123,447 +127,77 @@ int NBN_Reader_ReadFloat(NBN_Reader *reader, float *value); int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length); int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len); -#pragma endregion /* Serialization */ - -#pragma region NBN_Message - -#define NBN_MAX_MESSAGE_TYPES UINT8_MAX -#define NBN_RESERVED_MESSAGE_TYPES 4 /* Number of message types reserved for the library */ -#define NBN_MESSAGE_RESEND_DELAY 0.1 /* Number of seconds before a message is resent (reliable messages redundancy) */ -#define NBN_MESSAGE_HEADER_SIZE 6 /* See NBN_MessageHeader struct */ -#define NBN_RESERVED_MESSAGE_BUFFER_LEN 32 /* Fixed size used for all library reserved messages' buffer */ -#define NBN_MESSAGE_MAX_SIZE 256 - -// IMPORTANT: make sure you update NBN_MESSAGE_HEADER_SIZE if you modify NBN_MessageHeader struct -typedef struct NBN_MessageHeader { - uint16_t id; - uint16_t length; - uint8_t type; - uint8_t channel_id; -} NBN_MessageHeader; - -typedef enum { NBN_OUTGOING_MESSAGE, NBN_INCOMING_MESSAGE } NBN_MessageType; +/* ============================================= */ -typedef struct NBN_Message { - NBN_MessageHeader header; - NBN_MessageType type; - NBN_Connection *sender; - uint8_t data[NBN_MESSAGE_MAX_SIZE]; -} NBN_Message; +typedef uint64_t NBN_Connection_ID; -typedef struct NBN_OutgoingMessage { - NBN_Message message; - uint16_t id; // TODO: needed? - double last_send_time; - bool free; -} NBN_OutgoingMessage; +typedef struct NBN_ConnectionHandle { + // TODO: use 32 bits ID and 64 bits hash for the hashtable + NBN_Connection_ID id; + void *user_data; +} NBN_ConnectionHandle; -typedef struct NBN_IncomingMessage { - NBN_Message message; - bool free; -} NBN_IncomingMessage; +typedef struct NBN_ConnectionStats { + double ping; + unsigned int total_lost_packets; + float packet_loss; + float upload_bandwidth; + float download_bandwidth; +} NBN_ConnectionStats; /** * Information about a received message. */ typedef struct NBN_MessageInfo { - /** User defined message type */ + /** + * Type of the message + */ uint8_t type; - /** Channel the message was received on */ + /** + * Channel the message was received on + */ uint8_t channel_id; - /** Message data */ + /** + * Pointer to the internal message data buffer + */ uint8_t *data; - /** Length of message data in bytes */ + /* + * Length of the message data in bytes + */ uint16_t length; /** - * The message's sender. + * A handle to the connection that sent the message. * - * On the client side, it will always be NULL as all received messages come from the game server + * On the client, it will always be NULL as all messages are received from the server. */ - NBN_Connection *sender; + NBN_ConnectionHandle *sender; } NBN_MessageInfo; -#pragma endregion /* NBN_Message */ - -#pragma region NBN_Packet - -/* - * Maximum allowed packet size (including header) in bytes. - * The 1400 value has been chosen based on this statement: - * - * With the IPv4 header being 20 bytes and the UDP header being 8 bytes, the payload - * of a UDP packet should be no larger than 1500 - 20 - 8 = 1472 bytes to avoid fragmentation. - */ -#define NBN_PACKET_MAX_SIZE 1400 -#define NBN_MAX_MESSAGES_PER_PACKET UINT8_MAX - -#define NBN_PACKET_HEADER_SIZE 13 - -/* Maximum size of packet's data (NBN_PACKET_MAX_DATA_SIZE + NBN_PACKET_HEADER_SIZE = NBN_PACKET_MAX_SIZE) */ -#define NBN_PACKET_MAX_DATA_SIZE (NBN_PACKET_MAX_SIZE - NBN_PACKET_HEADER_SIZE) - -enum { - NBN_PACKET_WRITE_ERROR = -1, - NBN_PACKET_WRITE_OK, - NBN_PACKET_WRITE_NO_SPACE, -}; - -typedef enum NBN_PacketMode { NBN_PACKET_MODE_WRITE = 1, NBN_PACKET_MODE_READ } NBN_PacketMode; - -// IMPORTANT: don't forget to update NBN_PACKET_HEADER_SIZE after modifying this structure -typedef struct NBN_PacketHeader { - uint32_t protocol_id; - uint32_t ack_bits; - uint16_t seq_number; - uint16_t ack; - uint8_t messages_count; -} NBN_PacketHeader; - -typedef struct NBN_Packet { - NBN_PacketHeader header; - NBN_PacketMode mode; - struct NBN_Connection *sender; /* not serialized, filled by the network driver upon reception */ - uint8_t buffer[NBN_PACKET_MAX_SIZE]; - unsigned int size; /* in bytes */ - bool sealed; -} NBN_Packet; - -void NBN_Packet_InitWrite(NBN_Packet *, uint32_t, uint16_t, uint16_t, uint32_t); -int NBN_Packet_WriteMessage(NBN_Packet *, NBN_OutgoingMessage *); -int NBN_Packet_Seal(NBN_Packet *); -int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); - -#pragma endregion /* NBN_Packet */ - -#pragma region Library reserved messages - -// IMPORTANT: update NBN_RESERVED_MESSAGE_TYPES if you add or remove library messages -#define NBN_CLIENT_CLOSED_MESSAGE_TYPE NBN_MAX_MESSAGE_TYPES -#define NBN_CLIENT_ACCEPTED_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 1) -#define NBN_DISCONNECTION_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 2) -#define NBN_CONNECTION_REQUEST_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 3) - -#define NBN_SERVER_INITIAL_DATA_MAX_SIZE 256 -#define NBN_CONNECTION_REQUEST_DATA_MAX_SIZE 256 - -#pragma endregion /* Library reserved messages */ - -#pragma region NBN_Channel - -#ifndef NBN_CHANNEL_COUNT -#define NBN_CHANNEL_COUNT 2 -#endif - -#define NBN_CHANNEL_BUFFER_SIZE 256 - -typedef enum NBN_ChannelType { NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_RELIABLE } NBN_ChannelType; - -struct NBN_Channel { - uint8_t id; - NBN_ChannelType type; - uint16_t next_outgoing_message_id; - uint16_t next_recv_message_id; - uint16_t oldest_unacked_message_id; - uint16_t most_recent_message_id; - uint16_t last_received_message_id; - unsigned int next_outgoing_message_slot; - unsigned int outgoing_message_count; - NBN_OutgoingMessage outgoing_messages_buffer[NBN_CHANNEL_BUFFER_SIZE]; - NBN_IncomingMessage incoming_messages_buffer[NBN_CHANNEL_BUFFER_SIZE]; - bool ack_buffer[NBN_CHANNEL_BUFFER_SIZE]; -}; - -void NBN_Channel_Destroy(NBN_Endpoint *, NBN_Channel *); - -typedef struct NBN_UnreliableOrderedChannel { - NBN_Channel base; -} NBN_UnreliableOrderedChannel; - -#pragma endregion /* NBN_Channel */ - -#pragma region NBN_Connection - -#define NBN_MAX_PACKET_ENTRIES 1024 +// TODO: doc +typedef enum NBN_ChannelMode { NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_RELIABLE } NBN_ChannelMode; -/* Maximum number of packets that can be sent in a single flush - * - * IMPORTANT: do not increase this, it will break packet acks +/** + * - CLIENT API - */ -#define NBN_CONNECTION_MAX_SENT_PACKET_COUNT 16 - -/* Number of seconds before the connection is considered stale and get closed */ -#define NBN_CONNECTION_STALE_TIME_THRESHOLD 3 - -typedef struct NBN_MessageEntry { - uint16_t id; - uint8_t channel_id; -} NBN_MessageEntry; - -typedef struct NBN_PacketEntry { - bool acked; - bool flagged_as_lost; - unsigned int messages_count; - double send_time; - NBN_MessageEntry messages[NBN_MAX_MESSAGES_PER_PACKET]; -} NBN_PacketEntry; - -typedef struct NBN_ConnectionStats { - double ping; - unsigned int total_lost_packets; - float packet_loss; - float upload_bandwidth; - float download_bandwidth; -} NBN_ConnectionStats; - -#ifdef NBN_DEBUG - -typedef enum NBN_ConnectionDebugCallback { NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE } NBN_ConnectionDebugCallback; - -#endif /* NBN_DEBUG */ - -typedef struct NBN_IPAddress { - uint32_t host; - uint16_t port; -} NBN_IPAddress; - -typedef uint64_t NBN_Connection_ID; - -struct NBN_Connection { - // TODO: use 32 bits ID and 64 bits hash for the hashtable - NBN_Connection_ID id; - double last_recv_packet_time; /* Used to detect stale connections */ - double last_flush_time; /* Last time the send queue was flushed */ - double last_read_packets_time; /* Last time packets were read from the network driver */ - /* Keep track of bytes read from the socket (used for download bandwith calculation) */ - unsigned int downloaded_bytes; - uint8_t is_accepted : 1; - uint8_t is_stale : 1; - uint8_t is_closed : 1; - struct NBN_Endpoint *endpoint; - NBN_Driver *driver; /* Network driver used for that connection */ - NBN_Channel channels[NBN_CHANNEL_COUNT]; /* Message channels (sending & receiving) */ - NBN_ConnectionStats stats; - void *user_data; /* Pointer to user-defined data */ - - /* Driver-related data attached to the connection */ - union { - struct { - NBN_IPAddress ip_address; - } udp; - } driver_data; - -#ifdef NBN_DEBUG - /* Debug callbacks */ - void (*OnMessageAddedToRecvQueue)(struct NBN_Connection *, NBN_Message *); // TODO: rename this function pointer -#endif /* NBN_DEBUG */ - - /* - * Packet sequencing & acking - */ - uint16_t next_packet_seq_number; - uint16_t last_received_packet_seq_number; - uint32_t packet_send_seq_buffer[NBN_MAX_PACKET_ENTRIES]; - NBN_PacketEntry packet_send_buffer[NBN_MAX_PACKET_ENTRIES]; - uint32_t packet_recv_seq_buffer[NBN_MAX_PACKET_ENTRIES]; -}; - -typedef struct NBN_ConnectionListNode NBN_ConnectionListNode; - -/* Linked list of connections */ -struct NBN_ConnectionListNode { - NBN_Connection *conn; - NBN_ConnectionListNode *next; - NBN_ConnectionListNode *prev; -}; - -typedef uint8_t *(*NBN_AllocMessageFunc)(NBN_MessageHeader *); -typedef uint8_t *(*NBN_DeallocMessageFunc)(NBN_MessageHeader *, uint8_t *); - -int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); -int NBN_Connection_FlushChannels(NBN_Endpoint *, NBN_Connection *, uint32_t, double); -bool NBN_Connection_CheckIfStale(NBN_Connection *, double); - -#pragma endregion /* NBN_Connection */ - -#pragma region NBN_EventQueue - -#define NBN_NO_EVENT 0 /* No event left in the events queue */ -#define NBN_SKIP_EVENT 1 /* Indicates that the event should be skipped */ -#define NBN_EVENT_QUEUE_CAPACITY 1024 - -typedef struct NBN_DisconnectionInfo { - uint32_t conn_id; /* ID if the disconnected connection */ - void *user_data; /* User-defined data associated with this connection */ -} NBN_DisconnectionInfo; - -typedef union NBN_EventData { - NBN_MessageInfo message_info; - NBN_Connection *connection; - NBN_DisconnectionInfo disconnection; -} NBN_EventData; -typedef struct NBN_Event { - int type; - NBN_EventData data; -} NBN_Event; +typedef enum NBN_Client_Event { + NBN_CLIENT_ERROR = NBN_ERROR, -typedef struct NBN_EventQueue { - NBN_Event events[NBN_EVENT_QUEUE_CAPACITY]; - unsigned int head; - unsigned int tail; - unsigned int count; -} NBN_EventQueue; - -NBN_EventQueue *NBN_EventQueue_Create(void); -void NBN_EventQueue_Destroy(NBN_EventQueue *); -bool NBN_EventQueue_Enqueue(NBN_EventQueue *, NBN_Event); -bool NBN_EventQueue_Dequeue(NBN_EventQueue *, NBN_Event *); -bool NBN_EventQueue_IsEmpty(NBN_EventQueue *); - -#pragma endregion /* NBN_EventQueue */ - -#pragma region Packet simulator - -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - -#ifndef NBN_PLATFORM_WINDOWS -#include -#endif /* NBN_PLATFORM_WINDOWS */ - -typedef struct NBN_PacketSimulatorEntry NBN_PacketSimulatorEntry; - -struct NBN_PacketSimulatorEntry { - NBN_Packet packet; - NBN_Connection *receiver; - double delay; - double enqueued_at; - struct NBN_PacketSimulatorEntry *next; - struct NBN_PacketSimulatorEntry *prev; -}; + NBN_CLIENT_NO_EVENT = 0, -typedef struct NBN_PacketSimulator { - NBN_Endpoint *endpoint; - NBN_PacketSimulatorEntry *head_packet; - NBN_PacketSimulatorEntry *tail_packet; - unsigned int packet_count; - -#ifdef NBN_PLATFORM_WINDOWS - HANDLE queue_mutex; - HANDLE thread; -#else - pthread_mutex_t queue_mutex; - pthread_t thread; -#endif - - bool running; - unsigned int total_dropped_packets; - - /* Settings */ - float packet_loss_ratio; - float current_packet_loss_ratio; - float packet_duplication_ratio; - double ping; - double jitter; -} NBN_PacketSimulator; - -#define NBN_GameClient_SetPing(v) \ - { nbn_game_client.endpoint.packet_simulator.ping = v; } -#define NBN_GameClient_SetJitter(v) \ - { nbn_game_client.endpoint.packet_simulator.jitter = v; } -#define NBN_GameClient_SetPacketLoss(v) \ - { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } -#define NBN_GameClient_SetPacketDuplication(v) \ - { nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; } - -#define NBN_GameServer_SetPing(v) \ - { nbn_game_server.endpoint.packet_simulator.ping = v; } -#define NBN_GameServer_SetJitter(v) \ - { nbn_game_server.endpoint.packet_simulator.jitter = v; } -#define NBN_GameServer_SetPacketLoss(v) \ - { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } -#define NBN_GameServer_SetPacketDuplication(v) \ - { nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; } - -#else - -#define NBN_PacketSimulator_Disabled \ - do { \ - } while (0); - -#define NBN_GameClient_SetPing(v) NBN_PacketSimulator_Disabled -#define NBN_GameClient_SetJitter(v) NBN_PacketSimulator_Disabled -#define NBN_GameClient_SetPacketLoss(v) NBN_PacketSimulator_Disabled -#define NBN_GameClient_SetPacketDuplication(v) NBN_PacketSimulator_Disabled - -#define NBN_GameServer_SetPing(v) NBN_PacketSimulator_Disabled -#define NBN_GameServer_SetJitter(v) NBN_PacketSimulator_Disabled -#define NBN_GameServer_SetPacketLoss(v) NBN_PacketSimulator_Disabled -#define NBN_GameServer_SetPacketDuplication(v) NBN_PacketSimulator_Disabled - -#endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ - -#pragma endregion /* Packet simulator */ - -#pragma region NBN_Endpoint - -struct NBN_Endpoint { - NBN_EventQueue event_queue; - uint32_t protocol_id; - bool is_server; - double time; - NBN_Message write_message; - NBN_Writer message_writer; - NBN_Reader message_reader; - uint8_t server_initial_data_buffer[NBN_SERVER_INITIAL_DATA_MAX_SIZE]; - uint8_t connection_request_data_buffer[NBN_CONNECTION_REQUEST_DATA_MAX_SIZE]; - unsigned int client_connection_request_data_len; - unsigned int server_initial_data_len; - -#ifdef NBN_DEBUG - /* Debug callbacks */ - void (*OnMessageAddedToRecvQueue)(NBN_Connection *, NBN_Message *); -#endif - -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - NBN_PacketSimulator packet_simulator; -#endif -}; - -#pragma endregion /* NBN_Endpoint */ - -#pragma region NBN_GameClient - -enum { /* Client is connected to server */ - NBN_CONNECTED = 2, + NBN_CLIENT_CONNECTED, /* Client is disconnected from the server */ - NBN_DISCONNECTED, + NBN_CLIENT_DISCONNECTED, /* Client has received a message from the server */ - NBN_MESSAGE_RECEIVED -}; - -typedef struct NBN_GameClient_Config { - const char *protocol_name; - const char *host; - uint16_t port; -} NBN_GameClient_Config; - -typedef struct NBN_GameClient { - NBN_Endpoint endpoint; - NBN_GameClient_Config config; - NBN_Connection *server_connection; - bool is_connected; - NBN_Event last_event; - int closed_code; - NBN_Writer client_data_writer; - NBN_Reader server_data_reader; -} NBN_GameClient; + NBN_CLIENT_MESSAGE_RECEIVED +} NBN_Client_Event; /** * Initialize the game client with minimal configuration. @@ -575,6 +209,9 @@ typedef struct NBN_GameClient { */ void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port); +// TODO: doc +void NBN_GameClient_SetChannelMode(uint8_t channel_id, NBN_ChannelMode mode); + // TODO: doc NBN_Writer *NBN_GameClient_GetConnectionRequestDataWriter(void); @@ -601,7 +238,7 @@ NBN_Reader *NBN_GameClient_GetServerDataReader(void); * * @return The code of the polled event or NBN_NO_EVENT when there is no more events. */ -int NBN_GameClient_Poll(void); +NBN_Client_Event NBN_GameClient_Poll(void); /** * Pack all enqueued messages into packets and send them. @@ -620,15 +257,7 @@ NBN_Writer *NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id); int NBN_GameClient_EnqueueMessage(void); // TODO: doc -NBN_Writer *NBN_GameClient_GetMessageWriter(void); - -// TODO: doc -NBN_Reader *NBN_GameClient_GetMessageReader(void); - -/** - * For drivers only! NOT MEANT TO BE USED BY USER CODE. - */ -NBN_Connection *NBN_GameClient_CreateServerConnection(NBN_Driver_ID driver_id); +NBN_Reader *NBN_GameClient_ReadMessage(void); /** * Retrieve the info about the last received message. @@ -661,50 +290,32 @@ int NBN_GameClient_GetServerCloseCode(void); */ bool NBN_GameClient_IsConnected(void); -#ifdef NBN_DEBUG +/* ============================================= */ -void NBN_GameClient_Debug_RegisterCallback(NBN_ConnectionDebugCallback, void *); +typedef enum NBN_Server_Event { + NBN_SERVER_ERROR = NBN_ERROR, -#endif /* NBN_DEBUG */ + NBN_SERVER_NO_EVENT = 0, -#pragma endregion /* NBN_GameClient */ - -#pragma region NBN_GameServer - -enum { /* A new client has connected */ - NBN_NEW_CONNECTION = 2, + NBN_SERVER_NEW_CONNECTION, /* A client has disconnected */ - NBN_CLIENT_DISCONNECTED, + NBN_SERVER_DISCONNECTION, /* A message has been received from a client */ - NBN_CLIENT_MESSAGE_RECEIVED -}; + NBN_SERVER_MESSAGE_RECEIVED +} NBN_Server_Event; typedef struct NBN_GameServerStats { float upload_bandwidth; /* Total upload bandwith of the game server */ float download_bandwidth; /* Total download bandwith of the game server */ } NBN_GameServerStats; -typedef struct NBN_GameServer_Config { - const char *protocol_name; - uint16_t port; -} NBN_GameServer_Config; - -typedef struct NBN_GameServer { - NBN_Endpoint endpoint; - NBN_GameServer_Config config; - struct { - NBN_Connection_ID key; - NBN_Connection *value; - } *clients; - NBN_ConnectionListNode *closed_clients_head; - NBN_GameServerStats stats; - NBN_Event last_event; - NBN_Writer server_data_writer; - NBN_Reader client_data_reader; -} NBN_GameServer; +typedef struct NBN_DisconnectionInfo { + NBN_Connection_ID conn_id; /* ID if the disconnected connection */ + void *user_data; /* User-defined data associated with this connection */ +} NBN_DisconnectionInfo; /** * Initialize the game server with minimal configuration. @@ -715,6 +326,9 @@ typedef struct NBN_GameServer { */ void NBN_GameServer_Init(const char *protocol_name, uint16_t port); +// TODO: doc +void NBN_GameServer_SetChannelMode(uint8_t channel_id, NBN_ChannelMode mode); + /** * Start the game server with the provided configuration. * @@ -728,13 +342,13 @@ int NBN_GameServer_Start(void); void NBN_GameServer_Stop(void); // TODO: doc -NBN_Connection *NBN_GameServer_FindConnection(NBN_Connection_ID); +NBN_ConnectionHandle *NBN_GameServer_FindConnection(NBN_Connection_ID); // TODO: doc unsigned int NBN_GameServer_GetClientCount(void); // TODO: doc -NBN_Connection *NBN_GameServer_GetClientByIndex(unsigned int index); +NBN_ConnectionHandle *NBN_GameServer_GetClientByIndex(unsigned int index); /** * Poll game server events. @@ -743,7 +357,7 @@ NBN_Connection *NBN_GameServer_GetClientByIndex(unsigned int index); * * @return The code of the polled event or NBN_NO_EVENT when there is no more events. */ -int NBN_GameServer_Poll(void); +NBN_Server_Event NBN_GameServer_Poll(void); /** * Pack all enqueued messages into packets and send them. @@ -755,11 +369,6 @@ int NBN_GameServer_Poll(void); */ int NBN_GameServer_Flush(void); -/** - * For drivers only! NOT MEANT TO BE USED BY USER CODE. - */ -NBN_Connection *NBN_GameServer_CreateClientConnection(NBN_Driver_ID, NBN_Connection_ID); - /** * Close a client's connection without a specific code (default code is -1) * @@ -767,7 +376,7 @@ NBN_Connection *NBN_GameServer_CreateClientConnection(NBN_Driver_ID, NBN_Connect * * @return 0 when successful, -1 otherwise */ -int NBN_GameServer_CloseClient(NBN_Connection *conn); +int NBN_GameServer_CloseClient(NBN_ConnectionHandle *conn); /** * Close a client's connection with a specific code. @@ -779,17 +388,17 @@ int NBN_GameServer_CloseClient(NBN_Connection *conn); * * @return 0 when successful, -1 otherwise */ -int NBN_GameServer_CloseClientWithCode(NBN_Connection *conn, int code); +int NBN_GameServer_CloseClientWithCode(NBN_ConnectionHandle *conn, int code); // TODO: doc NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id); // TODO: doc -int NBN_GameServer_EnqueueMessageFor(NBN_Connection *conn); +int NBN_GameServer_EnqueueMessageFor(NBN_ConnectionHandle *conn); // TODO: doc int NBN_GameServer_EnqueueBroadcastMessage(void); // TODO: doc -NBN_Reader *NBN_GameServer_GetMessageReader(void); +NBN_Reader *NBN_GameServer_ReadMessage(void); // TODO: doc NBN_Writer *NBN_GameServer_GetConnectionDataWriter(void); @@ -825,7 +434,7 @@ int NBN_GameServer_RejectIncomingConnection(void); * * @return A pointer to a NBN_Connection representing the new connection */ -NBN_Connection *NBN_GameServer_GetIncomingConnection(void); +NBN_ConnectionHandle *NBN_GameServer_GetIncomingConnection(void); // TODO: doc NBN_Reader *NBN_GameServer_GetConnectionRequestDataReader(void); @@ -857,67 +466,82 @@ NBN_MessageInfo NBN_GameServer_GetMessageInfo(void); */ NBN_GameServerStats NBN_GameServer_GetStats(void); -#ifdef NBN_DEBUG +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) -void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback, void *); +#ifndef NBN_PLATFORM_WINDOWS +#include +#endif /* NBN_PLATFORM_WINDOWS */ -#endif /* NBN_DEBUG */ +typedef struct NBN_PacketSimulatorEntry NBN_PacketSimulatorEntry; -#pragma endregion /* NBN_GameServer */ +struct NBN_PacketSimulatorEntry { + NBN_Packet packet; + NBN_Connection *receiver; + double delay; + double enqueued_at; + struct NBN_PacketSimulatorEntry *next; + struct NBN_PacketSimulatorEntry *prev; +}; -#pragma region Network driver +typedef struct NBN_PacketSimulator { + NBN_Endpoint *endpoint; + NBN_PacketSimulatorEntry *head_packet; + NBN_PacketSimulatorEntry *tail_packet; + unsigned int packet_count; -typedef enum NBN_DriverEvent { - // Client events - NBN_DRIVER_CLI_PACKET_RECEIVED, +#ifdef NBN_PLATFORM_WINDOWS + HANDLE queue_mutex; + HANDLE thread; +#else + pthread_mutex_t queue_mutex; + pthread_t thread; +#endif - // Server events - NBN_DRIVER_SERV_CLIENT_CONNECTED, - NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, -} NBN_DriverEvent; + bool running; + unsigned int total_dropped_packets; -typedef int (*NBN_Driver_Func_ClientStart)(NBN_GameClient *, const char *, uint16_t); -typedef void (*NBN_Driver_Func_ClientStop)(NBN_GameClient *); -typedef int (*NBN_Driver_Func_ClientSendPacket)(NBN_GameClient *, NBN_Packet *); -typedef int (*NBN_Driver_Func_ClientRecvPackets)(NBN_GameClient *); + /* Settings */ + float packet_loss_ratio; + float current_packet_loss_ratio; + float packet_duplication_ratio; + double ping; + double jitter; +} NBN_PacketSimulator; -typedef int (*NBN_Driver_Func_ServerStart)(NBN_GameServer *, uint16_t); -typedef void (*NBN_Driver_Func_ServerStop)(NBN_GameServer *); -typedef int (*NBN_Driver_Func_ServerSendPacketTo)(NBN_GameServer *, NBN_Packet *, NBN_Connection *); -typedef void (*NBN_Driver_Func_ServerCleanupConnection)(NBN_GameServer *, NBN_Connection *); -typedef int (*NBN_Driver_Func_ServerRecvPackets)(NBN_GameServer *); +#define NBN_GameClient_SetPing(v) \ + { nbn_game_client.endpoint.packet_simulator.ping = v; } +#define NBN_GameClient_SetJitter(v) \ + { nbn_game_client.endpoint.packet_simulator.jitter = v; } +#define NBN_GameClient_SetPacketLoss(v) \ + { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } +#define NBN_GameClient_SetPacketDuplication(v) \ + { nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; } -typedef struct NBN_Driver_Implementation { - /* Client functions */ - NBN_Driver_Func_ClientStart cli_start; - NBN_Driver_Func_ClientStop cli_stop; - NBN_Driver_Func_ClientRecvPackets cli_recv_packets; - NBN_Driver_Func_ClientSendPacket cli_send_packet; +#define NBN_GameServer_SetPing(v) \ + { nbn_game_server.endpoint.packet_simulator.ping = v; } +#define NBN_GameServer_SetJitter(v) \ + { nbn_game_server.endpoint.packet_simulator.jitter = v; } +#define NBN_GameServer_SetPacketLoss(v) \ + { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } +#define NBN_GameServer_SetPacketDuplication(v) \ + { nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; } - /* Server functions */ - NBN_Driver_Func_ServerStart serv_start; - NBN_Driver_Func_ServerStop serv_stop; - NBN_Driver_Func_ServerRecvPackets serv_recv_packets; - NBN_Driver_Func_ServerSendPacketTo serv_send_packet_to; - NBN_Driver_Func_ServerCleanupConnection serv_cleanup_connection; -} NBN_Driver_Implementation; +#else -enum NBN_Driver_ID { NBN_DRIVER_UDP = 0x01 }; +#define NBN_PacketSimulator_Disabled \ + do { \ + } while (0); -struct NBN_Driver { - int id; - const char *name; - NBN_Driver_Implementation impl; -}; +#define NBN_GameClient_SetPing(v) NBN_PacketSimulator_Disabled +#define NBN_GameClient_SetJitter(v) NBN_PacketSimulator_Disabled +#define NBN_GameClient_SetPacketLoss(v) NBN_PacketSimulator_Disabled +#define NBN_GameClient_SetPacketDuplication(v) NBN_PacketSimulator_Disabled -/** - * Let nbnet know about specific network events happening within a network driver. - * - * @param ev Event type - * @param data Arbitrary data about the event - */ -int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data); +#define NBN_GameServer_SetPing(v) NBN_PacketSimulator_Disabled +#define NBN_GameServer_SetJitter(v) NBN_PacketSimulator_Disabled +#define NBN_GameServer_SetPacketLoss(v) NBN_PacketSimulator_Disabled +#define NBN_GameServer_SetPacketDuplication(v) NBN_PacketSimulator_Disabled -#pragma endregion /* Network driver */ +#endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ #endif /* NBNET_H */ diff --git a/soak/client.c b/soak/client.c index 87f9853..eb180cf 100644 --- a/soak/client.c +++ b/soak/client.c @@ -288,6 +288,10 @@ int main(int argc, char *argv[]) { NBN_GameClient_Init(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT); + for (uint8_t c = 0; c < NBN_CHANNEL_COUNT; c++) { + NBN_GameClient_SetChannelMode(c, NBN_CHANNEL_RELIABLE); + } + if (NBN_GameClient_Start() < 0) { LogError("Failed to start game client. Exit"); @@ -325,9 +329,6 @@ int main(int argc, char *argv[]) { channels[NBN_CHANNEL_COUNT - 1].message_count += leftover_message_count; - NBN_GameClient_Debug_RegisterCallback(NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE, - (void *)Soak_Debug_PrintAddedToRecvQueue); - int ret = Soak_MainLoop(Tick, channels); NBN_GameClient_Stop(); diff --git a/soak/server.c b/soak/server.c index ed332d2..c220e4a 100644 --- a/soak/server.c +++ b/soak/server.c @@ -23,6 +23,7 @@ */ #include +#include #include #include #include "soak.h" @@ -295,6 +296,10 @@ int main(int argc, char *argv[]) { NBN_GameServer_Init(SOAK_PROTOCOL_NAME, SOAK_PORT); + for (uint8_t c = 0; c < NBN_CHANNEL_COUNT; c++) { + NBN_GameServer_SetChannelMode(c, NBN_CHANNEL_RELIABLE); + } + if (NBN_GameServer_Start()) { LogError("Failed to start game server"); diff --git a/soak/soak.c b/soak/soak.c index 947e99f..23d8ef8 100644 --- a/soak/soak.c +++ b/soak/soak.c @@ -190,24 +190,6 @@ void Soak_Stop(void) { SoakOptions Soak_GetOptions(void) { return soak_options; } -void Soak_Debug_PrintAddedToRecvQueue(NBN_Connection *conn, NBN_Message *msg) { - // FIXME: - /*if (msg->header.type == NBN_MESSAGE_CHUNK_TYPE) - { - NBN_MessageChunk *chunk = (NBN_MessageChunk *)msg->data; - - Soak_LogDebug("Soak message chunk added to recv queue (chunk id: %d, chunk total: %d)", - chunk->id, chunk->total); - } - else - { - SoakMessage *soak_message = (SoakMessage *)msg->data; - - Soak_LogDebug("Soak message added to recv queue (conn id: %d, msg id: %d, soak msg id: %d)", - conn->id, msg->header.id, soak_message->id); - }*/ -} - unsigned int Soak_GetCreatedOutgoingSoakMessageCount(void) { return created_outgoing_soak_message_count; } unsigned int Soak_GetDestroyedOutgoingSoakMessageCount(void) { return destroyed_outgoing_soak_message_count; } diff --git a/soak/soak.h b/soak/soak.h index 3bf6df6..2d1c12c 100644 --- a/soak/soak.h +++ b/soak/soak.h @@ -71,7 +71,6 @@ int Soak_ReadCommandLine(int, char *[]); int Soak_MainLoop(int (*Tick)(void *), void *data); void Soak_Stop(void); SoakOptions Soak_GetOptions(void); -void Soak_Debug_PrintAddedToRecvQueue(NBN_Connection *, NBN_Message *); unsigned int Soak_GetCreatedOutgoingSoakMessageCount(void); unsigned int Soak_GetDestroyedOutgoingSoakMessageCount(void); unsigned int Soak_GetCreatedIncomingSoakMessageCount(void); From f3331e4f9880c7669962a788e0bdbc063f952f88 Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Fri, 6 Feb 2026 09:15:44 +0100 Subject: [PATCH 38/85] remove driver events --- nbnet.c | 56 ++++++-------------------------------------------------- nbnet.h | 36 ++++++++++++++++++++++++++---------- 2 files changed, 32 insertions(+), 60 deletions(-) diff --git a/nbnet.c b/nbnet.c index 031f178..cad1c34 100644 --- a/nbnet.c +++ b/nbnet.c @@ -222,7 +222,7 @@ typedef struct NBN_Message { typedef struct NBN_OutgoingMessage { NBN_Message message; - uint16_t id; // TODO: needed? + uint16_t id; double last_send_time; bool free; } NBN_OutgoingMessage; @@ -383,15 +383,6 @@ typedef struct NBN_GameClient { static NBN_GameServer nbn_game_server; static NBN_GameClient nbn_game_client; -typedef enum NBN_DriverEvent { - // Client events - NBN_DRIVER_CLI_PACKET_RECEIVED, - - // Server events - NBN_DRIVER_SERV_CLIENT_CONNECTED, - NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, -} NBN_DriverEvent; - typedef int (*NBN_Driver_Func_ClientStart)(NBN_GameClient *, const char *, uint16_t); typedef void (*NBN_Driver_Func_ClientStop)(NBN_GameClient *); typedef int (*NBN_Driver_Func_ClientSendPacket)(NBN_GameClient *, NBN_Packet *); @@ -426,14 +417,6 @@ struct NBN_Driver { NBN_Driver_Implementation impl; }; -/** - * Let nbnet know about specific network events happening within a network driver. - * - * @param ev Event type - * @param data Arbitrary data about the event - */ -int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data); - void NBN_Packet_InitWrite(NBN_Packet *, uint32_t, uint16_t, uint16_t, uint32_t); NBN_PacketResult NBN_Packet_WriteMessage(NBN_Packet *, NBN_OutgoingMessage *); int NBN_Packet_Seal(NBN_Packet *); @@ -1645,24 +1628,6 @@ static void ClientDriver_OnPacketReceived(NBN_Packet *packet); static void ServerDriver_OnClientConnected(NBN_Connection *); static int ServerDriver_OnClientPacketReceived(NBN_Packet *); -// TODO: just have the driver call functions from this file WTF -int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data) { - switch (ev) { - case NBN_DRIVER_CLI_PACKET_RECEIVED: - ClientDriver_OnPacketReceived((NBN_Packet *)data); - break; - - case NBN_DRIVER_SERV_CLIENT_CONNECTED: - ServerDriver_OnClientConnected((NBN_Connection *)data); - break; - - case NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED: - return ServerDriver_OnClientPacketReceived((NBN_Packet *)data); - } - - return 0; -} - #pragma endregion /* Network driver */ #pragma region NBN_GameClient @@ -1803,7 +1768,7 @@ void NBN_GameClient_Stop(void) { LogInfo("Stopped"); } -NBN_Reader *NBN_GameClient_GetServerDataReader(void) { +NBN_Reader *NBN_GameClient_ReadServerData(void) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; NBN_Reader_Init(&nbn_game_client.server_data_reader, endpoint->server_initial_data_buffer, @@ -2366,7 +2331,7 @@ NBN_ConnectionHandle *NBN_GameServer_GetIncomingConnection(void) { return (NBN_ConnectionHandle *)nbn_game_server.last_event.data.connection; } -NBN_Reader *NBN_GameServer_GetConnectionRequestDataReader(void) { +NBN_Reader *NBN_GameServer_ReadConnectionRequestData(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); NBN_Endpoint *endpoint = &nbn_game_server.endpoint; @@ -2784,12 +2749,7 @@ static NBN_Connection *UDP_FindOrCreateClientConnectionByAddress(NBN_IPAddress a LogInfo("New UDP connection (id: %llu, addr: %d, port: %d)", conn->handle.id, address.host, address.port); - if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, conn) < 0) { - LogError("Failed to raise game server event"); - - return NULL; - } - + ServerDriver_OnClientConnected(conn); return conn; } @@ -2874,11 +2834,7 @@ int UDP_Server_RecvPackets(NBN_GameServer *server) { packet.sender = UDP_FindOrCreateClientConnectionByAddress(ip_address); - if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet) < 0) { - LogError("Failed to raise game server event"); - - return NBN_ERROR; - } + ServerDriver_OnClientPacketReceived(&packet); } return 0; @@ -2953,7 +2909,7 @@ int UDP_Client_RecvPackets(NBN_GameClient *client) { packet.sender = client->server_connection; - NBN_Driver_RaiseEvent(NBN_DRIVER_CLI_PACKET_RECEIVED, &packet); + ClientDriver_OnPacketReceived(&packet); } return 0; diff --git a/nbnet.h b/nbnet.h index 426712b..03f118b 100644 --- a/nbnet.h +++ b/nbnet.h @@ -229,7 +229,7 @@ int NBN_GameClient_Start(void); void NBN_GameClient_Stop(void); // TODO: doc -NBN_Reader *NBN_GameClient_GetServerDataReader(void); +NBN_Reader *NBN_GameClient_ReadServerData(void); /** * Poll game client events. @@ -437,7 +437,7 @@ int NBN_GameServer_RejectIncomingConnection(void); NBN_ConnectionHandle *NBN_GameServer_GetIncomingConnection(void); // TODO: doc -NBN_Reader *NBN_GameServer_GetConnectionRequestDataReader(void); +NBN_Reader *NBN_GameServer_ReadConnectionRequestData(void); /** * Return the information about the last disconnected client. @@ -509,22 +509,38 @@ typedef struct NBN_PacketSimulator { } NBN_PacketSimulator; #define NBN_GameClient_SetPing(v) \ - { nbn_game_client.endpoint.packet_simulator.ping = v; } + { \ + nbn_game_client.endpoint.packet_simulator.ping = v; \ + } #define NBN_GameClient_SetJitter(v) \ - { nbn_game_client.endpoint.packet_simulator.jitter = v; } + { \ + nbn_game_client.endpoint.packet_simulator.jitter = v; \ + } #define NBN_GameClient_SetPacketLoss(v) \ - { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } + { \ + nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; \ + } #define NBN_GameClient_SetPacketDuplication(v) \ - { nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; } + { \ + nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; \ + } #define NBN_GameServer_SetPing(v) \ - { nbn_game_server.endpoint.packet_simulator.ping = v; } + { \ + nbn_game_server.endpoint.packet_simulator.ping = v; \ + } #define NBN_GameServer_SetJitter(v) \ - { nbn_game_server.endpoint.packet_simulator.jitter = v; } + { \ + nbn_game_server.endpoint.packet_simulator.jitter = v; \ + } #define NBN_GameServer_SetPacketLoss(v) \ - { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } + { \ + nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; \ + } #define NBN_GameServer_SetPacketDuplication(v) \ - { nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; } + { \ + nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; \ + } #else From 6fb1b7a091e2f44be00a8ea3dcc47be81fc46e4b Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Fri, 6 Feb 2026 09:45:50 +0100 Subject: [PATCH 39/85] use strsep for ip address parsing --- nbnet.c | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/nbnet.c b/nbnet.c index cad1c34..7d66b87 100644 --- a/nbnet.c +++ b/nbnet.c @@ -23,9 +23,9 @@ */ // TODO: make functions that are not part of the public API static +// TODO: reintroduce webrtc drivers #include -#include "examples/echo/shared.h" #include "nbnet.h" #define STB_DS_IMPLEMENTATION @@ -2753,35 +2753,31 @@ static NBN_Connection *UDP_FindOrCreateClientConnectionByAddress(NBN_IPAddress a return conn; } -static int UDP_ParseIpAddress(const char *host, uint16_t port, NBN_IPAddress *address) { - // TODO: need to malloc here? - size_t host_len = strlen(host); - char *dup_host = (char *)malloc(host_len + 1); - memcpy(dup_host, host, host_len + 1); - uint8_t arr[4]; +#define MAX_IP_ADDR_LEN 15 - for (int i = 0; i < 4; i++) { - char *s; +static void UDP_ParseIpAddress(const char *host, uint16_t port, NBN_IPAddress *address) { + uint8_t arr[4]; + char *dup_host = strndup(host, MAX_IP_ADDR_LEN + 1); - // TODO: replace strtok with strsep - if ((s = strtok(i == 0 ? dup_host : NULL, ".")) == NULL) - return NBN_ERROR; + char *s; + int i = 0; + while ((s = strsep(&dup_host, ".")) != NULL && i < 4) { char *end = NULL; int v = strtol(s, &end, 10); - if (end == s || v < 0 || v > 255) - return NBN_ERROR; + if (*end != '\0' || v < 0 || v > 255) { + LogError("Invalid IP address: %s", host); + NBN_Abort(); + } - arr[i] = (uint8_t)v; + arr[i++] = (uint8_t)v; } - address->host = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3]; - address->port = port; - free(dup_host); - return 0; + address->host = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3]; + address->port = port; } static char *UDP_GetLastErrorMessage(void) { @@ -2863,11 +2859,7 @@ static int UDP_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, N int UDP_Client_Start(NBN_GameClient *client, const char *host, uint16_t port) { NBN_IPAddress *ip_address = &client->server_connection->driver_data.udp.ip_address; - if (UDP_ParseIpAddress(host, port, ip_address) < 0) { - LogError("Failed to resolve IP address from %s", host); - - return NBN_ERROR; - } + UDP_ParseIpAddress(host, port, ip_address); if (UDP_InitSocket() < 0) return NBN_ERROR; From 1db2281514671b9f39109505d3ad029fcefc3ac2 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Fri, 6 Feb 2026 15:07:56 +0100 Subject: [PATCH 40/85] reintroduce *CreateReliableMessage and *CreateUnreliableMessage functions --- nbnet.c | 20 ++++++++++++++++++-- nbnet.h | 49 +++++++++++++++++++++++-------------------------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/nbnet.c b/nbnet.c index 7d66b87..c084bc6 100644 --- a/nbnet.c +++ b/nbnet.c @@ -1666,7 +1666,7 @@ void NBN_GameClient_SetChannelMode(uint8_t channel_id, NBN_ChannelMode mode) { nbn_game_client.endpoint.channel_modes[channel_id] = mode; } -NBN_Writer *NBN_GameClient_GetConnectionRequestDataWriter(void) { +NBN_Writer *NBN_GameClient_WriteConnectionRequestData(void) { NBN_Writer_Init(&nbn_game_client.client_data_writer, nbn_game_client.endpoint.connection_request_data_buffer, sizeof(nbn_game_client.endpoint.connection_request_data_buffer)); @@ -1854,6 +1854,14 @@ NBN_Writer *NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id) { return writer; } +NBN_Writer *NBN_GameClient_CreateReliableMessage(uint8_t type) { + return NBN_GameClient_CreateMessage(type, NBN_RESERVED_RELIABLE_CHANNEL_ID); +} + +NBN_Writer *NBN_GameClient_CreateUnreliableMessage(uint8_t type) { + return NBN_GameClient_CreateMessage(type, NBN_RESERVED_UNRELIABLE_CHANNEL_ID); +} + int NBN_GameClient_EnqueueMessage(void) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; NBN_Message *message = &endpoint->write_message; @@ -2226,6 +2234,14 @@ NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id) { return writer; } +NBN_Writer *NBN_GameServer_CreateReliableMessage(uint8_t type) { + return NBN_GameServer_CreateMessage(type, NBN_RESERVED_RELIABLE_CHANNEL_ID); +} + +NBN_Writer *NBN_GameServer_CreateUnreliableMessage(uint8_t type) { + return NBN_GameServer_CreateMessage(type, NBN_RESERVED_UNRELIABLE_CHANNEL_ID); +} + int NBN_GameServer_EnqueueMessageFor(NBN_ConnectionHandle *conn) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; NBN_Message *message = &endpoint->write_message; @@ -2272,7 +2288,7 @@ NBN_Reader *NBN_GameServer_ReadMessage(void) { return reader; } -NBN_Writer *NBN_GameServer_GetConnectionDataWriter(void) { +NBN_Writer *NBN_GameServer_WriteConnectionData(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); diff --git a/nbnet.h b/nbnet.h index 03f118b..b418879 100644 --- a/nbnet.h +++ b/nbnet.h @@ -213,7 +213,7 @@ void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t p void NBN_GameClient_SetChannelMode(uint8_t channel_id, NBN_ChannelMode mode); // TODO: doc -NBN_Writer *NBN_GameClient_GetConnectionRequestDataWriter(void); +NBN_Writer *NBN_GameClient_WriteConnectionRequestData(void); /** * Start the game client. @@ -253,6 +253,12 @@ int NBN_GameClient_Flush(void); // TODO: doc NBN_Writer *NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id); +// TODO: doc +NBN_Writer *NBN_GameClient_CreateReliableMessage(uint8_t type); + +// TODO: doc +NBN_Writer *NBN_GameClient_CreateUnreliableMessage(uint8_t type); + // TODO: doc int NBN_GameClient_EnqueueMessage(void); @@ -392,6 +398,13 @@ int NBN_GameServer_CloseClientWithCode(NBN_ConnectionHandle *conn, int code); // TODO: doc NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id); + +// TODO: doc +NBN_Writer *NBN_GameServer_CreateReliableMessage(uint8_t type); + +// TODO: doc +NBN_Writer *NBN_GameServer_CreateUnreliableMessage(uint8_t type); + // TODO: doc int NBN_GameServer_EnqueueMessageFor(NBN_ConnectionHandle *conn); // TODO: doc @@ -401,7 +414,7 @@ int NBN_GameServer_EnqueueBroadcastMessage(void); NBN_Reader *NBN_GameServer_ReadMessage(void); // TODO: doc -NBN_Writer *NBN_GameServer_GetConnectionDataWriter(void); +NBN_Writer *NBN_GameServer_WriteConnectionData(void); // TODO: doc int NBN_GameServer_AcceptIncomingConnection(void); @@ -509,38 +522,22 @@ typedef struct NBN_PacketSimulator { } NBN_PacketSimulator; #define NBN_GameClient_SetPing(v) \ - { \ - nbn_game_client.endpoint.packet_simulator.ping = v; \ - } + { nbn_game_client.endpoint.packet_simulator.ping = v; } #define NBN_GameClient_SetJitter(v) \ - { \ - nbn_game_client.endpoint.packet_simulator.jitter = v; \ - } + { nbn_game_client.endpoint.packet_simulator.jitter = v; } #define NBN_GameClient_SetPacketLoss(v) \ - { \ - nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; \ - } + { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } #define NBN_GameClient_SetPacketDuplication(v) \ - { \ - nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; \ - } + { nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; } #define NBN_GameServer_SetPing(v) \ - { \ - nbn_game_server.endpoint.packet_simulator.ping = v; \ - } + { nbn_game_server.endpoint.packet_simulator.ping = v; } #define NBN_GameServer_SetJitter(v) \ - { \ - nbn_game_server.endpoint.packet_simulator.jitter = v; \ - } + { nbn_game_server.endpoint.packet_simulator.jitter = v; } #define NBN_GameServer_SetPacketLoss(v) \ - { \ - nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; \ - } + { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } #define NBN_GameServer_SetPacketDuplication(v) \ - { \ - nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; \ - } + { nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; } #else From 1ca4bffac6b9afccf9d690be4fd1229026b81c72 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Fri, 6 Feb 2026 15:08:13 +0100 Subject: [PATCH 41/85] update echo example --- examples/echo/CMakeLists.txt | 7 ++- examples/echo/client.c | 24 +++++---- examples/echo/server.c | 18 ++++--- examples/echo/shared.c | 16 +----- examples/echo/shared.h | 12 ----- examples/logging/logging.c | 98 ++++++++++++++++++++++++++++++++++++ examples/logging/logging.h | 53 +++++++++++++++++++ 7 files changed, 181 insertions(+), 47 deletions(-) create mode 100644 examples/logging/logging.c create mode 100644 examples/logging/logging.h diff --git a/examples/echo/CMakeLists.txt b/examples/echo/CMakeLists.txt index 3856f30..a83598a 100644 --- a/examples/echo/CMakeLists.txt +++ b/examples/echo/CMakeLists.txt @@ -23,8 +23,11 @@ if(CMAKE_COMPILER_IS_GNUCXX) add_compile_options(-Wextra -Wpedantic) endif (CMAKE_COMPILER_IS_GNUCXX) -add_executable(echo_client client.c shared.c ../../nbnet.c) -add_executable(echo_server server.c shared.c ../../nbnet.c) +add_executable(echo_client client.c shared.c ../logging/logging.c ../../nbnet.c) +add_executable(echo_server server.c shared.c ../logging/logging.c ../../nbnet.c) + +target_include_directories(echo_server PUBLIC ../logging) +target_include_directories(echo_client PUBLIC ../logging) if (UDP) # can't compile UDP driver with emscripten diff --git a/examples/echo/client.c b/examples/echo/client.c index 653d6f3..88eba2e 100644 --- a/examples/echo/client.c +++ b/examples/echo/client.c @@ -29,19 +29,20 @@ #define NBNET_IMPL #include "shared.h" +#include "logging.h" static bool running = true; static bool connected = false; static bool disconnected = false; void OnConnected(void) { - Log(LOG_INFO, "Connected"); + LogInfo("Connected"); connected = true; // Start sending messages } void OnDisconnected(void) { - Log(LOG_INFO, "Disconnected"); + LogInfo("Disconnected"); // Stop the main loop disconnected = true; @@ -49,7 +50,7 @@ void OnDisconnected(void) { // Retrieve the server code used when closing our client connection if (NBN_GameClient_GetServerCloseCode() == ECHO_SERVER_BUSY_CODE) { - Log(LOG_INFO, "Another client is already connected"); + LogInfo("Another client is already connected"); } } @@ -71,11 +72,11 @@ void OnMessageReceived(void) { assert(res == 0); msg_str[length] = 0; - Log(LOG_INFO, "Received echo: %s (length: %d, channel: %d)", msg_str, msg_info.length, msg_info.channel_id); + LogInfo("Received echo: %s (length: %d, channel: %d)", msg_str, msg_info.length, msg_info.channel_id); } int SendEcho(const char *msg) { - NBN_Writer *writer = NBN_GameClient_CreateMessage(ECHO_MESSAGE_TYPE, 0); + NBN_Writer *writer = NBN_GameClient_CreateReliableMessage(ECHO_MESSAGE_TYPE); unsigned int length = strlen(msg); NBN_Writer_WriteUInt32(writer, length); @@ -96,12 +97,15 @@ int main(int argc, char *argv[]) { #endif } + InitLogging(); + SetLogLevel(NBN_LOG_DEBUG); + const char *msg = argv[1]; // reserve 4 bytes to write the message length in the message (see the SendEcho function) unsigned int msg_max_len = ECHO_MESSAGE_MAX_LENGTH - 4; if (strlen(msg) > msg_max_len) { - Log(LOG_ERROR, "Message length cannot exceed %d. Exit", msg_max_len); + LogError("Message length cannot exceed %d. Exit", msg_max_len); // Error, quit the client application #ifdef __EMSCRIPTEN__ @@ -151,7 +155,7 @@ int main(int argc, char *argv[]) { NBN_GameClient_Init(ECHO_PROTOCOL_NAME, "127.0.0.1", ECHO_EXAMPLE_PORT); if (NBN_GameClient_Start() < 0) { - Log(LOG_ERROR, "Failed to start client"); + LogError("Failed to start client"); // Error, quit the client application #ifdef __EMSCRIPTEN__ @@ -170,7 +174,7 @@ int main(int argc, char *argv[]) { // Poll for client events while ((ev = NBN_GameClient_Poll()) != NBN_CLIENT_NO_EVENT) { if (ev < 0) { - Log(LOG_ERROR, "An error occured while polling client events. Exit"); + LogError("An error occured while polling client events. Exit"); // Stop main loop running = false; @@ -200,7 +204,7 @@ int main(int argc, char *argv[]) { if (connected) { if (SendEcho(msg) < 0) { - Log(LOG_ERROR, "Failed to send message. Exit"); + LogError("Failed to send message. Exit"); // Stop main loop running = false; @@ -210,7 +214,7 @@ int main(int argc, char *argv[]) { // Pack all enqueued messages as packets and send them if (NBN_GameClient_Flush() < 0) { - Log(LOG_ERROR, "Failed to send packets. Exit"); + LogError("Failed to send packets. Exit"); // Stop main loop running = false; diff --git a/examples/echo/server.c b/examples/echo/server.c index a61dc1d..3bbb46e 100644 --- a/examples/echo/server.c +++ b/examples/echo/server.c @@ -24,8 +24,8 @@ #include #include #include - #include "shared.h" +#include "logging.h" static NBN_ConnectionHandle *connection = NULL; static NBN_Connection_ID conn_id; @@ -51,11 +51,10 @@ static int EchoReceivedMessage(void) { assert(res == 0); msg_str[length] = 0; - Log(LOG_INFO, "Received message: %s, send echo (length: %d, channel: %d)", msg_str, msg_info.length, - msg_info.channel_id); + LogInfo("Received message: %s, send echo (length: %d, channel: %d)", msg_str, msg_info.length, msg_info.channel_id); // create and send an echo of the received message - NBN_Writer *writer = NBN_GameServer_CreateMessage(ECHO_MESSAGE_TYPE, 0); + NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(ECHO_MESSAGE_TYPE); NBN_Writer_WriteUInt32(writer, length); NBN_Writer_WriteBytes(writer, (uint8_t *)msg_str, length); @@ -66,6 +65,9 @@ static int EchoReceivedMessage(void) { static bool error = false; int main(int argc, const char **argv) { + InitLogging(); + SetLogLevel(NBN_LOG_DEBUG); + #ifdef __EMSCRIPTEN__ // Register the WebRTC driver @@ -113,7 +115,7 @@ int main(int argc, const char **argv) { NBN_GameServer_Init(ECHO_PROTOCOL_NAME, ECHO_EXAMPLE_PORT); if (NBN_GameServer_Start() < 0) { - Log(LOG_ERROR, "Failed to start the server"); + LogError("Failed to start the server"); // Error, quit the server application #ifdef __EMSCRIPTEN__ @@ -133,7 +135,7 @@ int main(int argc, const char **argv) { // Poll for server events while ((ev = NBN_GameServer_Poll()) != NBN_SERVER_NO_EVENT) { if (ev < 0) { - Log(LOG_ERROR, "Something went wrong"); + LogError("Something went wrong"); // Error, quit the server application error = true; @@ -165,7 +167,7 @@ int main(int argc, const char **argv) { // A message has been received from the client case NBN_SERVER_MESSAGE_RECEIVED: if (EchoReceivedMessage() < 0) { - Log(LOG_ERROR, "Failed to echo received message"); + LogError("Failed to echo received message"); // Error, quit the server application error = true; @@ -180,7 +182,7 @@ int main(int argc, const char **argv) { // Pack all enqueued messages as packets and send them if (NBN_GameServer_Flush() < 0) { - Log(LOG_ERROR, "Failed to send packets"); + LogError("Failed to send packets"); // Error, quit the server application error = true; diff --git a/examples/echo/shared.c b/examples/echo/shared.c index 0ec6d8f..634f2f9 100644 --- a/examples/echo/shared.c +++ b/examples/echo/shared.c @@ -38,6 +38,7 @@ #endif #include "shared.h" +#include "logging.h" // Sleep for a given amount of seconds // Used to limit client and server tick rate @@ -53,18 +54,3 @@ void EchoSleep(double sec) { nanosleep(&t, &t); #endif } - -static const char *log_type_strings[] = {"INFO", "ERROR", "DEBUG", "TRACE", "WARNING"}; - -// Basic logging function -void Log(int type, const char *fmt, ...) { - va_list args; - - va_start(args, fmt); - - printf("[%s] ", log_type_strings[type]); - vprintf(fmt, args); - printf("\n"); - - va_end(args); -} diff --git a/examples/echo/shared.h b/examples/echo/shared.h index b610b63..d2a26c0 100644 --- a/examples/echo/shared.h +++ b/examples/echo/shared.h @@ -32,18 +32,6 @@ // An arbitrary chosen code used when rejecting a client to let it know that another client is already connected #define ECHO_SERVER_BUSY_CODE 42 -// nbnet logging -// nbnet does not implement any logging capabilities, you need to provide your own -enum { LOG_INFO, LOG_ERROR, LOG_DEBUG, LOG_TRACE, LOG_WARNING }; - -#define NBN_LogInfo(...) Log(LOG_INFO, __VA_ARGS__) -#define NBN_LogError(...) Log(LOG_ERROR, __VA_ARGS__) -#define NBN_LogDebug(...) Log(LOG_DEBUG, __VA_ARGS__) -#define NBN_LogTrace(...) Log(LOG_TRACE, __VA_ARGS__) -#define NBN_LogWarning(...) Log(LOG_WARNING, __VA_ARGS__) - -void Log(int, const char *, ...); - #include "../../nbnet.h" void EchoSleep(double); diff --git a/examples/logging/logging.c b/examples/logging/logging.c new file mode 100644 index 0000000..a6e7a57 --- /dev/null +++ b/examples/logging/logging.c @@ -0,0 +1,98 @@ +/* + + Copyright (C) 2024 BIAGINI Nathan + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +#include "logging.h" + +/* I did not write this library: https://github.com/rxi/log.c */ + +/** + * Copyright (c) 2017 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `log.c` for details. + */ + +static struct { + void *udata; + FILE *fp; + int level; + int quiet; +} L; + +static const char *level_names[] = {"ERROR", "INFO", "WARNING", "DEBUG"}; + +#ifdef LOG_USE_COLOR +static const char *level_colors[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"}; +#endif + +void InitLogging(void) { + NBN_SetLogFunction(Log); + SetLogLevel(NBN_LOG_INFO); +} + +void SetLogLevel(NBN_LogLevel level) { + L.level = level; + NBN_SetLogLevel(level); +} + +void Log(NBN_LogLevel level, const char *file, int line, const char *fmt, ...) { + if (L.level < level) { + return; + } + + /* Get current time */ + time_t t = time(NULL); + struct tm *lt = localtime(&t); + + /* Log to stderr */ + if (!L.quiet) { + va_list args; + char buf[16]; + buf[strftime(buf, sizeof(buf), "%H:%M:%S", lt)] = '\0'; +#ifdef LOG_USE_COLOR + fprintf(stderr, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buf, level_colors[level], level_names[level], file, + line); +#else + fprintf(stderr, "%s %-5s %s:%d: ", buf, level_names[level], file, line); +#endif + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); + fflush(stderr); + } + + /* Log to file */ + if (L.fp) { + va_list args; + char buf[32]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; + fprintf(L.fp, "%s %-5s %s:%d: ", buf, level_names[level], file, line); + va_start(args, fmt); + vfprintf(L.fp, fmt, args); + va_end(args); + fprintf(L.fp, "\n"); + fflush(L.fp); + } +} diff --git a/examples/logging/logging.h b/examples/logging/logging.h new file mode 100644 index 0000000..c47b195 --- /dev/null +++ b/examples/logging/logging.h @@ -0,0 +1,53 @@ +/* + + Copyright (C) 2024 BIAGINI Nathan + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +#ifndef SOAK_LOGGING_H +#define SOAK_LOGGING_H + +/* I did not write this library: https://github.com/rxi/log.c */ + +/** + * Copyright (c) 2017 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `log.c` for details. + */ + +#include +#include +#include +#include "../../nbnet.h" + +#define LOG_VERSION "0.1.0" + +#define LogInfo(msg, ...) Log(NBN_LOG_INFO, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define LogWarning(msg, ...) Log(NBN_LOG_WARNING, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define LogError(msg, ...) Log(NBN_LOG_ERROR, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define LogDebug(msg, ...) Log(NBN_LOG_DEBUG, __FILE__, __LINE__, msg, ##__VA_ARGS__) + +void InitLogging(void); +void Log(NBN_LogLevel level, const char *file, int line, const char *fmt, ...); +void SetLogLevel(NBN_LogLevel level); + +#endif /* SOAK_LOGGING_H */ From 0fa1cf9c19d5d20f3681809faa6d41504f5d6c68 Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sat, 7 Feb 2026 08:01:21 +0100 Subject: [PATCH 42/85] rework logging --- examples/echo/CMakeLists.txt | 4 +- examples/echo/client.c | 23 +++-- examples/echo/server.c | 17 ++-- examples/echo/shared.c | 1 - examples/logging/log.c | 168 +++++++++++++++++++++++++++++++++++ examples/logging/log.h | 49 ++++++++++ examples/logging/logging.c | 98 -------------------- examples/logging/logging.h | 53 ----------- nbnet.c | 76 +++++++++++----- nbnet.h | 34 ++++--- 10 files changed, 315 insertions(+), 208 deletions(-) create mode 100644 examples/logging/log.c create mode 100644 examples/logging/log.h delete mode 100644 examples/logging/logging.c delete mode 100644 examples/logging/logging.h diff --git a/examples/echo/CMakeLists.txt b/examples/echo/CMakeLists.txt index a83598a..b8bcc6e 100644 --- a/examples/echo/CMakeLists.txt +++ b/examples/echo/CMakeLists.txt @@ -23,8 +23,8 @@ if(CMAKE_COMPILER_IS_GNUCXX) add_compile_options(-Wextra -Wpedantic) endif (CMAKE_COMPILER_IS_GNUCXX) -add_executable(echo_client client.c shared.c ../logging/logging.c ../../nbnet.c) -add_executable(echo_server server.c shared.c ../logging/logging.c ../../nbnet.c) +add_executable(echo_client client.c shared.c ../logging/log.c ../../nbnet.c) +add_executable(echo_server server.c shared.c ../logging/log.c ../../nbnet.c) target_include_directories(echo_server PUBLIC ../logging) target_include_directories(echo_client PUBLIC ../logging) diff --git a/examples/echo/client.c b/examples/echo/client.c index 88eba2e..e946571 100644 --- a/examples/echo/client.c +++ b/examples/echo/client.c @@ -29,20 +29,20 @@ #define NBNET_IMPL #include "shared.h" -#include "logging.h" +#include "log.h" static bool running = true; static bool connected = false; static bool disconnected = false; void OnConnected(void) { - LogInfo("Connected"); + log_info("Connected"); connected = true; // Start sending messages } void OnDisconnected(void) { - LogInfo("Disconnected"); + log_info("Disconnected"); // Stop the main loop disconnected = true; @@ -50,7 +50,7 @@ void OnDisconnected(void) { // Retrieve the server code used when closing our client connection if (NBN_GameClient_GetServerCloseCode() == ECHO_SERVER_BUSY_CODE) { - LogInfo("Another client is already connected"); + log_info("Another client is already connected"); } } @@ -72,7 +72,7 @@ void OnMessageReceived(void) { assert(res == 0); msg_str[length] = 0; - LogInfo("Received echo: %s (length: %d, channel: %d)", msg_str, msg_info.length, msg_info.channel_id); + log_info("Received echo: %s (length: %d, channel: %d)", msg_str, msg_info.length, msg_info.channel_id); } int SendEcho(const char *msg) { @@ -97,15 +97,14 @@ int main(int argc, char *argv[]) { #endif } - InitLogging(); - SetLogLevel(NBN_LOG_DEBUG); + NBN_SetLogLevel(NBN_LOG_INFO); const char *msg = argv[1]; // reserve 4 bytes to write the message length in the message (see the SendEcho function) unsigned int msg_max_len = ECHO_MESSAGE_MAX_LENGTH - 4; if (strlen(msg) > msg_max_len) { - LogError("Message length cannot exceed %d. Exit", msg_max_len); + log_error("Message length cannot exceed %d. Exit", msg_max_len); // Error, quit the client application #ifdef __EMSCRIPTEN__ @@ -155,7 +154,7 @@ int main(int argc, char *argv[]) { NBN_GameClient_Init(ECHO_PROTOCOL_NAME, "127.0.0.1", ECHO_EXAMPLE_PORT); if (NBN_GameClient_Start() < 0) { - LogError("Failed to start client"); + log_error("Failed to start client"); // Error, quit the client application #ifdef __EMSCRIPTEN__ @@ -174,7 +173,7 @@ int main(int argc, char *argv[]) { // Poll for client events while ((ev = NBN_GameClient_Poll()) != NBN_CLIENT_NO_EVENT) { if (ev < 0) { - LogError("An error occured while polling client events. Exit"); + log_error("An error occured while polling client events. Exit"); // Stop main loop running = false; @@ -204,7 +203,7 @@ int main(int argc, char *argv[]) { if (connected) { if (SendEcho(msg) < 0) { - LogError("Failed to send message. Exit"); + log_error("Failed to send message. Exit"); // Stop main loop running = false; @@ -214,7 +213,7 @@ int main(int argc, char *argv[]) { // Pack all enqueued messages as packets and send them if (NBN_GameClient_Flush() < 0) { - LogError("Failed to send packets. Exit"); + log_error("Failed to send packets. Exit"); // Stop main loop running = false; diff --git a/examples/echo/server.c b/examples/echo/server.c index 3bbb46e..b5cb920 100644 --- a/examples/echo/server.c +++ b/examples/echo/server.c @@ -22,10 +22,9 @@ #include #include -#include #include #include "shared.h" -#include "logging.h" +#include "log.h" static NBN_ConnectionHandle *connection = NULL; static NBN_Connection_ID conn_id; @@ -51,7 +50,8 @@ static int EchoReceivedMessage(void) { assert(res == 0); msg_str[length] = 0; - LogInfo("Received message: %s, send echo (length: %d, channel: %d)", msg_str, msg_info.length, msg_info.channel_id); + log_info("Received message: %s, send echo (length: %d, channel: %d)", msg_str, msg_info.length, + msg_info.channel_id); // create and send an echo of the received message NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(ECHO_MESSAGE_TYPE); @@ -65,8 +65,7 @@ static int EchoReceivedMessage(void) { static bool error = false; int main(int argc, const char **argv) { - InitLogging(); - SetLogLevel(NBN_LOG_DEBUG); + NBN_SetLogLevel(NBN_LOG_INFO); #ifdef __EMSCRIPTEN__ @@ -115,7 +114,7 @@ int main(int argc, const char **argv) { NBN_GameServer_Init(ECHO_PROTOCOL_NAME, ECHO_EXAMPLE_PORT); if (NBN_GameServer_Start() < 0) { - LogError("Failed to start the server"); + log_error("Failed to start the server"); // Error, quit the server application #ifdef __EMSCRIPTEN__ @@ -135,7 +134,7 @@ int main(int argc, const char **argv) { // Poll for server events while ((ev = NBN_GameServer_Poll()) != NBN_SERVER_NO_EVENT) { if (ev < 0) { - LogError("Something went wrong"); + log_error("Something went wrong"); // Error, quit the server application error = true; @@ -167,7 +166,7 @@ int main(int argc, const char **argv) { // A message has been received from the client case NBN_SERVER_MESSAGE_RECEIVED: if (EchoReceivedMessage() < 0) { - LogError("Failed to echo received message"); + log_error("Failed to echo received message"); // Error, quit the server application error = true; @@ -182,7 +181,7 @@ int main(int argc, const char **argv) { // Pack all enqueued messages as packets and send them if (NBN_GameServer_Flush() < 0) { - LogError("Failed to send packets"); + log_error("Failed to send packets"); // Error, quit the server application error = true; diff --git a/examples/echo/shared.c b/examples/echo/shared.c index 634f2f9..275dd9e 100644 --- a/examples/echo/shared.c +++ b/examples/echo/shared.c @@ -38,7 +38,6 @@ #endif #include "shared.h" -#include "logging.h" // Sleep for a given amount of seconds // Used to limit client and server tick rate diff --git a/examples/logging/log.c b/examples/logging/log.c new file mode 100644 index 0000000..1a7626e --- /dev/null +++ b/examples/logging/log.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2020 rxi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "log.h" + +#define MAX_CALLBACKS 32 + +typedef struct { + log_LogFn fn; + void *udata; + int level; +} Callback; + +static struct { + void *udata; + log_LockFn lock; + int level; + bool quiet; + Callback callbacks[MAX_CALLBACKS]; +} L; + + +static const char *level_strings[] = { + "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" +}; + +#ifdef LOG_USE_COLOR +static const char *level_colors[] = { + "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" +}; +#endif + + +static void stdout_callback(log_Event *ev) { + char buf[16]; + buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; +#ifdef LOG_USE_COLOR + fprintf( + ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", + buf, level_colors[ev->level], level_strings[ev->level], + ev->file, ev->line); +#else + fprintf( + ev->udata, "%s %-5s %s:%d: ", + buf, level_strings[ev->level], ev->file, ev->line); +#endif + vfprintf(ev->udata, ev->fmt, ev->ap); + fprintf(ev->udata, "\n"); + fflush(ev->udata); +} + + +static void file_callback(log_Event *ev) { + char buf[64]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; + fprintf( + ev->udata, "%s %-5s %s:%d: ", + buf, level_strings[ev->level], ev->file, ev->line); + vfprintf(ev->udata, ev->fmt, ev->ap); + fprintf(ev->udata, "\n"); + fflush(ev->udata); +} + + +static void lock(void) { + if (L.lock) { L.lock(true, L.udata); } +} + + +static void unlock(void) { + if (L.lock) { L.lock(false, L.udata); } +} + + +const char* log_level_string(int level) { + return level_strings[level]; +} + + +void log_set_lock(log_LockFn fn, void *udata) { + L.lock = fn; + L.udata = udata; +} + + +void log_set_level(int level) { + L.level = level; +} + + +void log_set_quiet(bool enable) { + L.quiet = enable; +} + + +int log_add_callback(log_LogFn fn, void *udata, int level) { + for (int i = 0; i < MAX_CALLBACKS; i++) { + if (!L.callbacks[i].fn) { + L.callbacks[i] = (Callback) { fn, udata, level }; + return 0; + } + } + return -1; +} + + +int log_add_fp(FILE *fp, int level) { + return log_add_callback(file_callback, fp, level); +} + + +static void init_event(log_Event *ev, void *udata) { + if (!ev->time) { + time_t t = time(NULL); + ev->time = localtime(&t); + } + ev->udata = udata; +} + + +void log_log(int level, const char *file, int line, const char *fmt, ...) { + log_Event ev = { + .fmt = fmt, + .file = file, + .line = line, + .level = level, + }; + + lock(); + + if (!L.quiet && level >= L.level) { + init_event(&ev, stderr); + va_start(ev.ap, fmt); + stdout_callback(&ev); + va_end(ev.ap); + } + + for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { + Callback *cb = &L.callbacks[i]; + if (level >= cb->level) { + init_event(&ev, cb->udata); + va_start(ev.ap, fmt); + cb->fn(&ev); + va_end(ev.ap); + } + } + + unlock(); +} diff --git a/examples/logging/log.h b/examples/logging/log.h new file mode 100644 index 0000000..b1fae24 --- /dev/null +++ b/examples/logging/log.h @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2020 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `log.c` for details. + */ + +#ifndef LOG_H +#define LOG_H + +#include +#include +#include +#include + +#define LOG_VERSION "0.1.0" + +typedef struct { + va_list ap; + const char *fmt; + const char *file; + struct tm *time; + void *udata; + int line; + int level; +} log_Event; + +typedef void (*log_LogFn)(log_Event *ev); +typedef void (*log_LockFn)(bool lock, void *udata); + +enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; + +#define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) +#define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) +#define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) +#define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) +#define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) +#define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) + +const char* log_level_string(int level); +void log_set_lock(log_LockFn fn, void *udata); +void log_set_level(int level); +void log_set_quiet(bool enable); +int log_add_callback(log_LogFn fn, void *udata, int level); +int log_add_fp(FILE *fp, int level); + +void log_log(int level, const char *file, int line, const char *fmt, ...); + +#endif diff --git a/examples/logging/logging.c b/examples/logging/logging.c deleted file mode 100644 index a6e7a57..0000000 --- a/examples/logging/logging.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#include "logging.h" - -/* I did not write this library: https://github.com/rxi/log.c */ - -/** - * Copyright (c) 2017 rxi - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See `log.c` for details. - */ - -static struct { - void *udata; - FILE *fp; - int level; - int quiet; -} L; - -static const char *level_names[] = {"ERROR", "INFO", "WARNING", "DEBUG"}; - -#ifdef LOG_USE_COLOR -static const char *level_colors[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"}; -#endif - -void InitLogging(void) { - NBN_SetLogFunction(Log); - SetLogLevel(NBN_LOG_INFO); -} - -void SetLogLevel(NBN_LogLevel level) { - L.level = level; - NBN_SetLogLevel(level); -} - -void Log(NBN_LogLevel level, const char *file, int line, const char *fmt, ...) { - if (L.level < level) { - return; - } - - /* Get current time */ - time_t t = time(NULL); - struct tm *lt = localtime(&t); - - /* Log to stderr */ - if (!L.quiet) { - va_list args; - char buf[16]; - buf[strftime(buf, sizeof(buf), "%H:%M:%S", lt)] = '\0'; -#ifdef LOG_USE_COLOR - fprintf(stderr, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buf, level_colors[level], level_names[level], file, - line); -#else - fprintf(stderr, "%s %-5s %s:%d: ", buf, level_names[level], file, line); -#endif - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - fprintf(stderr, "\n"); - fflush(stderr); - } - - /* Log to file */ - if (L.fp) { - va_list args; - char buf[32]; - buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; - fprintf(L.fp, "%s %-5s %s:%d: ", buf, level_names[level], file, line); - va_start(args, fmt); - vfprintf(L.fp, fmt, args); - va_end(args); - fprintf(L.fp, "\n"); - fflush(L.fp); - } -} diff --git a/examples/logging/logging.h b/examples/logging/logging.h deleted file mode 100644 index c47b195..0000000 --- a/examples/logging/logging.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#ifndef SOAK_LOGGING_H -#define SOAK_LOGGING_H - -/* I did not write this library: https://github.com/rxi/log.c */ - -/** - * Copyright (c) 2017 rxi - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See `log.c` for details. - */ - -#include -#include -#include -#include "../../nbnet.h" - -#define LOG_VERSION "0.1.0" - -#define LogInfo(msg, ...) Log(NBN_LOG_INFO, __FILE__, __LINE__, msg, ##__VA_ARGS__) -#define LogWarning(msg, ...) Log(NBN_LOG_WARNING, __FILE__, __LINE__, msg, ##__VA_ARGS__) -#define LogError(msg, ...) Log(NBN_LOG_ERROR, __FILE__, __LINE__, msg, ##__VA_ARGS__) -#define LogDebug(msg, ...) Log(NBN_LOG_DEBUG, __FILE__, __LINE__, msg, ##__VA_ARGS__) - -void InitLogging(void); -void Log(NBN_LogLevel level, const char *file, int line, const char *fmt, ...); -void SetLogLevel(NBN_LogLevel level); - -#endif /* SOAK_LOGGING_H */ diff --git a/nbnet.c b/nbnet.c index c084bc6..b39e927 100644 --- a/nbnet.c +++ b/nbnet.c @@ -83,29 +83,6 @@ typedef struct NBN_Driver NBN_Driver; typedef enum NBN_Driver_ID NBN_Driver_ID; -static NBN_LogFunc nbn_log_func = NULL; - -#ifdef NBN_DEBUG -static NBN_LogLevel nbn_log_level = NBN_LOG_DEBUG; -#else -static NBN_LogLevel nbn_log_level = NBN_LOG_INFO; -#endif // NBN_DEBUG - -#define Log(level, filename, line, msg, ...) \ - do { \ - if (nbn_log_func != NULL && nbn_log_level >= level) \ - nbn_log_func(level, filename, line, msg, ##__VA_ARGS__); \ - } while (0); - -#define LogInfo(msg, ...) Log(NBN_LOG_INFO, __FILE__, __LINE__, msg, ##__VA_ARGS__) -#define LogWarning(msg, ...) Log(NBN_LOG_WARNING, __FILE__, __LINE__, msg, ##__VA_ARGS__) -#define LogError(msg, ...) Log(NBN_LOG_ERROR, __FILE__, __LINE__, msg, ##__VA_ARGS__) -#define LogDebug(msg, ...) Log(NBN_LOG_DEBUG, __FILE__, __LINE__, msg, ##__VA_ARGS__) - -void NBN_SetLogFunction(NBN_LogFunc func) { nbn_log_func = func; } - -void NBN_SetLogLevel(NBN_LogLevel level) { nbn_log_level = level; } - #define NBN_Abort abort #define NBN_Assert(cond) assert(cond) @@ -164,6 +141,59 @@ void NBN_SetLogLevel(NBN_LogLevel level) { nbn_log_level = level; } #define NBN_RESERVED_UNRELIABLE_CHANNEL_ID 0 #define NBN_RESERVED_RELIABLE_CHANNEL_ID 1 +static int log_level = NBN_LOG_INFO; + +void NBN_SetLogLevel(NBN_LogLevel level) { log_level = level; } + +#ifdef NBN_LOG_CUSTOM_FUNCTION + +extern void Log(NBN_LogLevel level, const char *filename, int line, const char *msg, ...); + +#else + +/** + * Default logging function + */ + +/** + * Copyright (c) 2017 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `log.c` for details. + */ + +#include +#include + +static const char *level_names[] = {"ERROR", "INFO", "WARNING", "DEBUG"}; + +static void Log(NBN_LogLevel level, const char *filename, int line, const char *msg, ...) { + if (log_level < level) { + return; + } + + time_t t = time(NULL); + struct tm *lt = localtime(&t); + FILE *fp = level == NBN_LOG_ERROR ? stderr : stdout; + + va_list args; + char buf[32]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; + fprintf(fp, "%s %-5s %s:%d: ", buf, level_names[level], filename, line); + va_start(args, msg); + vfprintf(fp, msg, args); + va_end(args); + fprintf(fp, "\n"); + fflush(fp); +} + +#endif // NBN_CUSTOM_LOG_FUNCTION + +#define LogInfo(msg, ...) Log(NBN_LOG_INFO, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define LogWarning(msg, ...) Log(NBN_LOG_WARNING, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define LogError(msg, ...) Log(NBN_LOG_ERROR, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define LogDebug(msg, ...) Log(NBN_LOG_DEBUG, __FILE__, __LINE__, msg, ##__VA_ARGS__) + typedef enum NBN_PacketResult { NBN_PACKET_WRITE_ERROR = -1, NBN_PACKET_WRITE_OK, diff --git a/nbnet.h b/nbnet.h index b418879..2520d95 100644 --- a/nbnet.h +++ b/nbnet.h @@ -76,9 +76,7 @@ /* ========================================================================== */ typedef enum NBN_LogLevel { NBN_LOG_ERROR, NBN_LOG_INFO, NBN_LOG_WARNING, NBN_LOG_DEBUG } NBN_LogLevel; -typedef void (*NBN_LogFunc)(NBN_LogLevel level, const char *filename, int line, const char *msg, ...); -void NBN_SetLogFunction(NBN_LogFunc); void NBN_SetLogLevel(NBN_LogLevel); /** @@ -522,22 +520,38 @@ typedef struct NBN_PacketSimulator { } NBN_PacketSimulator; #define NBN_GameClient_SetPing(v) \ - { nbn_game_client.endpoint.packet_simulator.ping = v; } + { \ + nbn_game_client.endpoint.packet_simulator.ping = v; \ + } #define NBN_GameClient_SetJitter(v) \ - { nbn_game_client.endpoint.packet_simulator.jitter = v; } + { \ + nbn_game_client.endpoint.packet_simulator.jitter = v; \ + } #define NBN_GameClient_SetPacketLoss(v) \ - { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } + { \ + nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; \ + } #define NBN_GameClient_SetPacketDuplication(v) \ - { nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; } + { \ + nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; \ + } #define NBN_GameServer_SetPing(v) \ - { nbn_game_server.endpoint.packet_simulator.ping = v; } + { \ + nbn_game_server.endpoint.packet_simulator.ping = v; \ + } #define NBN_GameServer_SetJitter(v) \ - { nbn_game_server.endpoint.packet_simulator.jitter = v; } + { \ + nbn_game_server.endpoint.packet_simulator.jitter = v; \ + } #define NBN_GameServer_SetPacketLoss(v) \ - { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } + { \ + nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; \ + } #define NBN_GameServer_SetPacketDuplication(v) \ - { nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; } + { \ + nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; \ + } #else From 50b3aead80db967b81c3beabe0db86498ce807d8 Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sat, 7 Feb 2026 09:06:23 +0100 Subject: [PATCH 43/85] fix issue with connection user data --- nbnet.c | 14 +++++++------- nbnet.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/nbnet.c b/nbnet.c index b39e927..d3a3590 100644 --- a/nbnet.c +++ b/nbnet.c @@ -302,7 +302,6 @@ struct NBN_Connection { NBN_Driver *driver; /* Network driver used for that connection */ NBN_Channel channels[NBN_CHANNEL_COUNT]; /* Message channels (sending & receiving) */ NBN_ConnectionStats stats; - void *user_data; /* Pointer to user-defined data */ /* Driver-related data attached to the connection */ union { @@ -563,7 +562,7 @@ void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int leng void NBN_Writer_WriteString(NBN_Writer *writer, const char *str, unsigned int max_len) { unsigned int len = strnlen(str, max_len); - NBN_Writer_WriteUInt32(writer, max_len); + NBN_Writer_WriteUInt32(writer, len); NBN_Writer_WriteBytes(writer, (uint8_t *)str, len); } @@ -2389,13 +2388,13 @@ NBN_Reader *NBN_GameServer_ReadConnectionRequestData(void) { } NBN_DisconnectionInfo NBN_GameServer_GetDisconnectionInfo(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_DISCONNECTED); + NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_DISCONNECTION); return nbn_game_server.last_event.data.disconnection; } NBN_MessageInfo NBN_GameServer_GetMessageInfo(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); + NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_MESSAGE_RECEIVED); return nbn_game_server.last_event.data.message_info; } @@ -2437,7 +2436,7 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool NBN_Event e; e.type = NBN_CLIENT_DISCONNECTED; - e.data.disconnection = (NBN_DisconnectionInfo){client->handle.id, client->user_data}; + e.data.disconnection = (NBN_DisconnectionInfo){client->handle.id, client->handle.user_data}; if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, e)) return NBN_ERROR; @@ -2606,7 +2605,8 @@ static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev) { } if (message_info.type == NBN_DISCONNECTION_MESSAGE_TYPE) { - LogInfo("Received a disconnection request from client %d", sender->handle.id); + LogInfo("Received a disconnection request from client %d (user_data: %p)", sender->handle.id, + sender->handle.user_data); if (GameServer_CloseClientWithCode(sender, -1, true) < 0) { *ev = NBN_ERROR; @@ -2616,7 +2616,7 @@ static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev) { sender->is_stale = true; last_event->type = NBN_SERVER_DISCONNECTION; - last_event->data.disconnection = (NBN_DisconnectionInfo){sender->handle.id, sender->user_data}; + last_event->data.disconnection = (NBN_DisconnectionInfo){sender->handle.id, sender->handle.user_data}; GameServer_RemoveClosedClientConnections(); diff --git a/nbnet.h b/nbnet.h index 2520d95..8822071 100644 --- a/nbnet.h +++ b/nbnet.h @@ -318,7 +318,7 @@ typedef struct NBN_GameServerStats { typedef struct NBN_DisconnectionInfo { NBN_Connection_ID conn_id; /* ID if the disconnected connection */ - void *user_data; /* User-defined data associated with this connection */ + void *user_data; /* Pointer to user-defined data associated with this connection */ } NBN_DisconnectionInfo; /** From bb2f43fe3af6065f9177a26538428bc0b30f09b9 Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sat, 7 Feb 2026 09:06:33 +0100 Subject: [PATCH 44/85] update raylib example --- examples/raylib/CMakeLists.txt | 24 ++++-- examples/raylib/client.c | 62 +++++++++------ examples/raylib/server.c | 62 +++++++-------- examples/raylib/shared.c | 139 +++++++++++++-------------------- examples/raylib/shared.h | 7 +- 5 files changed, 142 insertions(+), 152 deletions(-) diff --git a/examples/raylib/CMakeLists.txt b/examples/raylib/CMakeLists.txt index d131037..58a6a51 100644 --- a/examples/raylib/CMakeLists.txt +++ b/examples/raylib/CMakeLists.txt @@ -4,10 +4,10 @@ project(raylib_example C) set(raylib_DIR cmake) -set(CLIENT_SOURCES client.c shared.c) -set(SERVER_SOURCES server.c shared.c) +set(CLIENT_SOURCES client.c shared.c ../../nbnet.c) +set(SERVER_SOURCES server.c shared.c ../../nbnet.c) -add_compile_options(-Wall -Wextra -Wpedantic -Wno-unknown-pragmas -std=c99) +add_compile_options(-Wall -Wextra -std=c99 -Wno-gnu-zero-variadic-macro-arguments) add_executable(raylib_client ${CLIENT_SOURCES}) add_executable(raylib_server ${SERVER_SOURCES}) @@ -31,8 +31,20 @@ target_compile_definitions(raylib_client PUBLIC NBN_DEBUG) target_compile_definitions(raylib_server PUBLIC NBN_DEBUG) target_compile_definitions(raylib_server PUBLIC NBN_RAYLIB_SERVER) +if (UDP) + # can't compile UDP driver with emscripten + if (EMSCRIPTEN) + message(SEND_ERROR "Can't compile UDP driver with emscripten") + endif (EMSCRIPTEN) + + message("Compiling with UDP driver") + + target_compile_definitions(raylib_server PUBLIC NBN_UDP) + target_compile_definitions(raylib_client PUBLIC NBN_UDP) +endif (UDP) + # compile with C WebRTC driver -if (WEBRTC_C_DRIVER) +if (WEBRTC_NATIVE) # can't compile WebRTC native driver with emscripten if (EMSCRIPTEN) message(SEND_ERROR "Can't compile WebRTC native driver with emscripten") @@ -51,9 +63,9 @@ if (WEBRTC_C_DRIVER) target_compile_definitions(raylib_server PUBLIC NBN_HTTPS_KEY_PEM="localhost.key") target_compile_definitions(raylib_server PUBLIC NBN_HTTPS_CERT_PEM="localhost.crt") endif (USE_HTTPS) -endif (WEBRTC_C_DRIVER) +endif (WEBRTC_NATIVE) -unset(WEBRTC_C_DRIVER) +unset(WEBRTC_NATIVE) # Use HTTPS (for WebRTC drivers) if (USE_HTTPS) diff --git a/examples/raylib/client.c b/examples/raylib/client.c index c86fb97..b3fa6e1 100644 --- a/examples/raylib/client.c +++ b/examples/raylib/client.c @@ -21,6 +21,9 @@ #include #include +#include +#include +#include #ifdef __EMSCRIPTEN__ #include @@ -60,8 +63,9 @@ Color client_colors_to_raylib_colors[] = { }; static void WriteConnectionRequestData(const char *name) { - NBN_Writer *writer = NBN_GameClient_GetConnectionRequestDataWriter(); + NBN_Writer *writer = NBN_GameClient_WriteConnectionRequestData(); + // send the name of the client as the connection request data NBN_Writer_WriteString(writer, name, CLIENT_NAME_MAX_LEN); } @@ -80,7 +84,7 @@ static int HandleConnection(void) { TraceLog(LOG_INFO, "Connected, reading connection data..."); uint32_t x, y, client_id; - NBN_Reader *reader = NBN_GameClient_GetServerDataReader(); + NBN_Reader *reader = NBN_GameClient_ReadServerData(); if (NBN_Reader_ReadUInt32(reader, &x) < 0) { return -1; @@ -216,9 +220,12 @@ static void HandleGameStateMessage(void) { static GameState recv_game_state; // Read the game state from the received GAME_STATE_MESSAGE message - NBN_Reader *reader = NBN_GameClient_GetMessageReader(); + NBN_Reader *reader = NBN_GameClient_ReadMessage(); - GameStateMessage_Read(reader, &recv_game_state); + if (GameStateMessage_Read(reader, &recv_game_state) < 0) { + TraceLog(LOG_ERROR, "Failed to read game state"); + abort(); + } // Loop over the received client states and update the clients for (unsigned int i = 0; i < recv_game_state.client_count; i++) { @@ -257,7 +264,7 @@ static void HandleReceivedMessage(void) { static void HandleGameClientEvent(int ev) { switch (ev) { - case NBN_CONNECTED: + case NBN_CLIENT_CONNECTED: // We are connected to the server if (HandleConnection() < 0) { TraceLog(LOG_ERROR, "Failed to handle connection"); @@ -265,12 +272,12 @@ static void HandleGameClientEvent(int ev) { } break; - case NBN_DISCONNECTED: + case NBN_CLIENT_DISCONNECTED: // The server has closed our connection HandleDisconnection(); break; - case NBN_MESSAGE_RECEIVED: + case NBN_CLIENT_MESSAGE_RECEIVED: // We received a message from the server HandleReceivedMessage(); break; @@ -315,14 +322,14 @@ static int Update(void) { // Movement code if (IsKeyDown(KEY_UP)) - local_client_state.y = MAX(0, local_client_state.y - 5); + local_client_state.y = (int)fmax(0, local_client_state.y - 5); else if (IsKeyDown(KEY_DOWN)) - local_client_state.y = MIN(GAME_HEIGHT - 50, local_client_state.y + 5); + local_client_state.y = (int)fmin(GAME_HEIGHT - 50, local_client_state.y + 5); if (IsKeyDown(KEY_LEFT)) - local_client_state.x = MAX(0, local_client_state.x - 5); + local_client_state.x = (int)fmax(0, local_client_state.x - 5); else if (IsKeyDown(KEY_RIGHT)) - local_client_state.x = MIN(GAME_WIDTH - 50, local_client_state.x + 5); + local_client_state.x = (int)fmin(GAME_WIDTH - 50, local_client_state.x + 5); // Color switching if (IsKeyDown(KEY_SPACE) && !color_key_pressed) { @@ -343,10 +350,10 @@ static int Update(void) { // Increasing/Decreasing floating point value if (IsKeyDown(KEY_K)) - local_client_state.val = MIN(MAX_FLOAT_VAL, local_client_state.val + 0.005); + local_client_state.val = fmin(MAX_FLOAT_VAL, local_client_state.val + 0.005); if (IsKeyDown(KEY_J)) - local_client_state.val = MAX(MIN_FLOAT_VAL, local_client_state.val - 0.005); + local_client_state.val = fmax(MIN_FLOAT_VAL, local_client_state.val - 0.005); // Send the latest local client state to the server if (SendStateUpdate() < 0) { @@ -360,11 +367,13 @@ static int Update(void) { void DrawClient(ClientState *state, bool is_local) { Color color = client_colors_to_raylib_colors[state->color]; - const char *text = TextFormat("%.3f", state->val); + const char *val_text = TextFormat("%.3f", state->val); int font_size = 20; - int text_width = MeasureText(text, font_size); + int name_text_width = MeasureText(state->name, font_size); + int val_text_width = MeasureText(val_text, font_size); - DrawText(text, (state->x + 25) - text_width / 2, state->y - 20, font_size, color); + DrawText(state->name, (state->x + 25) - name_text_width / 2, state->y - 20, font_size, color); + DrawText(val_text, (state->x + 25) - val_text_width / 2, state->y + 70, font_size, color); DrawRectangle(state->x, state->y, 50, 50, color); if (is_local) @@ -431,7 +440,7 @@ void UpdateAndDraw(void) { while (acc >= tick_dt) { int ev; - while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) { + while ((ev = NBN_GameClient_Poll()) != NBN_CLIENT_NO_EVENT) { if (ev < 0) { TraceLog(LOG_WARNING, "An occured while polling network events. Exit"); @@ -470,11 +479,17 @@ int main(int argc, char *argv[]) { return 1; } -#else - (void)argc; - (void)argv; #endif + if (argc < 2) { + printf("Usage: raylib_client NAME\n"); + return 1; + } + + const char *name = argv[1]; + + memcpy(local_client_state.name, name, sizeof(local_client_state.name)); + SetTraceLogLevel(LOG_TRACE); InitWindow(GAME_WIDTH, GAME_HEIGHT, "raylib client"); @@ -483,16 +498,11 @@ int main(int argc, char *argv[]) { SetTargetFPS(TARGET_FPS); #endif -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register(); // Register the WebRTC driver -#else -#endif // __EMSCRIPTEN__ - // Initialize the client with a protocol name, the server host and the server port // protocol name has to be the same as the one used by the server NBN_GameClient_Init(RAYLIB_EXAMPLE_PROTOCOL_NAME, "127.0.0.1", RAYLIB_EXAMPLE_PORT); - // WriteConnectionRequestData("FOO"); + WriteConnectionRequestData(name); // Start the client with the configuration if (NBN_GameClient_Start() < 0) { diff --git a/examples/raylib/server.c b/examples/raylib/server.c index 2981f4c..668eb52 100644 --- a/examples/raylib/server.c +++ b/examples/raylib/server.c @@ -22,7 +22,9 @@ #include #include +#include #include +#include // For Sleep function #if defined(__EMSCRIPTEN__) @@ -40,7 +42,7 @@ // A simple structure to represent connected clients typedef struct { // Underlying nbnet connection, used to send messages to that particular client - NBN_Connection *conn; + NBN_ConnectionHandle *conn; // Client state ClientState state; @@ -56,14 +58,14 @@ static unsigned int client_count = 0; static Vector2 spawns[] = {(Vector2){50, 50}, (Vector2){GAME_WIDTH - 100, 50}, (Vector2){50, GAME_HEIGHT - 100}, (Vector2){GAME_WIDTH - 100, GAME_HEIGHT - 100}}; -static void AcceptConnection(Vector2 spawn, NBN_Connection *conn) { +static void AcceptConnection(Vector2 spawn, NBN_ConnectionHandle *conn) { // Accept the connection with some data // this data can be read by the client upon processing the connection event - NBN_Writer *writer = NBN_GameServer_GetConnectionDataWriter(); + NBN_Writer *writer = NBN_GameServer_WriteConnectionData(); NBN_Writer_WriteUInt32(writer, (uint32_t)spawn.x); NBN_Writer_WriteUInt32(writer, (uint32_t)spawn.y); - NBN_Writer_WriteUInt32(writer, conn->id); + NBN_Writer_WriteUInt32(writer, conn->id); // TODO: id is 64bits NBN_GameServer_AcceptIncomingConnection(); } @@ -82,15 +84,16 @@ static int HandleNewConnection(void) { // Otherwise... - NBN_Connection *conn; - - conn = NBN_GameServer_GetIncomingConnection(); + NBN_ConnectionHandle *conn = NBN_GameServer_GetIncomingConnection(); // Read the connection request data transmitted by the client - // NBN_Reader *reader = NBN_GameServer_GetConnectionRequestDataReader(); - // char name[CLIENT_NAME_MAX_LEN]; - // - // NBN_Reader_ReadString(reader, name, sizeof(name)); + NBN_Reader *reader = NBN_GameServer_ReadConnectionRequestData(); + char name[CLIENT_NAME_MAX_LEN]; + + if (NBN_Reader_ReadString(reader, name, sizeof(name)) < 0) { + TraceLog(LOG_ERROR, "Failed to read client name"); + abort(); + } // Get a spawning position for the client Vector2 spawn = spawns[conn->id % MAX_CLIENTS]; @@ -99,7 +102,7 @@ static int HandleNewConnection(void) { AcceptConnection(spawn, conn); - TraceLog(LOG_INFO, "Connection accepted (ID: %d)", conn->id); + TraceLog(LOG_INFO, "Connection accepted (ID: %d, name: %s)", conn->id, name); Client *client = NULL; @@ -120,6 +123,7 @@ static int HandleNewConnection(void) { // Fill the client state with initial spawning data client->state = (ClientState){.client_id = conn->id, .x = 200, .y = 400, .color = CLI_RED, .val = 0}; + memcpy(client->state.name, name, sizeof(client->state.name)); client_count++; @@ -141,7 +145,7 @@ static void DestroyClient(Client *client) { static void HandleClientDisconnection(void) { NBN_DisconnectionInfo info = NBN_GameServer_GetDisconnectionInfo(); - TraceLog(LOG_INFO, "Client has disconnected (id: %d)", info.conn_id); + TraceLog(LOG_INFO, "Client has disconnected (id: %d, user data: %p)", info.conn_id, info.user_data); Client *client = info.user_data; @@ -153,14 +157,14 @@ static void HandleClientDisconnection(void) { static int HandleUpdateStateMessage(Client *sender) { // Update the state of the client with the data from the received UPDATE_STATE_MESSAGE message - NBN_Reader *reader = NBN_GameServer_GetMessageReader(); + NBN_Reader *reader = NBN_GameServer_ReadMessage(); return UpdateClientStateMessage_Read(reader, &sender->state); } static int HandleChangeColorMessage(Client *sender) { // Update the client color - NBN_Reader *reader = NBN_GameServer_GetMessageReader(); + NBN_Reader *reader = NBN_GameServer_ReadMessage(); return ChangeColorMessage_Read(reader, &sender->state.color); } @@ -188,18 +192,18 @@ static int HandleReceivedMessage(void) { static int HandleGameServerEvent(int ev) { switch (ev) { - case NBN_NEW_CONNECTION: + case NBN_SERVER_NEW_CONNECTION: // A new client has requested a connection if (HandleNewConnection() < 0) return -1; break; - case NBN_CLIENT_DISCONNECTED: + case NBN_SERVER_DISCONNECTION: // A previously connected client has disconnected HandleClientDisconnection(); break; - case NBN_CLIENT_MESSAGE_RECEIVED: + case NBN_SERVER_MESSAGE_RECEIVED: // A message from a client has been received if (HandleReceivedMessage() < 0) { // TODO: kick client @@ -223,11 +227,14 @@ static int BroadcastGameState(void) { if (client == NULL) continue; - game_state.client_states[client_index] = (ClientState){.client_id = client->state.client_id, - .x = client->state.x, - .y = client->state.y, - .val = client->state.val, - .color = client->state.color}; + ClientState state = (ClientState){.client_id = client->state.client_id, + .x = client->state.x, + .y = client->state.y, + .val = client->state.val, + .color = client->state.color}; + + memcpy(state.name, client->state.name, sizeof(client->state.name)); + game_state.client_states[client_index] = state; client_index++; } @@ -267,13 +274,6 @@ int main(int argc, char *argv[]) { // Even though we do not display anything we still use raylib logging capacibilities SetTraceLogLevel(LOG_TRACE); -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register(); // Register the WebRTC driver -#else - NBN_UDP_Register(); // Register the UDP driver - -#endif // __EMSCRIPTEN__ - // Initialize the server with a protocol name and a port // protocol name has to match between the server and the clients NBN_GameServer_Init(RAYLIB_EXAMPLE_PROTOCOL_NAME, RAYLIB_EXAMPLE_PORT); @@ -297,7 +297,7 @@ int main(int argc, char *argv[]) { int ev; // Poll for server events - while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) { + while ((ev = NBN_GameServer_Poll()) != NBN_SERVER_NO_EVENT) { if (ev < 0) { TraceLog(LOG_ERROR, "An occured while polling network events. Exit"); diff --git a/examples/raylib/shared.c b/examples/raylib/shared.c index 553cc78..3135cd7 100644 --- a/examples/raylib/shared.c +++ b/examples/raylib/shared.c @@ -20,70 +20,55 @@ */ +#include #include #include #include // nbnet implementation -#define NBNET_IMPL +#define NBNET_IMPL #include "shared.h" // Command line options -enum -{ - OPT_MESSAGES_COUNT, - OPT_PACKET_LOSS, - OPT_PACKET_DUPLICATION, - OPT_PING, - OPT_JITTER -}; +enum { OPT_MESSAGES_COUNT, OPT_PACKET_LOSS, OPT_PACKET_DUPLICATION, OPT_PING, OPT_JITTER }; static Options options = {0}; -void ChangeColorMessage_Write(NBN_Writer *writer, ClientColor color) -{ +void ChangeColorMessage_Write(NBN_Writer *writer, ClientColor color) { NBN_Writer_WriteUInt32(writer, (uint32_t)color); } -int ChangeColorMessage_Read(NBN_Reader *reader, ClientColor *color) -{ +int ChangeColorMessage_Read(NBN_Reader *reader, ClientColor *color) { return NBN_Reader_ReadUInt32(reader, (uint32_t *)color); } -void UpdateClientStateMessage_Write(NBN_Writer *writer, ClientState state) -{ +void UpdateClientStateMessage_Write(NBN_Writer *writer, ClientState state) { NBN_Writer_WriteInt32(writer, state.x); NBN_Writer_WriteInt32(writer, state.y); NBN_Writer_WriteFloat(writer, state.val); } -int UpdateClientStateMessage_Read(NBN_Reader *reader, ClientState *state) -{ - if (NBN_Reader_ReadInt32(reader, &state->x) < 0) - { +int UpdateClientStateMessage_Read(NBN_Reader *reader, ClientState *state) { + if (NBN_Reader_ReadInt32(reader, &state->x) < 0) { return -1; } - if (NBN_Reader_ReadInt32(reader, &state->y) < 0) - { + if (NBN_Reader_ReadInt32(reader, &state->y) < 0) { return -1; } - if (NBN_Reader_ReadFloat(reader, &state->val) < 0) - { + if (NBN_Reader_ReadFloat(reader, &state->val) < 0) { return -1; } return 0; } -void GameStateMessage_Write(NBN_Writer *writer, GameState *state) -{ +void GameStateMessage_Write(NBN_Writer *writer, GameState *state) { NBN_Writer_WriteUInt32(writer, state->client_count); - for (unsigned int i = 0; i < state->client_count; i++) - { + for (unsigned int i = 0; i < state->client_count; i++) { ClientState cli_state = state->client_states[i]; NBN_Writer_WriteUInt32(writer, cli_state.client_id); @@ -91,47 +76,43 @@ void GameStateMessage_Write(NBN_Writer *writer, GameState *state) NBN_Writer_WriteInt32(writer, cli_state.x); NBN_Writer_WriteInt32(writer, cli_state.y); NBN_Writer_WriteFloat(writer, cli_state.val); + NBN_Writer_WriteString(writer, cli_state.name, CLIENT_NAME_MAX_LEN); } } -int GameStateMessage_Read(NBN_Reader *reader, GameState *state) -{ - if (NBN_Reader_ReadUInt32(reader, &state->client_count) < 0) - { +int GameStateMessage_Read(NBN_Reader *reader, GameState *state) { + if (NBN_Reader_ReadUInt32(reader, &state->client_count) < 0) { return -1; } - if (state->client_count > MAX_CLIENTS) - { + if (state->client_count > MAX_CLIENTS) { return -1; } - for (unsigned int i = 0; i < state->client_count; i++) - { + for (unsigned int i = 0; i < state->client_count; i++) { ClientState *cli_state = &state->client_states[i]; - if (NBN_Reader_ReadUInt32(reader, &cli_state->client_id) < 0) - { + if (NBN_Reader_ReadUInt32(reader, &cli_state->client_id) < 0) { return -1; } - if (NBN_Reader_ReadUInt32(reader, (uint32_t *)&cli_state->color) < 0) - { + if (NBN_Reader_ReadUInt32(reader, (uint32_t *)&cli_state->color) < 0) { return -1; } - if (NBN_Reader_ReadInt32(reader, &cli_state->x) < 0) - { + if (NBN_Reader_ReadInt32(reader, &cli_state->x) < 0) { return -1; } - if (NBN_Reader_ReadInt32(reader, &cli_state->y) < 0) - { + if (NBN_Reader_ReadInt32(reader, &cli_state->y) < 0) { return -1; } - if (NBN_Reader_ReadFloat(reader, &cli_state->val) < 0) - { + if (NBN_Reader_ReadFloat(reader, &cli_state->val) < 0) { + return -1; + } + + if (NBN_Reader_ReadString(reader, cli_state->name, CLIENT_NAME_MAX_LEN) < 0) { return -1; } } @@ -140,42 +121,37 @@ int GameStateMessage_Read(NBN_Reader *reader, GameState *state) } // Parse the command line -int ReadCommandLine(int argc, char *argv[]) -{ +int ReadCommandLine(int argc, char *argv[]) { int opt; int option_index; - struct option long_options[] = { - { "packet_loss", required_argument, NULL, OPT_PACKET_LOSS }, - { "packet_duplication", required_argument, NULL, OPT_PACKET_DUPLICATION }, - { "ping", required_argument, NULL, OPT_PING }, - { "jitter", required_argument, NULL, OPT_JITTER } - }; - - while ((opt = getopt_long(argc, argv, "", long_options, &option_index)) != -1) - { - switch (opt) - { - case OPT_PACKET_LOSS: - options.packet_loss = atof(optarg); - break; - - case OPT_PACKET_DUPLICATION: - options.packet_duplication = atof(optarg); - break; - - case OPT_PING: - options.ping = atof(optarg); - break; - - case OPT_JITTER: - options.jitter = atof(optarg); - break; - - case '?': - return -1; - - default: - return -1; + struct option long_options[] = {{"packet_loss", required_argument, NULL, OPT_PACKET_LOSS}, + {"packet_duplication", required_argument, NULL, OPT_PACKET_DUPLICATION}, + {"ping", required_argument, NULL, OPT_PING}, + {"jitter", required_argument, NULL, OPT_JITTER}}; + + while ((opt = getopt_long(argc, argv, "", long_options, &option_index)) != -1) { + switch (opt) { + case OPT_PACKET_LOSS: + options.packet_loss = atof(optarg); + break; + + case OPT_PACKET_DUPLICATION: + options.packet_duplication = atof(optarg); + break; + + case OPT_PING: + options.ping = atof(optarg); + break; + + case OPT_JITTER: + options.jitter = atof(optarg); + break; + + case '?': + return -1; + + default: + return -1; } } @@ -183,7 +159,4 @@ int ReadCommandLine(int argc, char *argv[]) } // Return the command line options -Options GetOptions(void) -{ - return options; -} +Options GetOptions(void) { return options; } diff --git a/examples/raylib/shared.h b/examples/raylib/shared.h index 05ea6bb..b877f36 100644 --- a/examples/raylib/shared.h +++ b/examples/raylib/shared.h @@ -95,12 +95,6 @@ typedef struct tagMSG *LPMSG; #include "../../nbnet.h" -#ifdef __EMSCRIPTEN__ -#include "../../net_drivers/webrtc.h" -#else -#include "../../net_drivers/udp.h" -#endif // __EMSCRIPTEN__ - #define TICK_RATE 60 // Simulation tick rate // Window size, used to display window but also to cap the serialized position values within messages @@ -138,6 +132,7 @@ typedef struct { int y; float val; ClientColor color; + char name[CLIENT_NAME_MAX_LEN]; } ClientState; // Represents the state of all clients From 567d0d297acc1eebedf3c78314b8de14e651b563 Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sat, 7 Feb 2026 12:07:41 +0100 Subject: [PATCH 45/85] update soak test --- nbnet.c | 14 +++- nbnet.h | 5 +- soak/CMakeLists.txt | 4 +- soak/client.c | 62 ++++++++-------- soak/log.c | 168 ++++++++++++++++++++++++++++++++++++++++++++ soak/log.h | 49 +++++++++++++ soak/logging.c | 93 ------------------------ soak/logging.h | 54 -------------- soak/server.c | 65 ++++++++--------- soak/soak.c | 12 ++-- soak/soak.h | 1 - 11 files changed, 303 insertions(+), 224 deletions(-) create mode 100644 soak/log.c create mode 100644 soak/log.h delete mode 100644 soak/logging.c delete mode 100644 soak/logging.h diff --git a/nbnet.c b/nbnet.c index d3a3590..fbf8be5 100644 --- a/nbnet.c +++ b/nbnet.c @@ -2152,8 +2152,18 @@ NBN_ConnectionHandle *NBN_GameServer_FindConnection(NBN_Connection_ID id) { unsigned int NBN_GameServer_GetClientCount(void) { return hmlen(nbn_game_server.clients); } -NBN_ConnectionHandle *NBN_GameServer_GetClientByIndex(unsigned int index) { - return (NBN_ConnectionHandle *)nbn_game_server.clients[index].value; +NBN_ConnectionHandle *NBN_GameServer_GetNextClient(NBN_Client_Iterator *it) { + for (; *it < hmlen(nbn_game_server.clients);) { + NBN_Connection *conn = (NBN_Connection *)nbn_game_server.clients[*it].value; + + (*it)++; + + if (conn->is_accepted) { + return (NBN_ConnectionHandle *)conn; + } + } + + return NULL; } NBN_Server_Event NBN_GameServer_Poll(void) { diff --git a/nbnet.h b/nbnet.h index 8822071..e7eb8e8 100644 --- a/nbnet.h +++ b/nbnet.h @@ -321,6 +321,8 @@ typedef struct NBN_DisconnectionInfo { void *user_data; /* Pointer to user-defined data associated with this connection */ } NBN_DisconnectionInfo; +typedef unsigned int NBN_Client_Iterator; + /** * Initialize the game server with minimal configuration. * @@ -351,8 +353,7 @@ NBN_ConnectionHandle *NBN_GameServer_FindConnection(NBN_Connection_ID); // TODO: doc unsigned int NBN_GameServer_GetClientCount(void); -// TODO: doc -NBN_ConnectionHandle *NBN_GameServer_GetClientByIndex(unsigned int index); +NBN_ConnectionHandle *NBN_GameServer_GetNextClient(NBN_Client_Iterator *it); /** * Poll game server events. diff --git a/soak/CMakeLists.txt b/soak/CMakeLists.txt index 57c55a5..a9bb18a 100644 --- a/soak/CMakeLists.txt +++ b/soak/CMakeLists.txt @@ -12,8 +12,8 @@ endif (CPP_COMPILE) unset(CPP_COMPILE) -add_executable(client client.c soak.c logging.c cargs.c ../nbnet.c) -add_executable(server server.c soak.c logging.c cargs.c ../nbnet.c) +add_executable(client client.c soak.c log.c cargs.c ../nbnet.c) +add_executable(server server.c soak.c log.c cargs.c ../nbnet.c) add_compile_options(-Wall -Wextra -Wpedantic -std=c99) diff --git a/soak/client.c b/soak/client.c index eb180cf..abcffd4 100644 --- a/soak/client.c +++ b/soak/client.c @@ -22,8 +22,11 @@ */ #include +#include +#include #include #include "soak.h" +#include "log.h" typedef struct { uint8_t data[SOAK_MESSAGE_BIG_MAX_LENGTH]; @@ -60,12 +63,12 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { // number of messages sent but not yet to be acked unsigned int pending_message_count = channel->last_sent_message_id - channel->last_recved_message_id; - LogInfo("Compute number of soak messages to send (sent: %d, pending: %d, remaining: %d)", - channel->sent_message_count, pending_message_count, remaining_message_count); + log_info("Compute number of soak messages to send (sent: %d, pending: %d, remaining: %d)", + channel->sent_message_count, pending_message_count, remaining_message_count); // don't send anything on this tick if we have reached the max number of unacked messages if (pending_message_count >= SOAK_CLIENT_MAX_PENDING_MESSAGES) { - LogInfo("Max number of pending messages has been reached, not sending anything this tick"); + log_info("Max number of pending messages has been reached, not sending anything this tick"); return 0; } @@ -74,7 +77,7 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { unsigned int send_message_count = fmin(SOAK_CLIENT_MAX_PENDING_MESSAGES - pending_message_count, remaining_message_count); - LogInfo("Will send %d soak messages this tick", send_message_count); + log_info("Will send %d soak messages this tick", send_message_count); for (int i = 0; i < send_message_count; i++) { int percent = rand() % 100 + 1; @@ -102,7 +105,7 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { entry->free = false; entry->channel_id = channel_id; - LogInfo("Send soak message (id: %d, data length: %d)", msg_id, data_length); + log_info("Send soak message (id: %d, data length: %d)", msg_id, data_length); // TODO: support big messages NBN_Writer *writer = NBN_GameClient_CreateMessage(SOAK_MESSAGE_SMALL, channel->id); @@ -126,17 +129,17 @@ static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) unsigned int data_length; static uint8_t recv_buffer[SOAK_MESSAGE_BIG_MAX_DATA_LENGTH]; - NBN_Reader *reader = NBN_GameClient_GetMessageReader(); + NBN_Reader *reader = NBN_GameClient_ReadMessage(); if (SoakMessage_Read(reader, &msg_id, recv_buffer, &data_length) < 0) { - LogError("Failed to read soak message"); + log_error("Failed to read soak message"); return -1; } if (msg_id != channel->last_recved_message_id + 1) { - LogError("Expected to receive message %d but received message %d (channel_id: %d)", - channel->last_recved_message_id + 1, msg_id, channel_id); + log_error("Expected to receive message %d but received message %d (channel_id: %d)", + channel->last_recved_message_id + 1, msg_id, channel_id); return -1; } @@ -147,15 +150,15 @@ static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) assert(entry->channel_id == channel_id); if (data_length != entry->length) { - LogError("Expected message %d to have length %d but was %d (channel_id: %d)", msg_id, entry->length, - data_length); + log_error("Expected message %d to have length %d but was %d (channel_id: %d)", msg_id, entry->length, + data_length); return -1; } if (memcmp(recv_buffer, entry->data, data_length) != 0) { - LogError("Received invalid data for message %d (data length: %d, channel_id: %d)", msg_id, data_length, - channel_id); + log_error("Received invalid data for message %d (data length: %d, channel_id: %d)", msg_id, data_length, + channel_id); return -1; } @@ -166,16 +169,16 @@ static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) SoakOptions options = Soak_GetOptions(); - LogInfo("Received soak message (length: %d, %d/%d) on channel %d", data_length, msg_id, channel->message_count, - channel_id); + log_info("Received soak message (length: %d, %d/%d) on channel %d", data_length, msg_id, channel->message_count, + channel_id); if (channel->last_recved_message_id == channel->message_count) { - LogInfo("Received all soak message echoes on channel %d", channel_id); + log_info("Received all soak message echoes on channel %d", channel_id); done_channel_count++; } if (done_channel_count >= NBN_CHANNEL_COUNT) { - LogInfo("Received all soak message echoes on all channels"); + log_info("Received all soak message echoes on all channels"); Soak_Stop(); return SOAK_DONE; @@ -192,7 +195,7 @@ static int HandleReceivedMessage(SoakChannel *channels) { if (msg.type == SOAK_MESSAGE_SMALL) { ret = HandleReceivedSoakMessage(msg.channel_id, channels); } else { - LogError("Received unexpected message (type: %d, channel_id: %d)", msg.type, msg.channel_id); + log_error("Received unexpected message (type: %d, channel_id: %d)", msg.type, msg.channel_id); ret = -1; } @@ -205,24 +208,24 @@ static int Tick(void *data) { int ev; - while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) { + while ((ev = NBN_GameClient_Poll()) != NBN_CLIENT_NO_EVENT) { if (ev < 0) return -1; switch (ev) { - case NBN_DISCONNECTED: + case NBN_CLIENT_DISCONNECTED: connected = false; - LogInfo("Disconnected from server (code: %d)", NBN_GameClient_GetServerCloseCode()); + log_info("Disconnected from server (code: %d)", NBN_GameClient_GetServerCloseCode()); Soak_Stop(); return 0; - case NBN_CONNECTED: - LogInfo("Connected to server"); + case NBN_CLIENT_CONNECTED: + log_info("Connected to server"); connected = true; break; - case NBN_MESSAGE_RECEIVED: + case NBN_CLIENT_MESSAGE_RECEIVED: if (HandleReceivedMessage(channels) < 0) return -1; break; @@ -234,14 +237,14 @@ static int Tick(void *data) { SoakChannel *channel = &channels[c]; if (SendSoakMessages(channel, channel->id) < 0) { - LogError("An error occured while sending messages on channel %d", c); + log_error("An error occured while sending messages on channel %d", c); return -1; } } } if (NBN_GameClient_Flush() < 0) { - LogError("Failed to flush game client send queue. Exit"); + log_error("Failed to flush game client send queue. Exit"); return -1; } @@ -250,8 +253,7 @@ static int Tick(void *data) { } int main(int argc, char *argv[]) { - NBN_SetLogFunction(Log); - SetLogLevel(NBN_LOG_DEBUG); + NBN_SetLogLevel(NBN_LOG_DEBUG); if (Soak_ReadCommandLine(argc, argv) < 0) return -1; @@ -293,7 +295,7 @@ int main(int argc, char *argv[]) { } if (NBN_GameClient_Start() < 0) { - LogError("Failed to start game client. Exit"); + log_error("Failed to start game client. Exit"); #ifdef __EMSCRIPTEN__ emscripten_force_exit(1); @@ -303,7 +305,7 @@ int main(int argc, char *argv[]) { } if (Soak_Init(argc, argv) < 0) { - LogError("Failed to initialize soak test"); + log_error("Failed to initialize soak test"); return 1; } diff --git a/soak/log.c b/soak/log.c new file mode 100644 index 0000000..1a7626e --- /dev/null +++ b/soak/log.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2020 rxi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "log.h" + +#define MAX_CALLBACKS 32 + +typedef struct { + log_LogFn fn; + void *udata; + int level; +} Callback; + +static struct { + void *udata; + log_LockFn lock; + int level; + bool quiet; + Callback callbacks[MAX_CALLBACKS]; +} L; + + +static const char *level_strings[] = { + "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" +}; + +#ifdef LOG_USE_COLOR +static const char *level_colors[] = { + "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" +}; +#endif + + +static void stdout_callback(log_Event *ev) { + char buf[16]; + buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; +#ifdef LOG_USE_COLOR + fprintf( + ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", + buf, level_colors[ev->level], level_strings[ev->level], + ev->file, ev->line); +#else + fprintf( + ev->udata, "%s %-5s %s:%d: ", + buf, level_strings[ev->level], ev->file, ev->line); +#endif + vfprintf(ev->udata, ev->fmt, ev->ap); + fprintf(ev->udata, "\n"); + fflush(ev->udata); +} + + +static void file_callback(log_Event *ev) { + char buf[64]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; + fprintf( + ev->udata, "%s %-5s %s:%d: ", + buf, level_strings[ev->level], ev->file, ev->line); + vfprintf(ev->udata, ev->fmt, ev->ap); + fprintf(ev->udata, "\n"); + fflush(ev->udata); +} + + +static void lock(void) { + if (L.lock) { L.lock(true, L.udata); } +} + + +static void unlock(void) { + if (L.lock) { L.lock(false, L.udata); } +} + + +const char* log_level_string(int level) { + return level_strings[level]; +} + + +void log_set_lock(log_LockFn fn, void *udata) { + L.lock = fn; + L.udata = udata; +} + + +void log_set_level(int level) { + L.level = level; +} + + +void log_set_quiet(bool enable) { + L.quiet = enable; +} + + +int log_add_callback(log_LogFn fn, void *udata, int level) { + for (int i = 0; i < MAX_CALLBACKS; i++) { + if (!L.callbacks[i].fn) { + L.callbacks[i] = (Callback) { fn, udata, level }; + return 0; + } + } + return -1; +} + + +int log_add_fp(FILE *fp, int level) { + return log_add_callback(file_callback, fp, level); +} + + +static void init_event(log_Event *ev, void *udata) { + if (!ev->time) { + time_t t = time(NULL); + ev->time = localtime(&t); + } + ev->udata = udata; +} + + +void log_log(int level, const char *file, int line, const char *fmt, ...) { + log_Event ev = { + .fmt = fmt, + .file = file, + .line = line, + .level = level, + }; + + lock(); + + if (!L.quiet && level >= L.level) { + init_event(&ev, stderr); + va_start(ev.ap, fmt); + stdout_callback(&ev); + va_end(ev.ap); + } + + for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { + Callback *cb = &L.callbacks[i]; + if (level >= cb->level) { + init_event(&ev, cb->udata); + va_start(ev.ap, fmt); + cb->fn(&ev); + va_end(ev.ap); + } + } + + unlock(); +} diff --git a/soak/log.h b/soak/log.h new file mode 100644 index 0000000..b1fae24 --- /dev/null +++ b/soak/log.h @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2020 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `log.c` for details. + */ + +#ifndef LOG_H +#define LOG_H + +#include +#include +#include +#include + +#define LOG_VERSION "0.1.0" + +typedef struct { + va_list ap; + const char *fmt; + const char *file; + struct tm *time; + void *udata; + int line; + int level; +} log_Event; + +typedef void (*log_LogFn)(log_Event *ev); +typedef void (*log_LockFn)(bool lock, void *udata); + +enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; + +#define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) +#define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) +#define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) +#define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) +#define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) +#define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) + +const char* log_level_string(int level); +void log_set_lock(log_LockFn fn, void *udata); +void log_set_level(int level); +void log_set_quiet(bool enable); +int log_add_callback(log_LogFn fn, void *udata, int level); +int log_add_fp(FILE *fp, int level); + +void log_log(int level, const char *file, int line, const char *fmt, ...); + +#endif diff --git a/soak/logging.c b/soak/logging.c deleted file mode 100644 index 949936c..0000000 --- a/soak/logging.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#include "logging.h" - -/* I did not write this library: https://github.com/rxi/log.c */ - -/** - * Copyright (c) 2017 rxi - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See `log.c` for details. - */ - -static struct { - void *udata; - FILE *fp; - int level; - int quiet; -} L; - -static const char *level_names[] = {"ERROR", "INFO", "WARNING", "DEBUG"}; - -#ifdef LOG_USE_COLOR -static const char *level_colors[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"}; -#endif - -void SetLogLevel(NBN_LogLevel level) { - L.level = level; - NBN_SetLogLevel(level); -} - -void Log(NBN_LogLevel level, const char *file, int line, const char *fmt, ...) { - if (L.level < level) { - return; - } - - /* Get current time */ - time_t t = time(NULL); - struct tm *lt = localtime(&t); - - /* Log to stderr */ - if (!L.quiet) { - va_list args; - char buf[16]; - buf[strftime(buf, sizeof(buf), "%H:%M:%S", lt)] = '\0'; -#ifdef LOG_USE_COLOR - fprintf(stderr, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buf, level_colors[level], level_names[level], file, - line); -#else - fprintf(stderr, "%s %-5s %s:%d: ", buf, level_names[level], file, line); -#endif - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - fprintf(stderr, "\n"); - fflush(stderr); - } - - /* Log to file */ - if (L.fp) { - va_list args; - char buf[32]; - buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; - fprintf(L.fp, "%s %-5s %s:%d: ", buf, level_names[level], file, line); - va_start(args, fmt); - vfprintf(L.fp, fmt, args); - va_end(args); - fprintf(L.fp, "\n"); - fflush(L.fp); - } -} diff --git a/soak/logging.h b/soak/logging.h deleted file mode 100644 index 300f18f..0000000 --- a/soak/logging.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#ifndef SOAK_LOGGING_H -#define SOAK_LOGGING_H - -/* I did not write this library: https://github.com/rxi/log.c */ - -/** - * Copyright (c) 2017 rxi - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See `log.c` for details. - */ - -#include -#include -#include -#include "../nbnet.h" - -#define LOG_VERSION "0.1.0" - -#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) - -#define LogDebug(...) Log(NBN_LOG_DEBUG, __FILENAME__, __LINE__, __VA_ARGS__) -#define LogInfo(...) Log(NBN_LOG_INFO, __FILENAME__, __LINE__, __VA_ARGS__) -#define LogWarn(...) Log(NBN_LOG_WARN, __FILENAME__, __LINE__, __VA_ARGS__) -#define LogError(...) Log(NBN_LOG_ERROR, __FILENAME__, __LINE__, __VA_ARGS__) - -void SetLogLevel(NBN_LogLevel level); -void Log(NBN_LogLevel level, const char *file, int line, const char *fmt, ...); - -#endif /* SOAK_LOGGING_H */ diff --git a/soak/server.c b/soak/server.c index c220e4a..ca638b4 100644 --- a/soak/server.c +++ b/soak/server.c @@ -25,8 +25,10 @@ #include #include #include +#include #include #include "soak.h" +#include "log.h" typedef struct { uint8_t channel_id; @@ -58,7 +60,7 @@ typedef struct { static void HandleNewConnection(void) { NBN_GameServer_AcceptIncomingConnection(); - NBN_Connection *conn = NBN_GameServer_GetIncomingConnection(); + NBN_ConnectionHandle *conn = NBN_GameServer_GetIncomingConnection(); SoakClient *soak_client = (SoakClient *)malloc(sizeof(SoakClient)); soak_client->error = false; @@ -82,7 +84,7 @@ static void HandleNewConnection(void) { conn->user_data = soak_client; - LogInfo("Client has connected (ID: %llu)", conn->id); + log_info("Client has connected (ID: %llu)", conn->id); } static void HandleClientDisconnection(NBN_DisconnectionInfo info) { @@ -90,18 +92,17 @@ static void HandleClientDisconnection(NBN_DisconnectionInfo info) { assert(soak_client != NULL); - LogInfo("Client has disconnected (ID: %d)", info.conn_id); + log_info("Client has disconnected (ID: %d)", info.conn_id); free(soak_client->channels); free(soak_client); } static void EchoReceivedSoakMessages(void) { - for (unsigned int i = 0; i < NBN_GameServer_GetClientCount(); i++) { - NBN_Connection *conn = NBN_GameServer_GetClientByIndex(i); + NBN_Client_Iterator it = 0; + NBN_ConnectionHandle *conn; - if (!conn->is_accepted) - continue; + while ((conn = NBN_GameServer_GetNextClient(&it))) { SoakClient *soak_client = (SoakClient *)conn->user_data; @@ -119,14 +120,14 @@ static void EchoReceivedSoakMessages(void) { SoakMessage_Write(writer, msg_entry->msg_id, msg_entry->data, msg_entry->length); - LogInfo("Send soak message %d's echo (length: %d) to client %llu", msg_entry->msg_id, msg_entry->length, - conn->id); + log_info("Send soak message %d's echo (length: %d) to client %llu", msg_entry->msg_id, + msg_entry->length, conn->id); if (NBN_GameServer_EnqueueMessageFor(conn) < 0) { - LogError("Failed to send soak message to client %llu, closing client", conn->id); + log_error("Failed to send soak message to client %llu, closing client", conn->id); if (NBN_GameServer_CloseClient(conn) < 0) { - LogError("Failed to close client %llu", conn->id); + log_error("Failed to close client %llu", conn->id); abort(); } @@ -143,7 +144,7 @@ static void EchoReceivedSoakMessages(void) { } } -static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_Connection *sender, uint8_t channel_id) { +static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_ConnectionHandle *sender, uint8_t channel_id) { SoakClient *soak_client = (SoakClient *)sender->user_data; assert(soak_client != NULL); @@ -157,22 +158,22 @@ static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_Connection *sender, static uint8_t recv_buffer[SOAK_MESSAGE_BIG_MAX_DATA_LENGTH]; if (SoakMessage_Read(reader, &msg_id, recv_buffer, &data_length) < 0) { - LogError("Failed to read soak message"); + log_error("Failed to read soak message"); return -1; } if (msg_id != channel->last_recved_message_id + 1) { - LogError("Expected to receive message %d but received message %d (from client: %d)", - channel->last_recved_message_id + 1, msg_id, sender); + log_error("Expected to receive message %d but received message %d (from client: %d)", + channel->last_recved_message_id + 1, msg_id, sender); soak_client->error = true; return -1; } - LogInfo("Received soak message %d (length: %d) from client %llu on channel %d", msg_id, data_length, sender->id, - channel_id); + log_info("Received soak message %d (length: %d) from client %llu on channel %d", msg_id, data_length, sender->id, + channel_id); channel->recved_messages_count++; channel->last_recved_message_id = msg_id; @@ -182,7 +183,7 @@ static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_Connection *sender, assert(channel->echo_queue.count < SOAK_CLIENT_MAX_PENDING_MESSAGES); assert(msg_entry->length == 0); - LogInfo("Enqueue soak message %d's echo for client %llu on channel %d", msg_id, sender->id, channel_id); + log_info("Enqueue soak message %d's echo for client %llu on channel %d", msg_id, sender->id, channel_id); memcpy(msg_entry->data, recv_buffer, data_length); msg_entry->msg_id = msg_id; @@ -197,14 +198,14 @@ static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_Connection *sender, static void HandleReceivedMessage(void) { NBN_MessageInfo msg_info = NBN_GameServer_GetMessageInfo(); - NBN_Reader *reader = NBN_GameServer_GetMessageReader(); + NBN_Reader *reader = NBN_GameServer_ReadMessage(); SoakClient *soak_client = (SoakClient *)msg_info.sender->user_data; switch (msg_info.type) { case SOAK_MESSAGE_SMALL: if (HandleReceivedSoakMessage(reader, msg_info.sender, msg_info.channel_id) < 0) { if (NBN_GameServer_CloseClient(msg_info.sender) < 0) { - LogError("Failed to close client %llu", msg_info.sender->id); + log_error("Failed to close client %llu", msg_info.sender->id); abort(); } @@ -215,10 +216,10 @@ static void HandleReceivedMessage(void) { // TODO: support big messages default: - LogError("Received unexpected message (type: %d, channel_id: %d)", msg_info.type, msg_info.channel_id); + log_error("Received unexpected message (type: %d, channel_id: %d)", msg_info.type, msg_info.channel_id); if (NBN_GameServer_CloseClient(msg_info.sender) < 0) { - LogError("Failed to close client %llu", msg_info.sender->id); + log_error("Failed to close client %llu", msg_info.sender->id); abort(); } @@ -232,20 +233,20 @@ static int Tick(void *data) { int ev; - while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) { + while ((ev = NBN_GameServer_Poll()) != NBN_SERVER_NO_EVENT) { if (ev < 0) return -1; switch (ev) { - case NBN_NEW_CONNECTION: + case NBN_SERVER_NEW_CONNECTION: HandleNewConnection(); break; - case NBN_CLIENT_DISCONNECTED: + case NBN_SERVER_DISCONNECTION: HandleClientDisconnection(NBN_GameServer_GetDisconnectionInfo()); break; - case NBN_CLIENT_MESSAGE_RECEIVED: + case NBN_SERVER_MESSAGE_RECEIVED: HandleReceivedMessage(); break; } @@ -254,7 +255,7 @@ static int Tick(void *data) { EchoReceivedSoakMessages(); if (NBN_GameServer_Flush() < 0) { - LogError("Failed to flush game server send queue. Exit"); + log_error("Failed to flush game server send queue. Exit"); return -1; } @@ -267,8 +268,7 @@ static void SigintHandler(int dummy) { Soak_Stop(); } int main(int argc, char *argv[]) { signal(SIGINT, SigintHandler); - NBN_SetLogFunction(Log); - SetLogLevel(NBN_LOG_DEBUG); + NBN_SetLogLevel(NBN_LOG_DEBUG); if (Soak_ReadCommandLine(argc, argv) < 0) return -1; @@ -301,20 +301,17 @@ int main(int argc, char *argv[]) { } if (NBN_GameServer_Start()) { - LogError("Failed to start game server"); + log_error("Failed to start game server"); return 1; } if (Soak_Init(argc, argv) < 0) { - LogError("Failed to initialize soak test"); + log_error("Failed to initialize soak test"); return 1; } - NBN_GameServer_Debug_RegisterCallback(NBN_DEBUG_CB_MSG_ADDED_TO_RECV_QUEUE, - (void *)Soak_Debug_PrintAddedToRecvQueue); - int ret = Soak_MainLoop(Tick, NULL); NBN_GameServer_Stop(); diff --git a/soak/soak.c b/soak/soak.c index 23d8ef8..22d5dbd 100644 --- a/soak/soak.c +++ b/soak/soak.c @@ -22,11 +22,11 @@ */ -#include "logging.h" #include #include #include #include +#include "log.h" #ifdef __EMSCRIPTEN__ #include @@ -69,8 +69,8 @@ int Soak_Init(int argc, char *argv[]) { SoakOptions options = Soak_GetOptions(); - LogInfo("Soak test initialized (Packet loss: %f, Packet duplication: %f, Ping: %f, Jitter: %f)", - options.packet_loss, options.packet_duplication, options.ping, options.jitter); + log_info("Soak test initialized (Packet loss: %f, Packet duplication: %f, Ping: %f, Jitter: %f)", + options.packet_loss, options.packet_duplication, options.ping, options.jitter); /* Packet simulator configuration */ #ifdef SOAK_CLIENT @@ -91,8 +91,8 @@ int Soak_Init(int argc, char *argv[]) { } void Soak_Deinit(void) { - LogInfo("Done."); - LogInfo("Memory report:\n"); + log_info("Done."); + log_info("Memory report:\n"); // TODO } @@ -185,7 +185,7 @@ int Soak_MainLoop(int (*Tick)(void *), void *data) { void Soak_Stop(void) { running = false; - LogInfo("Soak test stopped"); + log_info("Soak test stopped"); } SoakOptions Soak_GetOptions(void) { return soak_options; } diff --git a/soak/soak.h b/soak/soak.h index 2d1c12c..1776ff5 100644 --- a/soak/soak.h +++ b/soak/soak.h @@ -34,7 +34,6 @@ #include #include -#include "logging.h" #include "../nbnet.h" #define SOAK_PROTOCOL_NAME "nbnet_soak" From ca2bd1616dcd20bb43b85347bd1fb98987c05b4e Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sat, 7 Feb 2026 12:20:43 +0100 Subject: [PATCH 46/85] pipelines: enable udp driver for soak tests --- .github/workflows/nbnet.yml | 11 ++++------- nbnet.c | 5 ++++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/nbnet.yml b/.github/workflows/nbnet.yml index d85cee4..87c1fc1 100644 --- a/.github/workflows/nbnet.yml +++ b/.github/workflows/nbnet.yml @@ -52,9 +52,6 @@ jobs: cd tests cmake -G "NMake Makefiles" . nmake - # FIXME: string_tests hangs forever - # - name: Run tests - # run: ctest soak-test-linux-c: runs-on: ubuntu-latest @@ -67,7 +64,7 @@ jobs: cd soak mkdir build cd build - cmake -DCMAKE_BUILD_TYPE=Debug .. + cmake -DCMAKE_BUILD_TYPE=Debug -DUDP=ON .. make - name: Run soak test run: timeout 240 ./bin/github-actions/run_soak.sh @@ -83,7 +80,7 @@ jobs: cd soak mkdir build cd build - cmake -DCMAKE_BUILD_TYPE=Debug -DCPP_COMPILE=ON .. + cmake -DCMAKE_BUILD_TYPE=Debug -DUDP=ON -DCPP_COMPILE=ON .. make - name: Run soak test run: timeout 240 ./bin/github-actions/run_soak.sh @@ -123,7 +120,7 @@ jobs: cd soak mkdir build cd build - cmake -DCMAKE_BUILD_TYPE=Debug .. + cmake -DCMAKE_BUILD_TYPE=Debug -DUDP=ON .. make - name: Run soak test run: gtimeout 240 ./bin/github-actions/run_soak.sh @@ -139,7 +136,7 @@ jobs: cd soak mkdir build cd build - cmake -DCMAKE_BUILD_TYPE=Debug -DCPP_COMPILE=ON .. + cmake -DCMAKE_BUILD_TYPE=Debug -DUDP=ON -DCPP_COMPILE=ON .. make - name: Run soak test run: gtimeout 240 ./bin/github-actions/run_soak.sh diff --git a/nbnet.c b/nbnet.c index fbf8be5..cda1646 100644 --- a/nbnet.c +++ b/nbnet.c @@ -1724,7 +1724,10 @@ int NBN_GameClient_Start(void) { driver_count++; #endif // NBN_UDP - if (driver_count != 1) { + if (driver_count < 1) { + LogError("At least one network driver has to be activated"); + NBN_Abort(); + } else if (driver_count > 1) { LogError("Only one network driver can be activated for the client"); NBN_Abort(); } From d4f4ec534072fcf1df86b68577bddb4962f2dff3 Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sun, 8 Feb 2026 06:21:03 +0100 Subject: [PATCH 47/85] fix some windows compilation errors --- examples/echo/shared.c | 5 +- examples/logging/log.c | 173 +++++++++++++++++------------------------ soak/log.c | 173 +++++++++++++++++------------------------ 3 files changed, 149 insertions(+), 202 deletions(-) diff --git a/examples/echo/shared.c b/examples/echo/shared.c index 275dd9e..2f34218 100644 --- a/examples/echo/shared.c +++ b/examples/echo/shared.c @@ -22,7 +22,6 @@ #include #include -#include #include #include @@ -30,9 +29,11 @@ #if defined(__EMSCRIPTEN__) #include #elif defined(_WIN32) || defined(_WIN64) +#define WIN32_LEAN_AND_MEAN +// prevent inclusion of winnt.h in windows.h +#define _WINNT_ #include #include -#include #else #include #endif diff --git a/examples/logging/log.c b/examples/logging/log.c index 1a7626e..55275e4 100644 --- a/examples/logging/log.c +++ b/examples/logging/log.c @@ -25,144 +25,117 @@ #define MAX_CALLBACKS 32 typedef struct { - log_LogFn fn; - void *udata; - int level; + log_LogFn fn; + void *udata; + int level; } Callback; static struct { - void *udata; - log_LockFn lock; - int level; - bool quiet; - Callback callbacks[MAX_CALLBACKS]; + void *udata; + log_LockFn lock; + int level; + bool quiet; + Callback callbacks[MAX_CALLBACKS]; } L; - -static const char *level_strings[] = { - "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" -}; +static const char *level_strings[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}; #ifdef LOG_USE_COLOR -static const char *level_colors[] = { - "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" -}; +static const char *level_colors[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"}; #endif - static void stdout_callback(log_Event *ev) { - char buf[16]; - buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; + char buf[16]; + buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; #ifdef LOG_USE_COLOR - fprintf( - ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", - buf, level_colors[ev->level], level_strings[ev->level], - ev->file, ev->line); + fprintf((FILE *)ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buf, level_colors[ev->level], + level_strings[ev->level], ev->file, ev->line); #else - fprintf( - ev->udata, "%s %-5s %s:%d: ", - buf, level_strings[ev->level], ev->file, ev->line); + fprintf((FILE *)ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level], ev->file, ev->line); #endif - vfprintf(ev->udata, ev->fmt, ev->ap); - fprintf(ev->udata, "\n"); - fflush(ev->udata); + vfprintf((FILE *)ev->udata, ev->fmt, ev->ap); + fprintf((FILE *)ev->udata, "\n"); + fflush((FILE *)ev->udata); } - static void file_callback(log_Event *ev) { - char buf[64]; - buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; - fprintf( - ev->udata, "%s %-5s %s:%d: ", - buf, level_strings[ev->level], ev->file, ev->line); - vfprintf(ev->udata, ev->fmt, ev->ap); - fprintf(ev->udata, "\n"); - fflush(ev->udata); + char buf[64]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; + fprintf((FILE *)ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level], ev->file, ev->line); + vfprintf((FILE *)ev->udata, ev->fmt, ev->ap); + fprintf((FILE *)ev->udata, "\n"); + fflush((FILE *)ev->udata); } - -static void lock(void) { - if (L.lock) { L.lock(true, L.udata); } +static void lock(void) { + if (L.lock) { + L.lock(true, L.udata); + } } - static void unlock(void) { - if (L.lock) { L.lock(false, L.udata); } -} - - -const char* log_level_string(int level) { - return level_strings[level]; + if (L.lock) { + L.lock(false, L.udata); + } } +const char *log_level_string(int level) { return level_strings[level]; } void log_set_lock(log_LockFn fn, void *udata) { - L.lock = fn; - L.udata = udata; -} - - -void log_set_level(int level) { - L.level = level; + L.lock = fn; + L.udata = udata; } +void log_set_level(int level) { L.level = level; } -void log_set_quiet(bool enable) { - L.quiet = enable; -} - +void log_set_quiet(bool enable) { L.quiet = enable; } int log_add_callback(log_LogFn fn, void *udata, int level) { - for (int i = 0; i < MAX_CALLBACKS; i++) { - if (!L.callbacks[i].fn) { - L.callbacks[i] = (Callback) { fn, udata, level }; - return 0; + for (int i = 0; i < MAX_CALLBACKS; i++) { + if (!L.callbacks[i].fn) { + L.callbacks[i] = (Callback){fn, udata, level}; + return 0; + } } - } - return -1; -} - - -int log_add_fp(FILE *fp, int level) { - return log_add_callback(file_callback, fp, level); + return -1; } +int log_add_fp(FILE *fp, int level) { return log_add_callback(file_callback, fp, level); } static void init_event(log_Event *ev, void *udata) { - if (!ev->time) { - time_t t = time(NULL); - ev->time = localtime(&t); - } - ev->udata = udata; + if (!ev->time) { + time_t t = time(NULL); + ev->time = localtime(&t); + } + ev->udata = udata; } - void log_log(int level, const char *file, int line, const char *fmt, ...) { - log_Event ev = { - .fmt = fmt, - .file = file, - .line = line, - .level = level, - }; - - lock(); - - if (!L.quiet && level >= L.level) { - init_event(&ev, stderr); - va_start(ev.ap, fmt); - stdout_callback(&ev); - va_end(ev.ap); - } - - for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { - Callback *cb = &L.callbacks[i]; - if (level >= cb->level) { - init_event(&ev, cb->udata); - va_start(ev.ap, fmt); - cb->fn(&ev); - va_end(ev.ap); + log_Event ev = { + .fmt = fmt, + .file = file, + .line = line, + .level = level, + }; + + lock(); + + if (!L.quiet && level >= L.level) { + init_event(&ev, stderr); + va_start(ev.ap, fmt); + stdout_callback(&ev); + va_end(ev.ap); + } + + for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { + Callback *cb = &L.callbacks[i]; + if (level >= cb->level) { + init_event(&ev, cb->udata); + va_start(ev.ap, fmt); + cb->fn(&ev); + va_end(ev.ap); + } } - } - unlock(); + unlock(); } diff --git a/soak/log.c b/soak/log.c index 1a7626e..55275e4 100644 --- a/soak/log.c +++ b/soak/log.c @@ -25,144 +25,117 @@ #define MAX_CALLBACKS 32 typedef struct { - log_LogFn fn; - void *udata; - int level; + log_LogFn fn; + void *udata; + int level; } Callback; static struct { - void *udata; - log_LockFn lock; - int level; - bool quiet; - Callback callbacks[MAX_CALLBACKS]; + void *udata; + log_LockFn lock; + int level; + bool quiet; + Callback callbacks[MAX_CALLBACKS]; } L; - -static const char *level_strings[] = { - "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" -}; +static const char *level_strings[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}; #ifdef LOG_USE_COLOR -static const char *level_colors[] = { - "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" -}; +static const char *level_colors[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"}; #endif - static void stdout_callback(log_Event *ev) { - char buf[16]; - buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; + char buf[16]; + buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; #ifdef LOG_USE_COLOR - fprintf( - ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", - buf, level_colors[ev->level], level_strings[ev->level], - ev->file, ev->line); + fprintf((FILE *)ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buf, level_colors[ev->level], + level_strings[ev->level], ev->file, ev->line); #else - fprintf( - ev->udata, "%s %-5s %s:%d: ", - buf, level_strings[ev->level], ev->file, ev->line); + fprintf((FILE *)ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level], ev->file, ev->line); #endif - vfprintf(ev->udata, ev->fmt, ev->ap); - fprintf(ev->udata, "\n"); - fflush(ev->udata); + vfprintf((FILE *)ev->udata, ev->fmt, ev->ap); + fprintf((FILE *)ev->udata, "\n"); + fflush((FILE *)ev->udata); } - static void file_callback(log_Event *ev) { - char buf[64]; - buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; - fprintf( - ev->udata, "%s %-5s %s:%d: ", - buf, level_strings[ev->level], ev->file, ev->line); - vfprintf(ev->udata, ev->fmt, ev->ap); - fprintf(ev->udata, "\n"); - fflush(ev->udata); + char buf[64]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; + fprintf((FILE *)ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level], ev->file, ev->line); + vfprintf((FILE *)ev->udata, ev->fmt, ev->ap); + fprintf((FILE *)ev->udata, "\n"); + fflush((FILE *)ev->udata); } - -static void lock(void) { - if (L.lock) { L.lock(true, L.udata); } +static void lock(void) { + if (L.lock) { + L.lock(true, L.udata); + } } - static void unlock(void) { - if (L.lock) { L.lock(false, L.udata); } -} - - -const char* log_level_string(int level) { - return level_strings[level]; + if (L.lock) { + L.lock(false, L.udata); + } } +const char *log_level_string(int level) { return level_strings[level]; } void log_set_lock(log_LockFn fn, void *udata) { - L.lock = fn; - L.udata = udata; -} - - -void log_set_level(int level) { - L.level = level; + L.lock = fn; + L.udata = udata; } +void log_set_level(int level) { L.level = level; } -void log_set_quiet(bool enable) { - L.quiet = enable; -} - +void log_set_quiet(bool enable) { L.quiet = enable; } int log_add_callback(log_LogFn fn, void *udata, int level) { - for (int i = 0; i < MAX_CALLBACKS; i++) { - if (!L.callbacks[i].fn) { - L.callbacks[i] = (Callback) { fn, udata, level }; - return 0; + for (int i = 0; i < MAX_CALLBACKS; i++) { + if (!L.callbacks[i].fn) { + L.callbacks[i] = (Callback){fn, udata, level}; + return 0; + } } - } - return -1; -} - - -int log_add_fp(FILE *fp, int level) { - return log_add_callback(file_callback, fp, level); + return -1; } +int log_add_fp(FILE *fp, int level) { return log_add_callback(file_callback, fp, level); } static void init_event(log_Event *ev, void *udata) { - if (!ev->time) { - time_t t = time(NULL); - ev->time = localtime(&t); - } - ev->udata = udata; + if (!ev->time) { + time_t t = time(NULL); + ev->time = localtime(&t); + } + ev->udata = udata; } - void log_log(int level, const char *file, int line, const char *fmt, ...) { - log_Event ev = { - .fmt = fmt, - .file = file, - .line = line, - .level = level, - }; - - lock(); - - if (!L.quiet && level >= L.level) { - init_event(&ev, stderr); - va_start(ev.ap, fmt); - stdout_callback(&ev); - va_end(ev.ap); - } - - for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { - Callback *cb = &L.callbacks[i]; - if (level >= cb->level) { - init_event(&ev, cb->udata); - va_start(ev.ap, fmt); - cb->fn(&ev); - va_end(ev.ap); + log_Event ev = { + .fmt = fmt, + .file = file, + .line = line, + .level = level, + }; + + lock(); + + if (!L.quiet && level >= L.level) { + init_event(&ev, stderr); + va_start(ev.ap, fmt); + stdout_callback(&ev); + va_end(ev.ap); + } + + for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { + Callback *cb = &L.callbacks[i]; + if (level >= cb->level) { + init_event(&ev, cb->udata); + va_start(ev.ap, fmt); + cb->fn(&ev); + va_end(ev.ap); + } } - } - unlock(); + unlock(); } From bea84b9612f370c34907e625dab7f4f29d79b66d Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sun, 8 Feb 2026 06:35:50 +0100 Subject: [PATCH 48/85] fixes --- nbnet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nbnet.c b/nbnet.c index cda1646..961e5c4 100644 --- a/nbnet.c +++ b/nbnet.c @@ -1663,7 +1663,7 @@ static int ServerDriver_OnClientPacketReceived(NBN_Packet *); static int GameClient_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); static NBN_Client_Event GameClient_HandleEvent(void); -static int GameClient_HandleMessageReceivedEvent(void); +static NBN_Client_Event GameClient_HandleMessageReceivedEvent(void); static NBN_Connection *CreateServerConnection(NBN_Driver_ID driver_id); static void InitChannelModes(NBN_ChannelMode channel_modes[NBN_CHANNEL_COUNT]) { @@ -2144,7 +2144,7 @@ void NBN_GameServer_Stop(void) { static NBN_Connection_ID NBN_BuildConnectionHash(NBN_Connection_ID id, NBN_Driver_ID driver_id) { NBN_Assert(id <= UINT64_MAX - 0xFF); - uint8_t driver_byte = NBN_DRIVER_UDP; + uint8_t driver_byte = driver_id; return ((NBN_Connection_ID)driver_byte << 56) | id; } From 2acfb400146fd29a9584ac6973752ad8aac66c25 Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sun, 8 Feb 2026 06:36:18 +0100 Subject: [PATCH 49/85] pipelines: compile examples with UDP driver --- .github/workflows/nbnet.yml | 24 ++++++++++++------------ examples/CMakeLists.txt | 6 ++++++ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/.github/workflows/nbnet.yml b/.github/workflows/nbnet.yml index 87c1fc1..6aa4e82 100644 --- a/.github/workflows/nbnet.yml +++ b/.github/workflows/nbnet.yml @@ -213,7 +213,7 @@ jobs: cd .. WEBRTC_NATIVE=1 timeout 240 ./bin/github-actions/run_soak.sh - compile-examples-linux-c: + compile-examples-udp-linux-c: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -222,10 +222,10 @@ jobs: - name: Compile examples run: | cd examples - cmake . + cmake -DEXAMPLES_UDP=ON . make - compile-examples-linux-cpp: + compile-examples-udp-linux-cpp: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -234,10 +234,10 @@ jobs: - name: Compile examples run: | cd examples - cmake -DCPP_COMPILE=ON . + cmake -DEXAMPLES_UDP=ON -DCPP_COMPILE=ON . make - compile-examples-osx-c: + compile-examples-udp-osx-c: runs-on: macos-latest steps: - uses: actions/checkout@v3 @@ -246,10 +246,10 @@ jobs: - name: Compile examples run: | cd examples - cmake . + cmake -DEXAMPLES_UDP=ON . make - compile-examples-osx-cpp: + compile-examples-udp-osx-cpp: runs-on: macos-latest steps: - uses: actions/checkout@v3 @@ -258,10 +258,10 @@ jobs: - name: Compile examples run: | cd examples - cmake -DCPP_COMPILE=ON . + cmake -DEXAMPLES_UDP=ON -DCPP_COMPILE=ON . make - compile-examples-windows-c: + compile-examples-udp-windows-c: runs-on: windows-latest steps: - uses: actions/checkout@v3 @@ -269,10 +269,10 @@ jobs: - name: Compile examples run: | cd examples - cmake -G "NMake Makefiles" . + cmake -G "NMake Makefiles" -DEXAMPLES_UDP=ON . nmake - compile-examples-windows-cpp: + compile-examples-udp-windows-cpp: runs-on: windows-latest steps: - uses: actions/checkout@v3 @@ -280,5 +280,5 @@ jobs: - name: Compile examples run: | cd examples - cmake -G "NMake Makefiles" -DCPP_COMPILE=ON . + cmake -G "NMake Makefiles" -DEXAMPLES_UDP=ON -DCPP_COMPILE=ON . nmake diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 4725651..0fd039e 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,4 +1,10 @@ cmake_minimum_required(VERSION 3.5) project(nbnet_examples) +option(EXAMPLES_UDP OFF) + +if (EXAMPLES_UDP) + set(UDP ON) +endif (EXAMPLES_UDP) + add_subdirectory(echo) From a44fa4da63f9ebf0efc9e0c436c8416cb0147fdd Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sun, 8 Feb 2026 08:02:21 +0100 Subject: [PATCH 50/85] reintroduce webrtc emscripten driver --- nbnet.c | 310 +++++++++++++++++++++++++++++++++++++------ nbnet.h | 19 ++- net_drivers/webrtc.h | 1 - soak/client.c | 12 +- soak/server.c | 9 +- 5 files changed, 294 insertions(+), 57 deletions(-) diff --git a/nbnet.c b/nbnet.c index 961e5c4..d98f142 100644 --- a/nbnet.c +++ b/nbnet.c @@ -23,7 +23,8 @@ */ // TODO: make functions that are not part of the public API static -// TODO: reintroduce webrtc drivers +// TODO: reintroduce webrtc native driver +// TODO: remove pragmas #include #include "nbnet.h" @@ -58,12 +59,6 @@ #endif -#ifdef __EMSCRIPTEN__ - -#include - -#endif - #ifndef NBN_PLATFORM_WINDOWS #include @@ -288,6 +283,8 @@ typedef struct NBN_IPAddress { uint16_t port; } NBN_IPAddress; +typedef uint32_t NBN_WebRTC_Peer_ID; + struct NBN_Connection { NBN_ConnectionHandle handle; double last_recv_packet_time; /* Used to detect stale connections */ @@ -308,6 +305,8 @@ struct NBN_Connection { struct { NBN_IPAddress ip_address; } udp; + + NBN_WebRTC_Peer_ID peer_id; } driver_data; /* @@ -438,7 +437,7 @@ typedef struct NBN_Driver_Implementation { NBN_Driver_Func_ServerCleanupConnection serv_cleanup_connection; } NBN_Driver_Implementation; -enum NBN_Driver_ID { NBN_DRIVER_UDP = 0x01 }; +enum NBN_Driver_ID { NBN_DRIVER_UDP = 0x01, NBN_DRIVER_WEBRTC_EMSCRIPTEN }; struct NBN_Driver { int id; @@ -514,6 +513,47 @@ static SOCKET nbn_udp_sock; #endif // NBN_UDP +#ifdef __EMSCRIPTEN__ + +#ifdef NBN_UDP +#error "Cannot compile UDP driver with emscripten" +#endif + +// TODO: add a check for webrtc native as well + +#include + +static int WebRTC_Client_Start(NBN_GameClient *client, const char *host, uint16_t port); +static void WebRTC_Client_Stop(NBN_GameClient *client); +static int WebRTC_Client_RecvPackets(NBN_GameClient *client); +static int WebRTC_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet); + +static int WebRTC_Server_Start(NBN_GameServer *server, uint16_t port); +static void WebRTC_Server_Stop(NBN_GameServer *server); +static int WebRTC_Server_RecvPackets(NBN_GameServer *server); +static int WebRTC_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *connection); +static void WebRTC_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *connection); + +static NBN_Driver nbn_webrtc_em_driver = {.name = "WebRTC_EMSCRIPTEN", + .impl = {// Client implementation + .cli_start = WebRTC_Client_Start, + .cli_stop = WebRTC_Client_Stop, + .cli_recv_packets = WebRTC_Client_RecvPackets, + .cli_send_packet = WebRTC_Client_SendPacket, + + // Server implementation + .serv_start = WebRTC_Server_Start, + .serv_stop = WebRTC_Server_Stop, + .serv_recv_packets = WebRTC_Server_RecvPackets, + .serv_send_packet_to = WebRTC_Server_SendPacketTo, + .serv_cleanup_connection = WebRTC_Server_CleanupConnection}}; + +static NBN_WebRTC_Config nbn_wrtc_cfg = {.enable_tls = false, .cert_path = NULL, .key_path = NULL}; + +void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config); + +#endif // __EMSCRIPTEN__ + #pragma region Serialization void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length) { @@ -1566,6 +1606,12 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, NBN_Con connection->driver = &nbn_udp_driver; break; #endif // NBN_UDP + +#ifdef __EMSCRIPTEN__ + case NBN_DRIVER_WEBRTC_EMSCRIPTEN: + connection->driver = &nbn_webrtc_em_driver; + break; +#endif default: LogError("Unsupported driver: %d", driver_id); NBN_Abort(); @@ -1702,15 +1748,7 @@ NBN_Writer *NBN_GameClient_WriteConnectionRequestData(void) { return &nbn_game_client.client_data_writer; } -int NBN_GameClient_Start(void) { - NBN_GameClient_Config config = nbn_game_client.config; - const char *protocol_name = config.protocol_name; - const char *host = config.host; - uint16_t port = config.port; - uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - - Endpoint_Init(&nbn_game_client.endpoint, protocol_id, false, config.channel_modes); - +static int StartClientDrivers(const char *host, uint16_t port) { int driver_count = 0; #ifdef NBN_UDP @@ -1724,6 +1762,31 @@ int NBN_GameClient_Start(void) { driver_count++; #endif // NBN_UDP +#ifdef __EMSCRIPTEN__ + nbn_game_client.server_connection = CreateServerConnection(NBN_DRIVER_WEBRTC_EMSCRIPTEN); + + if (nbn_webrtc_em_driver.impl.cli_start(&nbn_game_client, host, port) < 0) { + LogError("Failed to start driver %s", nbn_webrtc_em_driver.name); + return NBN_ERROR; + } + + driver_count++; +#endif // __EMSCRIPTEN__ + + return driver_count; +} + +int NBN_GameClient_Start(void) { + NBN_GameClient_Config config = nbn_game_client.config; + const char *protocol_name = config.protocol_name; + const char *host = config.host; + uint16_t port = config.port; + uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); + + Endpoint_Init(&nbn_game_client.endpoint, protocol_id, false, config.channel_modes); + + int driver_count = StartClientDrivers(host, port); + if (driver_count < 1) { LogError("At least one network driver has to be activated"); NBN_Abort(); @@ -1791,6 +1854,10 @@ void NBN_GameClient_Stop(void) { nbn_udp_driver.impl.cli_stop(&nbn_game_client); #endif // NBN_UDP +#ifdef __EMSCRIPTEN__ + nbn_webrtc_em_driver.impl.cli_stop(&nbn_game_client); +#endif // __EMSCRIPTEN__ + nbn_game_client.is_connected = false; nbn_game_client.closed_code = -1; nbn_game_client.endpoint.server_initial_data_len = 0; @@ -1809,6 +1876,24 @@ NBN_Reader *NBN_GameClient_ReadServerData(void) { return &nbn_game_client.server_data_reader; } +static int ReadPacketsFromClientDrivers(void) { +#ifdef NBN_UDP + if (nbn_udp_driver.impl.cli_recv_packets(&nbn_game_client) < 0) { + LogError("Failed to read packets from driver %s", nbn_udp_driver.name); + return NBN_ERROR; + } +#endif // NBN_UDP + +#ifdef __EMSCRIPTEN__ + if (nbn_webrtc_em_driver.impl.cli_recv_packets(&nbn_game_client) < 0) { + LogError("Failed to read packets from driver %s", nbn_webrtc_em_driver.name); + return NBN_ERROR; + } +#endif // __EMSCRIPTEN__ + + return 0; +} + NBN_Client_Event NBN_GameClient_Poll(void) { Endpoint_UpdateTime(&nbn_game_client.endpoint); @@ -1832,12 +1917,9 @@ NBN_Client_Event NBN_GameClient_Poll(void) { if (!NBN_EventQueue_Enqueue(&endpoint->event_queue, e)) return NBN_ERROR; } else { -#ifdef NBN_UDP - if (nbn_udp_driver.impl.cli_recv_packets(&nbn_game_client) < 0) { - LogError("Failed to read packets from driver %s", nbn_udp_driver.name); + if (ReadPacketsFromClientDrivers() < 0) { return NBN_ERROR; } -#endif // NBN_UDP NBN_Connection *server_conn = nbn_game_client.server_connection; @@ -2076,16 +2158,7 @@ void NBN_GameServer_SetChannelMode(uint8_t channel_id, NBN_ChannelMode mode) { nbn_game_server.endpoint.channel_modes[channel_id] = mode; } -int NBN_GameServer_Start(void) { - NBN_GameServer_Config config = nbn_game_server.config; - const char *protocol_name = config.protocol_name; - uint16_t port = config.port; - uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - - Endpoint_Init(&nbn_game_server.endpoint, protocol_id, true, config.channel_modes); - - nbn_game_server.closed_clients_head = NULL; - +static int StartServerDrivers(uint16_t port) { int driver_count = 0; #ifdef NBN_UDP @@ -2097,6 +2170,30 @@ int NBN_GameServer_Start(void) { driver_count++; #endif // NBN_UDP +#ifdef __EMSCRIPTEN__ + if (nbn_webrtc_em_driver.impl.serv_start(&nbn_game_server, port) < 0) { + LogError("Failed to start driver %s", nbn_webrtc_em_driver.name); + return NBN_ERROR; + } + + driver_count++; +#endif // __EMSCRIPTEN__ + + return driver_count; +} + +int NBN_GameServer_Start(void) { + NBN_GameServer_Config config = nbn_game_server.config; + const char *protocol_name = config.protocol_name; + uint16_t port = config.port; + uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); + + Endpoint_Init(&nbn_game_server.endpoint, protocol_id, true, config.channel_modes); + + nbn_game_server.closed_clients_head = NULL; + + int driver_count = StartServerDrivers(port); + if (driver_count < 1) { LogError("At least one network driver has to be activated"); NBN_Abort(); @@ -2125,6 +2222,10 @@ void NBN_GameServer_Stop(void) { nbn_udp_driver.impl.serv_stop(&nbn_game_server); #endif // NBN_UDP +#ifdef __EMSCRIPTEN__ + nbn_webrtc_em_driver.impl.serv_stop(&nbn_game_server); +#endif // __EMSCRIPTEN__ + // Free closed clients list NBN_ConnectionListNode *current = nbn_game_server.closed_clients_head; @@ -2169,6 +2270,20 @@ NBN_ConnectionHandle *NBN_GameServer_GetNextClient(NBN_Client_Iterator *it) { return NULL; } +static void ReadPacketsFromServerDrivers(void) { +#ifdef NBN_UDP + if (nbn_udp_driver.impl.serv_recv_packets(&nbn_game_server) < 0) { + LogError("Failed to read packets from driver %s", nbn_udp_driver.name); + } +#endif // NBN_UDP + +#ifdef __EMSCRIPTEN__ + if (nbn_webrtc_em_driver.impl.serv_recv_packets(&nbn_game_server) < 0) { + LogError("Failed to read packets from driver %s", nbn_webrtc_em_driver.name); + } +#endif // __EMSCRIPTEN__ +} + NBN_Server_Event NBN_GameServer_Poll(void) { Endpoint_UpdateTime(&nbn_game_server.endpoint); @@ -2178,12 +2293,7 @@ NBN_Server_Event NBN_GameServer_Poll(void) { if (GameServer_CloseStaleClientConnections() < 0) return NBN_ERROR; -#ifdef NBN_UDP - if (nbn_udp_driver.impl.serv_recv_packets(&nbn_game_server) < 0) { - LogError("Failed to read packets from driver %s", nbn_udp_driver.name); - return NBN_ERROR; - } -#endif // NBN_UDP + ReadPacketsFromServerDrivers(); nbn_game_server.stats.download_bandwidth = 0; @@ -2715,9 +2825,11 @@ static int ServerDriver_OnClientPacketReceived(NBN_Packet *packet) { #pragma endregion /* Game server driver */ -#ifdef NBN_UDP +/** + * ======================= DRIVER IMPLEMENTATIONS ======================= * + */ -#pragma region UDP driver +#ifdef NBN_UDP #ifdef NBN_PLATFORM_WINDOWS @@ -2862,7 +2974,7 @@ int UDP_Server_Start(NBN_GameServer *server, uint16_t port) { void UDP_Server_Stop(NBN_GameServer *server) { UDP_DeinitSocket(); } int UDP_Server_RecvPackets(NBN_GameServer *server) { - NBN_Packet packet = {0}; + static NBN_Packet packet = {0}; SOCKADDR_IN src_addr; socklen_t src_addr_len = sizeof(src_addr); @@ -2984,10 +3096,124 @@ static int UDP_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet) { return 0; } -#pragma enregion /* UDP driver */ - #endif // NBN_UDP +#ifdef __EMSCRIPTEN__ + +/** + * JS API + * + * See net_drivers/webrtc/js for the implementation of these functions. + */ + +extern void __js_game_server_init(uint32_t, bool, const char *, const char *); +extern int __js_game_server_start(uint16_t); +extern int __js_game_server_dequeue_packet(uint32_t *, uint8_t *); +extern int __js_game_server_send_packet_to(uint8_t *, unsigned int, uint32_t); +extern void __js_game_server_close_client_peer(unsigned int); +extern void __js_game_server_stop(void); + +extern void __js_game_client_init(uint32_t, bool); +extern int __js_game_client_start(const char *, uint16_t); +extern int __js_game_client_dequeue_packet(uint8_t *); +extern int __js_game_client_send_packet(uint8_t *, unsigned int); +extern void __js_game_client_close(void); + +void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config) { nbn_wrtc_cfg = config; } + +static int WebRTC_Server_Start(NBN_GameServer *server, uint16_t port) { + __js_game_server_init(server->endpoint.protocol_id, nbn_wrtc_cfg.enable_tls, nbn_wrtc_cfg.key_path, + nbn_wrtc_cfg.cert_path); + + if (__js_game_server_start(port) < 0) + return -1; + + return 0; +} + +static void WebRTC_Server_Stop(NBN_GameServer *server) { __js_game_server_stop(); } + +static int WebRTC_Server_RecvPackets(NBN_GameServer *server) { + static NBN_Packet packet = {0}; + uint32_t peer_id; + unsigned int len; + + while ((len = __js_game_server_dequeue_packet(&peer_id, packet.buffer)) > 0) { + NBN_Connection_ID conn_id = NBN_BuildConnectionHash(peer_id, NBN_DRIVER_WEBRTC_EMSCRIPTEN); + NBN_ConnectionHandle *handle = NBN_GameServer_FindConnection(conn_id); + NBN_Connection *conn = NULL; + + if (handle == NULL) { + LogInfo("Peer %d has connected", peer_id); + + conn = CreateClientConnection(NBN_DRIVER_WEBRTC_EMSCRIPTEN, conn_id); + conn->driver_data.peer_id = peer_id; + + ServerDriver_OnClientConnected(conn); + } else { + conn = HANDLE_TO_CONN(handle); + } + + if (NBN_Packet_InitRead(&packet, server->endpoint.protocol_id, len) < 0) + continue; + + packet.sender = conn; + + ServerDriver_OnClientPacketReceived(&packet); + } + + return 0; +} + +static void WebRTC_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *conn) { + assert(conn != NULL); + + __js_game_server_close_client_peer(conn->driver_data.peer_id); +} + +static int WebRTC_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *conn) { + return __js_game_server_send_packet_to(packet->buffer, packet->size, conn->driver_data.peer_id); +} + +static int WebRTC_Client_Start(NBN_GameClient *client, const char *host, uint16_t port) { + __js_game_client_init(client->endpoint.protocol_id, nbn_wrtc_cfg.enable_tls); + + int res; + + if ((res = __js_game_client_start(host, port)) < 0) + return NBN_ERROR; + + return 0; +} + +static void WebRTC_Client_Stop(NBN_GameClient *client) { __js_game_client_close(); } + +static int WebRTC_Client_RecvPackets(NBN_GameClient *client) { + static NBN_Packet packet = {0}; + unsigned int len; + + while ((len = __js_game_client_dequeue_packet(packet.buffer)) > 0) { + if (NBN_Packet_InitRead(&packet, client->endpoint.protocol_id, len) < 0) + continue; + + packet.sender = client->server_connection; + + ClientDriver_OnPacketReceived(&packet); + } + + return 0; +} + +static int WebRTC_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet) { + return __js_game_client_send_packet(packet->buffer, packet->size); +} + +#endif // __EMSCRIPTEN__ + +/** + * ====================================================================== * + */ + #pragma region Packet simulator #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) diff --git a/nbnet.h b/nbnet.h index e7eb8e8..1ff02d7 100644 --- a/nbnet.h +++ b/nbnet.h @@ -130,7 +130,6 @@ int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len); typedef uint64_t NBN_Connection_ID; typedef struct NBN_ConnectionHandle { - // TODO: use 32 bits ID and 64 bits hash for the hashtable NBN_Connection_ID id; void *user_data; } NBN_ConnectionHandle; @@ -478,6 +477,24 @@ NBN_MessageInfo NBN_GameServer_GetMessageInfo(void); */ NBN_GameServerStats NBN_GameServer_GetStats(void); +#ifdef __EMSCRIPTEN__ + +/** + * - EMSCRIPTEN WEBRTC DRIVER SPECIFIC API - + */ + +typedef struct NBN_WebRTC_Config { + bool enable_tls; + const char *cert_path; + const char *key_path; +} NBN_WebRTC_Config; + +void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config); + +/* ========================================================================== */ + +#endif // __EMSCRIPTEN__ + #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) #ifndef NBN_PLATFORM_WINDOWS diff --git a/net_drivers/webrtc.h b/net_drivers/webrtc.h index 7ce7b54..cd29cd3 100644 --- a/net_drivers/webrtc.h +++ b/net_drivers/webrtc.h @@ -85,7 +85,6 @@ typedef struct NBN_WebRTC_Server { int key; NBN_WebRTC_Peer *value; } *peers; - uint32_t protocol_id; } NBN_WebRTC_Server; static NBN_WebRTC_Server nbn_wrtc_serv = {NULL, 0}; diff --git a/soak/client.c b/soak/client.c index abcffd4..c61cb64 100644 --- a/soak/client.c +++ b/soak/client.c @@ -28,6 +28,10 @@ #include "soak.h" #include "log.h" +#ifdef __EMSCRIPTEN__ +#include "emscripten.h" +#endif + typedef struct { uint8_t data[SOAK_MESSAGE_BIG_MAX_LENGTH]; uint8_t channel_id; @@ -260,10 +264,6 @@ int main(int argc, char *argv[]) { SoakOptions options = Soak_GetOptions(); -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register((NBN_WebRTC_Config){.enable_tls = false}); // Register the JS WebRTC driver -#else - #ifdef WEBRTC_NATIVE if (options.webrtc) { @@ -282,12 +282,8 @@ int main(int argc, char *argv[]) { NBN_UDP_Register(); } -#else - #endif // WEBRTC_NATIVE -#endif // __EMSCRIPTEN__ - NBN_GameClient_Init(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT); for (uint8_t c = 0; c < NBN_CHANNEL_COUNT; c++) { diff --git a/soak/server.c b/soak/server.c index ca638b4..5f6432d 100644 --- a/soak/server.c +++ b/soak/server.c @@ -30,6 +30,10 @@ #include "soak.h" #include "log.h" +#ifdef __EMSCRIPTEN__ +#include "emscripten.h" +#endif + typedef struct { uint8_t channel_id; unsigned int msg_id; @@ -273,11 +277,6 @@ int main(int argc, char *argv[]) { if (Soak_ReadCommandLine(argc, argv) < 0) return -1; -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register((NBN_WebRTC_Config){.enable_tls = false}); // Register JS WebRTC driver -#else -#endif // __EMSCRIPTEN__ - #ifdef WEBRTC_NATIVE // Register native WebRTC driver const char *ice_servers[] = {"stun:stun01.sipphone.com"}; From 7c95abda681003130e516d9b3cf6d0b2ceef7fef Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sun, 8 Feb 2026 08:38:14 +0100 Subject: [PATCH 51/85] fix packet simulator --- .github/workflows/nbnet.yml | 2 +- bin/github-actions/build_soak_web.sh | 2 +- nbnet.c | 78 ++++++++++++++++++++++---- nbnet.h | 82 +++------------------------- soak/CMakeLists.txt | 11 +++- 5 files changed, 88 insertions(+), 87 deletions(-) diff --git a/.github/workflows/nbnet.yml b/.github/workflows/nbnet.yml index 6aa4e82..530b0e7 100644 --- a/.github/workflows/nbnet.yml +++ b/.github/workflows/nbnet.yml @@ -175,7 +175,7 @@ jobs: cd soak mkdir build cd build - cmake -G "NMake Makefiles" .. + cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Debug -DUDP=ON .. nmake - name: Run soak test run: ./bin/github-actions/run_soak.sh diff --git a/bin/github-actions/build_soak_web.sh b/bin/github-actions/build_soak_web.sh index f1a1594..ad8187f 100755 --- a/bin/github-actions/build_soak_web.sh +++ b/bin/github-actions/build_soak_web.sh @@ -5,7 +5,7 @@ source ./emsdk_env.sh cd ../soak mkdir build_web cd build_web -emcmake cmake .. +emcmake cmake -DCMAKE_BUILD_TYPE=Debug .. make cd .. npm install diff --git a/nbnet.c b/nbnet.c index d98f142..9cbfb5b 100644 --- a/nbnet.c +++ b/nbnet.c @@ -352,6 +352,55 @@ bool NBN_EventQueue_Enqueue(NBN_EventQueue *, NBN_Event); bool NBN_EventQueue_Dequeue(NBN_EventQueue *, NBN_Event *); bool NBN_EventQueue_IsEmpty(NBN_EventQueue *); +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + +#ifndef NBN_PLATFORM_WINDOWS +#include +#endif /* NBN_PLATFORM_WINDOWS */ + +typedef struct NBN_PacketSimulatorEntry NBN_PacketSimulatorEntry; + +struct NBN_PacketSimulatorEntry { + NBN_Packet packet; + NBN_Connection *receiver; + double delay; + double enqueued_at; + struct NBN_PacketSimulatorEntry *next; + struct NBN_PacketSimulatorEntry *prev; +}; + +typedef struct NBN_PacketSimulator { + NBN_Endpoint *endpoint; + NBN_PacketSimulatorEntry *head_packet; + NBN_PacketSimulatorEntry *tail_packet; + unsigned int packet_count; + +#ifdef NBN_PLATFORM_WINDOWS + HANDLE queue_mutex; + HANDLE thread; +#else + pthread_mutex_t queue_mutex; + pthread_t thread; +#endif + + bool running; + unsigned int total_dropped_packets; + + /* Settings */ + float packet_loss_ratio; + float current_packet_loss_ratio; + float packet_duplication_ratio; + double ping; + double jitter; +} NBN_PacketSimulator; + +static void PacketSimulator_Init(NBN_PacketSimulator *, NBN_Endpoint *); +static int PacketSimulator_EnqueuePacket(NBN_PacketSimulator *, NBN_Packet *, NBN_Connection *); +static void PacketSimulator_Start(NBN_PacketSimulator *); +static void PacketSimulator_Stop(NBN_PacketSimulator *); + +#endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ + struct NBN_Endpoint { NBN_EventQueue event_queue; uint32_t protocol_id; @@ -1521,16 +1570,7 @@ bool NBN_EventQueue_IsEmpty(NBN_EventQueue *event_queue) { return event_queue->c #pragma endregion /* NBN_EventQueue */ -#pragma region NBN_Endpoint - -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - -static void PacketSimulator_Init(NBN_PacketSimulator *, NBN_Endpoint *); -static int PacketSimulator_EnqueuePacket(NBN_PacketSimulator *, NBN_Packet *, NBN_Connection *); -static void PacketSimulator_Start(NBN_PacketSimulator *); -static void PacketSimulator_Stop(NBN_PacketSimulator *); - -#endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ +#pragma region Endpoint static void Endpoint_Init(NBN_Endpoint *, uint32_t, bool, NBN_ChannelMode[NBN_CHANNEL_COUNT]); static void Endpoint_Deinit(NBN_Endpoint *); @@ -3227,6 +3267,20 @@ DWORD WINAPI PacketSimulator_Routine(LPVOID); static void *PacketSimulator_Routine(void *); #endif +void NBN_GameClient_SetPing(float v) { nbn_game_client.endpoint.packet_simulator.ping = v; } +void NBN_GameClient_SetJitter(float v) { nbn_game_client.endpoint.packet_simulator.jitter = v; } +void NBN_GameClient_SetPacketLoss(float v) { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } +void NBN_GameClient_SetPacketDuplication(float v) { + nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; +} + +void NBN_GameServer_SetPing(float v) { nbn_game_server.endpoint.packet_simulator.ping = v; } +void NBN_GameServer_SetJitter(float v) { nbn_game_server.endpoint.packet_simulator.jitter = v; } +void NBN_GameServer_SetPacketLoss(float v) { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } +void NBN_GameServer_SetPacketDuplication(float v) { + nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; +} + static int PacketSimulator_SendPacket(NBN_PacketSimulator *, NBN_Packet *, NBN_Connection *receiver); static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimulator *); @@ -3306,6 +3360,10 @@ void PacketSimulator_Start(NBN_PacketSimulator *packet_simulator) { #endif packet_simulator->running = true; + + LogDebug("Packet simulator started (Packet loss: %f, Packet duplication: %f, Ping: %f, Jitter: %f)", + packet_simulator->packet_loss_ratio, packet_simulator->packet_duplication_ratio, packet_simulator->ping, + packet_simulator->jitter); } void PacketSimulator_Stop(NBN_PacketSimulator *packet_simulator) { diff --git a/nbnet.h b/nbnet.h index 1ff02d7..d0d2bc6 100644 --- a/nbnet.h +++ b/nbnet.h @@ -497,79 +497,15 @@ void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config); #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) -#ifndef NBN_PLATFORM_WINDOWS -#include -#endif /* NBN_PLATFORM_WINDOWS */ - -typedef struct NBN_PacketSimulatorEntry NBN_PacketSimulatorEntry; - -struct NBN_PacketSimulatorEntry { - NBN_Packet packet; - NBN_Connection *receiver; - double delay; - double enqueued_at; - struct NBN_PacketSimulatorEntry *next; - struct NBN_PacketSimulatorEntry *prev; -}; - -typedef struct NBN_PacketSimulator { - NBN_Endpoint *endpoint; - NBN_PacketSimulatorEntry *head_packet; - NBN_PacketSimulatorEntry *tail_packet; - unsigned int packet_count; - -#ifdef NBN_PLATFORM_WINDOWS - HANDLE queue_mutex; - HANDLE thread; -#else - pthread_mutex_t queue_mutex; - pthread_t thread; -#endif - - bool running; - unsigned int total_dropped_packets; - - /* Settings */ - float packet_loss_ratio; - float current_packet_loss_ratio; - float packet_duplication_ratio; - double ping; - double jitter; -} NBN_PacketSimulator; - -#define NBN_GameClient_SetPing(v) \ - { \ - nbn_game_client.endpoint.packet_simulator.ping = v; \ - } -#define NBN_GameClient_SetJitter(v) \ - { \ - nbn_game_client.endpoint.packet_simulator.jitter = v; \ - } -#define NBN_GameClient_SetPacketLoss(v) \ - { \ - nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; \ - } -#define NBN_GameClient_SetPacketDuplication(v) \ - { \ - nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; \ - } - -#define NBN_GameServer_SetPing(v) \ - { \ - nbn_game_server.endpoint.packet_simulator.ping = v; \ - } -#define NBN_GameServer_SetJitter(v) \ - { \ - nbn_game_server.endpoint.packet_simulator.jitter = v; \ - } -#define NBN_GameServer_SetPacketLoss(v) \ - { \ - nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; \ - } -#define NBN_GameServer_SetPacketDuplication(v) \ - { \ - nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; \ - } +void NBN_GameClient_SetPing(float v); +void NBN_GameClient_SetJitter(float v); +void NBN_GameClient_SetPacketLoss(float v); +void NBN_GameClient_SetPacketDuplication(float v); + +void NBN_GameServer_SetPing(float v); +void NBN_GameServer_SetJitter(float v); +void NBN_GameServer_SetPacketLoss(float v); +void NBN_GameServer_SetPacketDuplication(float v); #else diff --git a/soak/CMakeLists.txt b/soak/CMakeLists.txt index a9bb18a..aba4959 100644 --- a/soak/CMakeLists.txt +++ b/soak/CMakeLists.txt @@ -17,8 +17,15 @@ add_executable(server server.c soak.c log.c cargs.c ../nbnet.c) add_compile_options(-Wall -Wextra -Wpedantic -std=c99) -target_compile_definitions(client PUBLIC NBN_DEBUG SOAK_CLIENT) -target_compile_definitions(server PUBLIC NBN_DEBUG SOAK_SERVER) +if (CMAKE_BUILD_TYPE MATCHES "Debug") + message("Compiling in Debug mode with packet simulator enabled") + + target_compile_definitions(client PUBLIC NBN_DEBUG NBN_USE_PACKET_SIMULATOR) + target_compile_definitions(server PUBLIC NBN_DEBUG NBN_USE_PACKET_SIMULATOR) +endif() + +target_compile_definitions(client PUBLIC SOAK_CLIENT) +target_compile_definitions(server PUBLIC SOAK_SERVER) target_compile_definitions(client PUBLIC NBN_CHANNEL_COUNT=4) target_compile_definitions(server PUBLIC NBN_CHANNEL_COUNT=4) From 2a85873836c47810e7c739de523f380f2107acbf Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sun, 8 Feb 2026 08:50:17 +0100 Subject: [PATCH 52/85] fix channel mode config --- nbnet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nbnet.c b/nbnet.c index 9cbfb5b..017dd64 100644 --- a/nbnet.c +++ b/nbnet.c @@ -1778,7 +1778,7 @@ void NBN_GameClient_SetChannelMode(uint8_t channel_id, NBN_ChannelMode mode) { NBN_Abort(); } - nbn_game_client.endpoint.channel_modes[channel_id] = mode; + nbn_game_client.config.channel_modes[channel_id] = mode; } NBN_Writer *NBN_GameClient_WriteConnectionRequestData(void) { @@ -2195,7 +2195,7 @@ void NBN_GameServer_SetChannelMode(uint8_t channel_id, NBN_ChannelMode mode) { NBN_Abort(); } - nbn_game_server.endpoint.channel_modes[channel_id] = mode; + nbn_game_server.config.channel_modes[channel_id] = mode; } static int StartServerDrivers(uint16_t port) { From f8ff56704331bab75ed45cfd81857d15ba4cff68 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Mon, 9 Feb 2026 07:43:23 +0100 Subject: [PATCH 53/85] add serialization functions --- nbnet.c | 48 ++++++++++++++++++++++++++++++++++++++++-------- nbnet.h | 10 ++++++++-- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/nbnet.c b/nbnet.c index 017dd64..9d527af 100644 --- a/nbnet.c +++ b/nbnet.c @@ -611,8 +611,17 @@ void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length) { writer->position = 0; } +void NBN_Writer_WriteInt8(NBN_Writer *writer, int8_t value) { NBN_Writer_WriteUInt8(writer, value); } + void NBN_Writer_WriteInt32(NBN_Writer *writer, int32_t value) { NBN_Writer_WriteUInt32(writer, value); } +void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value) { + NBN_Assert(writer->position + 1 <= writer->length); + + writer->buffer[writer->position] = value; + writer->position++; +} + void NBN_Writer_WriteUInt16(NBN_Writer *writer, uint16_t value) { NBN_Assert(writer->position + 2 <= writer->length); @@ -627,11 +636,11 @@ void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value) { writer->position += 4; } -void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value) { - NBN_Assert(writer->position + 1 <= writer->length); +void NBN_Writer_WriteUInt64(NBN_Writer *writer, uint64_t value) { + NBN_Assert(writer->position + 8 <= writer->length); - writer->buffer[writer->position] = value; - writer->position++; + *((uint64_t *)(writer->buffer + writer->position)) = htonll(value); + writer->position += 8; } void NBN_Writer_WriteFloat(NBN_Writer *writer, float value) { @@ -641,6 +650,8 @@ void NBN_Writer_WriteFloat(NBN_Writer *writer, float value) { NBN_Writer_WriteUInt32(writer, htonl(*val_u)); } +void NBN_Writer_WriteBool(NBN_Writer *writer, bool value) { NBN_Writer_WriteUInt8(writer, value); } + void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int length) { NBN_Assert(writer->position + length <= writer->length); @@ -661,10 +672,23 @@ void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length) { reader->position = 0; } +int NBN_Reader_ReadInt8(NBN_Reader *reader, int8_t *value) { return NBN_Reader_ReadUInt8(reader, (uint8_t *)value); } + int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value) { return NBN_Reader_ReadUInt32(reader, (uint32_t *)value); } +int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value) { + if (reader->position + 1 > reader->length) { + return NBN_ERROR; + } + + *value = reader->buffer[reader->position]; + reader->position++; + + return 0; +} + int NBN_Reader_ReadUInt16(NBN_Reader *reader, uint16_t *value) { if (reader->position + 2 > reader->length) { return NBN_ERROR; @@ -687,13 +711,13 @@ int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value) { return 0; } -int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value) { - if (reader->position + 1 > reader->length) { +int NBN_Reader_ReadUInt64(NBN_Reader *reader, uint64_t *value) { + if (reader->position + 8 > reader->length) { return NBN_ERROR; } - *value = reader->buffer[reader->position]; - reader->position++; + *value = ntohll(*((uint64_t *)(reader->buffer + reader->position))); + reader->position += 8; return 0; } @@ -706,6 +730,14 @@ int NBN_Reader_ReadFloat(NBN_Reader *reader, float *value) { return 0; } +int NBN_Reader_ReadBool(NBN_Reader *reader, bool *value) { + if (NBN_Reader_ReadUInt8(reader, (uint8_t *)value) < 0) { + return -1; + } + + return 0; +} + int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length) { if (reader->position + length > reader->length) { LogError("reader->position = %d, length = %d, reader->length = %d", reader->position, length, reader->length); diff --git a/nbnet.h b/nbnet.h index d0d2bc6..e31ebee 100644 --- a/nbnet.h +++ b/nbnet.h @@ -108,20 +108,26 @@ typedef struct NBN_Reader { } NBN_Reader; void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length); +void NBN_Writer_WriteInt8(NBN_Writer *writer, int8_t value); void NBN_Writer_WriteInt32(NBN_Writer *writer, int32_t value); +void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value); void NBN_Writer_WriteUInt16(NBN_Writer *writer, uint16_t value); void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value); -void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value); +void NBN_Writer_WriteUInt64(NBN_Writer *writer, uint64_t value); void NBN_Writer_WriteFloat(NBN_Writer *writer, float value); +void NBN_Writer_WriteBool(NBN_Writer *writer, bool value); void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int length); void NBN_Writer_WriteString(NBN_Writer *writer, const char *str, unsigned int max_len); void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length); +int NBN_Reader_ReadInt8(NBN_Reader *reader, int8_t *value); int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value); +int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value); int NBN_Reader_ReadUInt16(NBN_Reader *reader, uint16_t *value); int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value); -int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value); +int NBN_Reader_ReadUInt64(NBN_Reader *reader, uint64_t *value); int NBN_Reader_ReadFloat(NBN_Reader *reader, float *value); +int NBN_Reader_ReadBool(NBN_Reader *reader, bool *value); int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length); int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len); From 2070fc1e14d21e4b6c7a4d4afe4ad8203972e432 Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Wed, 11 Feb 2026 17:55:39 +0100 Subject: [PATCH 54/85] replace non-standard htonll and ntohll --- nbnet.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/nbnet.c b/nbnet.c index 9d527af..fc804c0 100644 --- a/nbnet.c +++ b/nbnet.c @@ -605,6 +605,23 @@ void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config); #pragma region Serialization +static union { + uint64_t v; + uint8_t bytes[8]; +} swap; + +static uint64_t SwapBytes64(uint64_t v) { + if (htonl(1) == 1) { + return v; + } + + swap.v = v; + + return ((uint64_t)swap.bytes[0] << 56) | ((uint64_t)swap.bytes[1] << 48) | ((uint64_t)swap.bytes[2] << 40) | + ((uint64_t)swap.bytes[3] << 32) | ((uint64_t)swap.bytes[4] << 24) | ((uint64_t)swap.bytes[5] << 16) | + ((uint64_t)swap.bytes[6] << 8) | (uint64_t)swap.bytes[7]; +} + void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length) { writer->buffer = buffer; writer->length = length; @@ -639,7 +656,7 @@ void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value) { void NBN_Writer_WriteUInt64(NBN_Writer *writer, uint64_t value) { NBN_Assert(writer->position + 8 <= writer->length); - *((uint64_t *)(writer->buffer + writer->position)) = htonll(value); + *((uint64_t *)(writer->buffer + writer->position)) = htonl(1) == 1 ? value : SwapBytes64(value); writer->position += 8; } @@ -716,7 +733,12 @@ int NBN_Reader_ReadUInt64(NBN_Reader *reader, uint64_t *value) { return NBN_ERROR; } - *value = ntohll(*((uint64_t *)(reader->buffer + reader->position))); + *value = *((uint64_t *)(reader->buffer + reader->position)); + + if (htonl(1) != 1) { + *value = SwapBytes64(*value); + } + reader->position += 8; return 0; From 3d45b5adfc223fd7c9bb2117d740312ffe94bdd4 Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Wed, 11 Feb 2026 18:58:35 +0100 Subject: [PATCH 55/85] organization stuff --- nbnet.c | 459 +++++++++++++++++++++++++++++--------------------------- nbnet.h | 4 +- 2 files changed, 238 insertions(+), 225 deletions(-) diff --git a/nbnet.c b/nbnet.c index fc804c0..1539563 100644 --- a/nbnet.c +++ b/nbnet.c @@ -22,9 +22,7 @@ */ -// TODO: make functions that are not part of the public API static // TODO: reintroduce webrtc native driver -// TODO: remove pragmas #include #include "nbnet.h" @@ -136,59 +134,6 @@ typedef enum NBN_Driver_ID NBN_Driver_ID; #define NBN_RESERVED_UNRELIABLE_CHANNEL_ID 0 #define NBN_RESERVED_RELIABLE_CHANNEL_ID 1 -static int log_level = NBN_LOG_INFO; - -void NBN_SetLogLevel(NBN_LogLevel level) { log_level = level; } - -#ifdef NBN_LOG_CUSTOM_FUNCTION - -extern void Log(NBN_LogLevel level, const char *filename, int line, const char *msg, ...); - -#else - -/** - * Default logging function - */ - -/** - * Copyright (c) 2017 rxi - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See `log.c` for details. - */ - -#include -#include - -static const char *level_names[] = {"ERROR", "INFO", "WARNING", "DEBUG"}; - -static void Log(NBN_LogLevel level, const char *filename, int line, const char *msg, ...) { - if (log_level < level) { - return; - } - - time_t t = time(NULL); - struct tm *lt = localtime(&t); - FILE *fp = level == NBN_LOG_ERROR ? stderr : stdout; - - va_list args; - char buf[32]; - buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; - fprintf(fp, "%s %-5s %s:%d: ", buf, level_names[level], filename, line); - va_start(args, msg); - vfprintf(fp, msg, args); - va_end(args); - fprintf(fp, "\n"); - fflush(fp); -} - -#endif // NBN_CUSTOM_LOG_FUNCTION - -#define LogInfo(msg, ...) Log(NBN_LOG_INFO, __FILE__, __LINE__, msg, ##__VA_ARGS__) -#define LogWarning(msg, ...) Log(NBN_LOG_WARNING, __FILE__, __LINE__, msg, ##__VA_ARGS__) -#define LogError(msg, ...) Log(NBN_LOG_ERROR, __FILE__, __LINE__, msg, ##__VA_ARGS__) -#define LogDebug(msg, ...) Log(NBN_LOG_DEBUG, __FILE__, __LINE__, msg, ##__VA_ARGS__) - typedef enum NBN_PacketResult { NBN_PACKET_WRITE_ERROR = -1, NBN_PACKET_WRITE_OK, @@ -272,12 +217,6 @@ struct NBN_Channel { bool ack_buffer[NBN_CHANNEL_BUFFER_SIZE]; }; -void NBN_Channel_Destroy(NBN_Endpoint *, NBN_Channel *); - -typedef struct NBN_UnreliableOrderedChannel { - NBN_Channel base; -} NBN_UnreliableOrderedChannel; - typedef struct NBN_IPAddress { uint32_t host; uint16_t port; @@ -319,15 +258,6 @@ struct NBN_Connection { uint32_t packet_recv_seq_buffer[NBN_MAX_PACKET_ENTRIES]; }; -typedef struct NBN_ConnectionListNode NBN_ConnectionListNode; - -/* Linked list of connections */ -struct NBN_ConnectionListNode { - NBN_Connection *conn; - NBN_ConnectionListNode *next; - NBN_ConnectionListNode *prev; -}; - typedef union NBN_EventData { NBN_MessageInfo message_info; NBN_DisconnectionInfo disconnection; @@ -339,6 +269,19 @@ typedef struct NBN_Event { NBN_EventData data; } NBN_Event; +/** + * ====== DATA STRUCTURES ====== + */ + +typedef struct NBN_ConnectionListNode NBN_ConnectionListNode; + +/* Linked list of connections */ +struct NBN_ConnectionListNode { + NBN_Connection *conn; + NBN_ConnectionListNode *next; + NBN_ConnectionListNode *prev; +}; + typedef struct NBN_EventQueue { NBN_Event events[NBN_EVENT_QUEUE_CAPACITY]; unsigned int head; @@ -346,11 +289,39 @@ typedef struct NBN_EventQueue { unsigned int count; } NBN_EventQueue; -NBN_EventQueue *NBN_EventQueue_Create(void); -void NBN_EventQueue_Destroy(NBN_EventQueue *); -bool NBN_EventQueue_Enqueue(NBN_EventQueue *, NBN_Event); -bool NBN_EventQueue_Dequeue(NBN_EventQueue *, NBN_Event *); -bool NBN_EventQueue_IsEmpty(NBN_EventQueue *); +static void EventQueue_Init(NBN_EventQueue *event_queue) { + event_queue->head = 0; + event_queue->tail = 0; + event_queue->count = 0; +} + +static bool EventQueue_Enqueue(NBN_EventQueue *event_queue, NBN_Event ev) { + if (event_queue->count >= NBN_EVENT_QUEUE_CAPACITY) + return false; + + event_queue->events[event_queue->tail] = ev; + + event_queue->tail = (event_queue->tail + 1) % NBN_EVENT_QUEUE_CAPACITY; + event_queue->count++; + + return true; +} + +static bool EventQueue_IsEmpty(NBN_EventQueue *event_queue) { return event_queue->count == 0; } + +static bool EventQueue_Dequeue(NBN_EventQueue *event_queue, NBN_Event *ev) { + if (EventQueue_IsEmpty(event_queue)) + return false; + + memcpy(ev, &event_queue->events[event_queue->head], sizeof(NBN_Event)); + event_queue->head = (event_queue->head + 1) % NBN_EVENT_QUEUE_CAPACITY; + event_queue->count--; + + return true; +} + +// END DATA STRUCTURES +// =================================================== #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) @@ -398,6 +369,8 @@ static void PacketSimulator_Init(NBN_PacketSimulator *, NBN_Endpoint *); static int PacketSimulator_EnqueuePacket(NBN_PacketSimulator *, NBN_Packet *, NBN_Connection *); static void PacketSimulator_Start(NBN_PacketSimulator *); static void PacketSimulator_Stop(NBN_PacketSimulator *); +static int PacketSimulator_SendPacket(NBN_PacketSimulator *, NBN_Packet *, NBN_Connection *receiver); +static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimulator *); #endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ @@ -494,17 +467,6 @@ struct NBN_Driver { NBN_Driver_Implementation impl; }; -void NBN_Packet_InitWrite(NBN_Packet *, uint32_t, uint16_t, uint16_t, uint32_t); -NBN_PacketResult NBN_Packet_WriteMessage(NBN_Packet *, NBN_OutgoingMessage *); -int NBN_Packet_Seal(NBN_Packet *); -int NBN_Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); - -int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); -int NBN_Connection_FlushChannels(NBN_Endpoint *, NBN_Connection *, uint32_t, double); -bool NBN_Connection_CheckIfStale(NBN_Connection *, double); - -#pragma endregion /* NBN_Packet */ - #ifdef NBN_UDP #if defined(NBN_PLATFORM_WINDOWS) @@ -603,7 +565,23 @@ void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config); #endif // __EMSCRIPTEN__ -#pragma region Serialization +/** + * ====== LOGGING ====== + */ + +#define LogInfo(msg, ...) Log(NBN_LOG_INFO, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define LogWarning(msg, ...) Log(NBN_LOG_WARNING, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define LogError(msg, ...) Log(NBN_LOG_ERROR, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define LogDebug(msg, ...) Log(NBN_LOG_DEBUG, __FILE__, __LINE__, msg, ##__VA_ARGS__) + +static void Log(NBN_LogLevel level, const char *filename, int line, const char *msg, ...); + +// END OF LOGGING +// =================================================== + +/** + * ====== SERIALIZATION ====== + */ static union { uint64_t v; @@ -789,12 +767,20 @@ int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len) { return 0; } -#pragma endregion /* Serialization */ +// END OF SERIALIZATION +// =================================================== + +/** + * ====== PACKET ====== + */ -#pragma region NBN_Packet +static void Packet_InitWrite(NBN_Packet *, uint32_t, uint16_t, uint16_t, uint32_t); +static NBN_PacketResult Packet_WriteMessage(NBN_Packet *, NBN_OutgoingMessage *); +static int Packet_Seal(NBN_Packet *); +static int Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); -void NBN_Packet_InitWrite(NBN_Packet *packet, uint32_t protocol_id, uint16_t seq_number, uint16_t ack, - uint32_t ack_bits) { +static void Packet_InitWrite(NBN_Packet *packet, uint32_t protocol_id, uint16_t seq_number, uint16_t ack, + uint32_t ack_bits) { packet->header.protocol_id = protocol_id; packet->header.messages_count = 0; packet->header.seq_number = seq_number; @@ -809,7 +795,7 @@ void NBN_Packet_InitWrite(NBN_Packet *packet, uint32_t protocol_id, uint16_t seq memset(packet->buffer, 0, sizeof(packet->buffer)); } -NBN_PacketResult NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_OutgoingMessage *out_msg) { +static NBN_PacketResult Packet_WriteMessage(NBN_Packet *packet, NBN_OutgoingMessage *out_msg) { NBN_Message *message = &out_msg->message; LogDebug("Write message %d (type: %d, length: %d) to packet %d", out_msg->id, message->header.type, @@ -848,7 +834,7 @@ NBN_PacketResult NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_OutgoingMessage return NBN_PACKET_WRITE_OK; } -int NBN_Packet_Seal(NBN_Packet *packet) { +static int Packet_Seal(NBN_Packet *packet) { if (packet->mode != NBN_PACKET_MODE_WRITE) return NBN_ERROR; @@ -869,7 +855,7 @@ int NBN_Packet_Seal(NBN_Packet *packet) { return 0; } -int NBN_Packet_InitRead(NBN_Packet *packet, uint32_t protocol_id, unsigned int size) { +static int Packet_InitRead(NBN_Packet *packet, uint32_t protocol_id, unsigned int size) { if (size < NBN_PACKET_HEADER_SIZE || size > NBN_PACKET_MAX_SIZE) { return NBN_ERROR; } @@ -917,11 +903,19 @@ int NBN_Packet_InitRead(NBN_Packet *packet, uint32_t protocol_id, unsigned int s return 0; } -#pragma endregion /* NBN_Packet */ +// END OF PACKET +// =================================================== -#pragma region NBN_Channel +/** + * ====== CHANNEL ====== + */ -static unsigned int Channel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2); +static unsigned int Channel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2) { + if (SEQUENCE_NUMBER_GT(id1, id2)) + return (id1 >= id2) ? id1 - id2 : ((0xFFFF + 1) - id2) + id1; + else + return (id2 >= id1) ? id2 - id1 : (((0xFFFF + 1) - id1) + id2) % 0xFFFF; +} static void Channel_Init(NBN_Channel *channel, uint8_t id, NBN_ChannelMode type) { channel->id = id; @@ -1151,18 +1145,12 @@ static int Channel_OnOutgoingMessageAcked(NBN_Endpoint *endpoint, NBN_Channel *c return 0; } -static unsigned int Channel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2) { - if (SEQUENCE_NUMBER_GT(id1, id2)) - return (id1 >= id2) ? id1 - id2 : ((0xFFFF + 1) - id2) + id1; - else - return (id2 >= id1) ? id2 - id1 : (((0xFFFF + 1) - id1) + id2) % 0xFFFF; -} - -#pragma endregion /* NBN_Channel */ - -#pragma region NBN_Connection +// END OF CHANNEL +// =================================================== -static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *, uint8_t, uint8_t); +/** + * ====== CONNECTION ====== + */ static uint32_t Connection_BuildPacketAckBits(NBN_Connection *); static int Connection_DecodePacketHeader(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); @@ -1178,9 +1166,12 @@ static void Connection_UpdateAveragePing(NBN_Connection *, double); static void Connection_UpdateAveragePacketLoss(NBN_Connection *, uint16_t); static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *, float); static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *, double); +static int Connection_ProcessReceivedPacket(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); +static int Connection_FlushChannels(NBN_Endpoint *, NBN_Connection *, uint32_t, double); +static bool Connection_CheckIfStale(NBN_Connection *, double); -int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, - double time) { +static int Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, + double time) { if (Connection_DecodePacketHeader(endpoint, connection, packet, time) < 0) { LogError("Failed to decode packet %d header", packet->header.seq_number); @@ -1236,8 +1227,8 @@ int NBN_Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection return 0; } -int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connection, uint32_t protocol_id, - double time) { +static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connection, uint32_t protocol_id, + double time) { LogDebug("Flushing all channels"); NBN_Packet packet = {0}; @@ -1261,7 +1252,7 @@ int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connect NBN_Message *message = &out_msg.message; uint16_t msg_id = out_msg.id; bool message_sent = false; - NBN_PacketResult ret = NBN_Packet_WriteMessage(&packet, &out_msg); + NBN_PacketResult ret = Packet_WriteMessage(&packet, &out_msg); if (ret == NBN_PACKET_WRITE_OK) { message_sent = true; @@ -1277,7 +1268,7 @@ int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connect Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); - NBN_PacketResult ret = NBN_Packet_WriteMessage(&packet, &out_msg); + NBN_PacketResult ret = Packet_WriteMessage(&packet, &out_msg); if (ret != NBN_PACKET_WRITE_OK) { LogError("Failed to send packet %d", packet.header.seq_number); @@ -1327,7 +1318,7 @@ int NBN_Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connect return 0; } -bool NBN_Connection_CheckIfStale(NBN_Connection *connection, double time) { +static bool Connection_CheckIfStale(NBN_Connection *connection, double time) { #if defined(NBN_DEBUG) && defined(NBN_DISABLE_STALE_CONNECTION_DETECTION) /* When testing under bad network conditions (in soak test for instance), we don't want to deal with stale connections */ @@ -1405,8 +1396,8 @@ static int Connection_AckPacket(NBN_Endpoint *endpoint, NBN_Connection *connecti static void Connection_InitOutgoingPacket(NBN_Connection *connection, uint32_t protocol_id, NBN_Packet *outgoing_packet, NBN_PacketEntry **packet_entry) { - NBN_Packet_InitWrite(outgoing_packet, protocol_id, connection->next_packet_seq_number++, - connection->last_received_packet_seq_number, Connection_BuildPacketAckBits(connection)); + Packet_InitWrite(outgoing_packet, protocol_id, connection->next_packet_seq_number++, + connection->last_received_packet_seq_number, Connection_BuildPacketAckBits(connection)); *packet_entry = Connection_InsertOutgoingPacketEntry(connection, outgoing_packet->header.seq_number); } @@ -1467,7 +1458,7 @@ static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, NBN_Assert(packet_entry->messages_count == packet->header.messages_count); - if (NBN_Packet_Seal(packet) < 0) { + if (Packet_Seal(packet) < 0) { LogError("Failed to seal packet"); return NBN_ERROR; @@ -1587,44 +1578,12 @@ static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *connection connection->downloaded_bytes = 0; } -#pragma endregion /* NBN_Connection */ - -#pragma region NBN_EventQueue - -void NBN_EventQueue_Init(NBN_EventQueue *event_queue) { - event_queue->head = 0; - event_queue->tail = 0; - event_queue->count = 0; -} - -bool NBN_EventQueue_Enqueue(NBN_EventQueue *event_queue, NBN_Event ev) { - if (event_queue->count >= NBN_EVENT_QUEUE_CAPACITY) - return false; - - event_queue->events[event_queue->tail] = ev; - - event_queue->tail = (event_queue->tail + 1) % NBN_EVENT_QUEUE_CAPACITY; - event_queue->count++; - - return true; -} - -bool NBN_EventQueue_Dequeue(NBN_EventQueue *event_queue, NBN_Event *ev) { - if (NBN_EventQueue_IsEmpty(event_queue)) - return false; +// END OF CONNECTION +// =================================================== - memcpy(ev, &event_queue->events[event_queue->head], sizeof(NBN_Event)); - event_queue->head = (event_queue->head + 1) % NBN_EVENT_QUEUE_CAPACITY; - event_queue->count--; - - return true; -} - -bool NBN_EventQueue_IsEmpty(NBN_EventQueue *event_queue) { return event_queue->count == 0; } - -#pragma endregion /* NBN_EventQueue */ - -#pragma region Endpoint +/** + * ====== ENDPOINT ====== + */ static void Endpoint_Init(NBN_Endpoint *, uint32_t, bool, NBN_ChannelMode[NBN_CHANNEL_COUNT]); static void Endpoint_Deinit(NBN_Endpoint *); @@ -1633,6 +1592,7 @@ static uint32_t Endpoint_BuildProtocolId(const char *); static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *, NBN_Packet *, NBN_Connection *); static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN_Message *); static void Endpoint_UpdateTime(NBN_Endpoint *); +static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *, uint8_t, uint8_t); static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_server, NBN_ChannelMode channel_modes[NBN_CHANNEL_COUNT]) { @@ -1646,7 +1606,7 @@ static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_ memcpy(&endpoint->channel_modes, channel_modes, sizeof(NBN_ChannelMode) * NBN_CHANNEL_COUNT); - NBN_EventQueue_Init(&endpoint->event_queue); + EventQueue_Init(&endpoint->event_queue); #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) PacketSimulator_Init(&endpoint->packet_simulator, endpoint); @@ -1731,7 +1691,7 @@ static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *pa LogDebug("Received packet %d (conn id: %d, ack: %d, messages count: %d)", packet->header.seq_number, connection->handle.id, packet->header.ack, packet->header.messages_count); - if (NBN_Connection_ProcessReceivedPacket(endpoint, connection, packet, endpoint->time) < 0) + if (Connection_ProcessReceivedPacket(endpoint, connection, packet, endpoint->time) < 0) return NBN_ERROR; connection->last_recv_packet_time = endpoint->time; @@ -1789,22 +1749,8 @@ static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) { #endif // NBN_PLATFORM_WINDOWS } -#pragma endregion /* NBN_Endpoint */ - -#pragma region Network driver - -static void ClientDriver_OnPacketReceived(NBN_Packet *packet); -static void ServerDriver_OnClientConnected(NBN_Connection *); -static int ServerDriver_OnClientPacketReceived(NBN_Packet *); - -#pragma endregion /* Network driver */ - -#pragma region NBN_GameClient - -static int GameClient_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); -static NBN_Client_Event GameClient_HandleEvent(void); -static NBN_Client_Event GameClient_HandleMessageReceivedEvent(void); -static NBN_Connection *CreateServerConnection(NBN_Driver_ID driver_id); +// END OF ENDPOINT +// =================================================== static void InitChannelModes(NBN_ChannelMode channel_modes[NBN_CHANNEL_COUNT]) { // channel 0 is always unreliable, channel 1 is always reliable @@ -1818,6 +1764,15 @@ static void InitChannelModes(NBN_ChannelMode channel_modes[NBN_CHANNEL_COUNT]) { } } +/** + * ====== CLIENT ====== + */ + +static int GameClient_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); +static NBN_Client_Event GameClient_HandleEvent(void); +static NBN_Client_Event GameClient_HandleMessageReceivedEvent(void); +static NBN_Connection *CreateServerConnection(NBN_Driver_ID driver_id); + void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port) { nbn_game_client.config = (NBN_GameClient_Config){.protocol_name = protocol_name, .host = host, .port = port}; @@ -1996,8 +1951,8 @@ NBN_Client_Event NBN_GameClient_Poll(void) { if (nbn_game_client.server_connection->is_stale) return NBN_CLIENT_NO_EVENT; - if (NBN_EventQueue_IsEmpty(&endpoint->event_queue)) { - if (NBN_Connection_CheckIfStale(nbn_game_client.server_connection, nbn_game_client.endpoint.time)) { + if (EventQueue_IsEmpty(&endpoint->event_queue)) { + if (Connection_CheckIfStale(nbn_game_client.server_connection, nbn_game_client.endpoint.time)) { nbn_game_client.server_connection->is_stale = true; nbn_game_client.is_connected = false; @@ -2008,7 +1963,7 @@ NBN_Client_Event NBN_GameClient_Poll(void) { e.type = NBN_CLIENT_DISCONNECTED; e.data.connection = (NBN_Connection *)NULL; - if (!NBN_EventQueue_Enqueue(&endpoint->event_queue, e)) + if (!EventQueue_Enqueue(&endpoint->event_queue, e)) return NBN_ERROR; } else { if (ReadPacketsFromClientDrivers() < 0) { @@ -2040,14 +1995,14 @@ NBN_Client_Event NBN_GameClient_Poll(void) { } } - bool ret = NBN_EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_client.last_event); + bool ret = EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_client.last_event); return ret ? GameClient_HandleEvent() : NBN_CLIENT_NO_EVENT; } int NBN_GameClient_Flush(void) { - return NBN_Connection_FlushChannels(&nbn_game_client.endpoint, nbn_game_client.server_connection, - nbn_game_client.endpoint.protocol_id, nbn_game_client.endpoint.time); + return Connection_FlushChannels(&nbn_game_client.endpoint, nbn_game_client.server_connection, + nbn_game_client.endpoint.protocol_id, nbn_game_client.endpoint.time); } NBN_Writer *NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id) { @@ -2135,7 +2090,7 @@ static int GameClient_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio ev.data.message_info = msg_info; - if (!NBN_EventQueue_Enqueue(&nbn_game_client.endpoint.event_queue, ev)) + if (!EventQueue_Enqueue(&nbn_game_client.endpoint.event_queue, ev)) return NBN_ERROR; return 0; @@ -2208,10 +2163,6 @@ static NBN_Client_Event GameClient_HandleMessageReceivedEvent(void) { return ret; } -#pragma endregion /* NBN_GameClient */ - -#pragma region Game client driver - static void ClientDriver_OnPacketReceived(NBN_Packet *packet) { if (Endpoint_ProcessReceivedPacket(&nbn_game_client.endpoint, packet, nbn_game_client.server_connection) < 0) { // packets from the server should always be valid @@ -2220,9 +2171,12 @@ static void ClientDriver_OnPacketReceived(NBN_Packet *packet) { } } -#pragma endregion /* Game Client driver */ +// END OF CLIENT +// =================================================== -#pragma region NBN_GameServer +/** + * ====== SERVER ====== + */ static int GameServer_EnqueueMessageFor(NBN_Connection *client, NBN_Message *message); static void GameServer_AddClient(NBN_Connection *); @@ -2344,7 +2298,7 @@ static NBN_Connection_ID NBN_BuildConnectionHash(NBN_Connection_ID id, NBN_Drive return ((NBN_Connection_ID)driver_byte << 56) | id; } -NBN_ConnectionHandle *NBN_GameServer_FindConnection(NBN_Connection_ID id) { +NBN_ConnectionHandle *NBN_GameServer_GetConnection(NBN_Connection_ID id) { return (NBN_ConnectionHandle *)hmget(nbn_game_server.clients, id); } @@ -2383,7 +2337,7 @@ NBN_Server_Event NBN_GameServer_Poll(void) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - if (NBN_EventQueue_IsEmpty(&endpoint->event_queue)) { + if (EventQueue_IsEmpty(&endpoint->event_queue)) { if (GameServer_CloseStaleClientConnections() < 0) return NBN_ERROR; @@ -2422,7 +2376,7 @@ NBN_Server_Event NBN_GameServer_Poll(void) { NBN_Server_Event ev; - while (NBN_EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_server.last_event)) { + while (EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_server.last_event)) { if (GameServer_HandleEvent(&ev)) { return ev; } @@ -2442,8 +2396,8 @@ int NBN_GameServer_Flush(void) { NBN_Assert(!(client->is_closed && client->is_stale)); if (!client->is_stale && - NBN_Connection_FlushChannels(&nbn_game_server.endpoint, client, nbn_game_server.endpoint.protocol_id, - nbn_game_server.endpoint.time) < 0) { + Connection_FlushChannels(&nbn_game_server.endpoint, client, nbn_game_server.endpoint.protocol_id, + nbn_game_server.endpoint.time) < 0) { return NBN_ERROR; } @@ -2655,7 +2609,7 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool e.type = NBN_CLIENT_DISCONNECTED; e.data.disconnection = (NBN_DisconnectionInfo){client->handle.id, client->handle.user_data}; - if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, e)) + if (!EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, e)) return NBN_ERROR; } } @@ -2729,7 +2683,7 @@ static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio client->handle.id); ev.data.message_info = msg_info; - if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, ev)) + if (!EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, ev)) return NBN_ERROR; return 0; @@ -2739,7 +2693,7 @@ static int GameServer_CloseStaleClientConnections(void) { for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { NBN_Connection *client = nbn_game_server.clients[i].value; - if (!client->is_stale && NBN_Connection_CheckIfStale(client, nbn_game_server.endpoint.time)) { + if (!client->is_stale && Connection_CheckIfStale(client, nbn_game_server.endpoint.time)) { LogInfo("Client %lld connection is stale, closing it.", client->handle.id); client->is_stale = true; @@ -2891,7 +2845,7 @@ static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev) { e.type = NBN_SERVER_NEW_CONNECTION; e.data.connection = sender; - if (!NBN_EventQueue_Enqueue(&endpoint->event_queue, e)) { + if (!EventQueue_Enqueue(&endpoint->event_queue, e)) { *ev = NBN_ERROR; return true; } @@ -2900,10 +2854,6 @@ static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev) { return true; } -#pragma endregion /* NBN_GameServer */ - -#pragma region Game server driver - static void ServerDriver_OnClientConnected(NBN_Connection *client) { GameServer_AddClient(client); } static int ServerDriver_OnClientPacketReceived(NBN_Packet *packet) { @@ -2917,10 +2867,11 @@ static int ServerDriver_OnClientPacketReceived(NBN_Packet *packet) { return 0; } -#pragma endregion /* Game server driver */ +// END OF SERVER +// =================================================== /** - * ======================= DRIVER IMPLEMENTATIONS ======================= * + * ====== UDP DRIVER ====== * */ #ifdef NBN_UDP @@ -3002,7 +2953,7 @@ static NBN_Connection_ID UDP_BuildConnectionID(NBN_IPAddress address) { static NBN_Connection *UDP_FindOrCreateClientConnectionByAddress(NBN_IPAddress address) { NBN_Connection_ID conn_id = UDP_BuildConnectionID(address); conn_id = NBN_BuildConnectionHash(conn_id, NBN_DRIVER_UDP); - NBN_ConnectionHandle *handle = NBN_GameServer_FindConnection(conn_id); + NBN_ConnectionHandle *handle = NBN_GameServer_GetConnection(conn_id); if (handle) { return HANDLE_TO_CONN(handle); @@ -3055,7 +3006,7 @@ static char *UDP_GetLastErrorMessage(void) { #endif } -int UDP_Server_Start(NBN_GameServer *server, uint16_t port) { +static int UDP_Server_Start(NBN_GameServer *server, uint16_t port) { if (UDP_InitSocket() < 0) return NBN_ERROR; @@ -3065,9 +3016,9 @@ int UDP_Server_Start(NBN_GameServer *server, uint16_t port) { return 0; } -void UDP_Server_Stop(NBN_GameServer *server) { UDP_DeinitSocket(); } +static void UDP_Server_Stop(NBN_GameServer *server) { UDP_DeinitSocket(); } -int UDP_Server_RecvPackets(NBN_GameServer *server) { +static int UDP_Server_RecvPackets(NBN_GameServer *server) { static NBN_Packet packet = {0}; SOCKADDR_IN src_addr; socklen_t src_addr_len = sizeof(src_addr); @@ -3082,7 +3033,7 @@ int UDP_Server_RecvPackets(NBN_GameServer *server) { if (bytes <= NBN_PACKET_HEADER_SIZE) continue; - if (NBN_Packet_InitRead(&packet, server->endpoint.protocol_id, bytes) < 0) { + if (Packet_InitRead(&packet, server->endpoint.protocol_id, bytes) < 0) { LogDebug("Discarded invalid packet"); continue; } @@ -3121,7 +3072,7 @@ static int UDP_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, N return 0; } -int UDP_Client_Start(NBN_GameClient *client, const char *host, uint16_t port) { +static int UDP_Client_Start(NBN_GameClient *client, const char *host, uint16_t port) { NBN_IPAddress *ip_address = &client->server_connection->driver_data.udp.ip_address; UDP_ParseIpAddress(host, port, ip_address); @@ -3135,9 +3086,9 @@ int UDP_Client_Start(NBN_GameClient *client, const char *host, uint16_t port) { return 0; } -void UDP_Client_Stop(NBN_GameClient *client) { UDP_DeinitSocket(); } +static void UDP_Client_Stop(NBN_GameClient *client) { UDP_DeinitSocket(); } -int UDP_Client_RecvPackets(NBN_GameClient *client) { +static int UDP_Client_RecvPackets(NBN_GameClient *client) { NBN_IPAddress server_address = client->server_connection->driver_data.udp.ip_address; static NBN_Packet packet = {0}; SOCKADDR_IN src_addr; @@ -3159,7 +3110,7 @@ int UDP_Client_RecvPackets(NBN_GameClient *client) { if (host != server_address.host || port != server_address.port) continue; - if (NBN_Packet_InitRead(&packet, client->endpoint.protocol_id, bytes) < 0) { + if (Packet_InitRead(&packet, client->endpoint.protocol_id, bytes) < 0) { LogDebug("Discarded invalid packet"); continue; } @@ -3192,6 +3143,13 @@ static int UDP_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet) { #endif // NBN_UDP +// END OF UDP DRIVER +// =================================================== + +/** + * ====== WEBRTC JS DRIVER ====== * + */ + #ifdef __EMSCRIPTEN__ /** @@ -3234,7 +3192,7 @@ static int WebRTC_Server_RecvPackets(NBN_GameServer *server) { while ((len = __js_game_server_dequeue_packet(&peer_id, packet.buffer)) > 0) { NBN_Connection_ID conn_id = NBN_BuildConnectionHash(peer_id, NBN_DRIVER_WEBRTC_EMSCRIPTEN); - NBN_ConnectionHandle *handle = NBN_GameServer_FindConnection(conn_id); + NBN_ConnectionHandle *handle = NBN_GameServer_GetConnection(conn_id); NBN_Connection *conn = NULL; if (handle == NULL) { @@ -3248,7 +3206,7 @@ static int WebRTC_Server_RecvPackets(NBN_GameServer *server) { conn = HANDLE_TO_CONN(handle); } - if (NBN_Packet_InitRead(&packet, server->endpoint.protocol_id, len) < 0) + if (Packet_InitRead(&packet, server->endpoint.protocol_id, len) < 0) continue; packet.sender = conn; @@ -3287,7 +3245,7 @@ static int WebRTC_Client_RecvPackets(NBN_GameClient *client) { unsigned int len; while ((len = __js_game_client_dequeue_packet(packet.buffer)) > 0) { - if (NBN_Packet_InitRead(&packet, client->endpoint.protocol_id, len) < 0) + if (Packet_InitRead(&packet, client->endpoint.protocol_id, len) < 0) continue; packet.sender = client->server_connection; @@ -3304,12 +3262,13 @@ static int WebRTC_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet) #endif // __EMSCRIPTEN__ +// END OF WEBRTC JS DRIVER +// =================================================== + /** - * ====================================================================== * + * ====== PACKET SIMULATOR ====== */ -#pragma region Packet simulator - #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) #define RAND_RATIO_BETWEEN(min, max) (((rand() % (int)((max * 100.f) - (min * 100.f) + 1)) + (min * 100.f)) / 100.f) @@ -3335,10 +3294,7 @@ void NBN_GameServer_SetPacketDuplication(float v) { nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; } -static int PacketSimulator_SendPacket(NBN_PacketSimulator *, NBN_Packet *, NBN_Connection *receiver); -static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimulator *); - -void PacketSimulator_Init(NBN_PacketSimulator *packet_simulator, NBN_Endpoint *endpoint) { +static void PacketSimulator_Init(NBN_PacketSimulator *packet_simulator, NBN_Endpoint *endpoint) { packet_simulator->endpoint = endpoint; packet_simulator->running = false; packet_simulator->ping = 0; @@ -3357,7 +3313,8 @@ void PacketSimulator_Init(NBN_PacketSimulator *packet_simulator, NBN_Endpoint *e #endif } -int PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN_Packet *packet, NBN_Connection *receiver) { +static int PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN_Packet *packet, + NBN_Connection *receiver) { #ifdef NBN_PLATFORM_WINDOWS WaitForSingleObject(packet_simulator->queue_mutex, INFINITE); #else @@ -3406,7 +3363,7 @@ int PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN_Pac return 0; } -void PacketSimulator_Start(NBN_PacketSimulator *packet_simulator) { +static void PacketSimulator_Start(NBN_PacketSimulator *packet_simulator) { #ifdef NBN_PLATFORM_WINDOWS packet_simulator->thread = CreateThread(NULL, 0, PacketSimulator_Routine, packet_simulator, 0, NULL); #else @@ -3540,4 +3497,60 @@ static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimu #endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ -#pragma endregion /* Packet simulator */ +// END OF PACKET SIMULATOR +// =================================================== + +/** + * ====== LOGGING ====== + */ + +static int log_level = NBN_LOG_INFO; + +void NBN_SetLogLevel(NBN_LogLevel level) { log_level = level; } + +#ifdef NBN_LOG_CUSTOM_FUNCTION + +extern void Log(NBN_LogLevel level, const char *filename, int line, const char *msg, ...); + +#else + +/** + * Default logging function + */ + +/** + * Copyright (c) 2017 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `log.c` for details. + */ + +#include +#include + +static const char *level_names[] = {"ERROR", "INFO", "WARNING", "DEBUG"}; + +static void Log(NBN_LogLevel level, const char *filename, int line, const char *msg, ...) { + if (log_level < level) { + return; + } + + time_t t = time(NULL); + struct tm *lt = localtime(&t); + FILE *fp = level == NBN_LOG_ERROR ? stderr : stdout; + + va_list args; + char buf[32]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; + fprintf(fp, "%s %-5s %s:%d: ", buf, level_names[level], filename, line); + va_start(args, msg); + vfprintf(fp, msg, args); + va_end(args); + fprintf(fp, "\n"); + fflush(fp); +} + +#endif // NBN_CUSTOM_LOG_FUNCTION + +// END OF LOGGING +// =================================================== diff --git a/nbnet.h b/nbnet.h index e31ebee..ae748dd 100644 --- a/nbnet.h +++ b/nbnet.h @@ -31,7 +31,7 @@ #define NBN_ERROR -1 /** - * - CONFIGURATION - + * ====== CONFIGURATION ====== * * The macros below can be redefined to change how the library behaves. */ @@ -353,7 +353,7 @@ int NBN_GameServer_Start(void); void NBN_GameServer_Stop(void); // TODO: doc -NBN_ConnectionHandle *NBN_GameServer_FindConnection(NBN_Connection_ID); +NBN_ConnectionHandle *NBN_GameServer_GetConnection(NBN_Connection_ID); // TODO: doc unsigned int NBN_GameServer_GetClientCount(void); From 6201a16be7676480c0b6958750c0d84fa456351c Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Wed, 11 Feb 2026 20:13:06 +0100 Subject: [PATCH 56/85] add extern "C" when compiling as cpp --- nbnet.h | 176 ++++++++++++++++++++++++-------------------------------- 1 file changed, 75 insertions(+), 101 deletions(-) diff --git a/nbnet.h b/nbnet.h index ae748dd..7e86cd4 100644 --- a/nbnet.h +++ b/nbnet.h @@ -25,17 +25,15 @@ #ifndef NBNET_H #define NBNET_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include #define NBN_ERROR -1 -/** - * ====== CONFIGURATION ====== - * - * The macros below can be redefined to change how the library behaves. - */ - // TODO: doc #ifndef NBN_SERVER_INITIAL_DATA_MAX_SIZE #define NBN_SERVER_INITIAL_DATA_MAX_SIZE 256 @@ -73,66 +71,6 @@ #define NBN_CONNECTION_STALE_TIME_THRESHOLD 3 #endif -/* ========================================================================== */ - -typedef enum NBN_LogLevel { NBN_LOG_ERROR, NBN_LOG_INFO, NBN_LOG_WARNING, NBN_LOG_DEBUG } NBN_LogLevel; - -void NBN_SetLogLevel(NBN_LogLevel); - -/** - * - SERIALIZATION API - - * - * Functions to read and write nbnet messages. - * - * You'll never need to create a reader or writer yourself - nbnet will provide pointers to - * NBN_Reader or NBN_Writer, see the client and server API sections. - * - * NBN_Writer_Write* functions will assert if trying to write outside the buffer. - * - * NBN_Reader_Read* functions returns NBN_ERROR if trying to read outside the buffer. - * If read successully, 0 is returned and the value pointer points to the read value. - * - * Write and Read functions account for endianness. - */ - -typedef struct NBN_Writer { - uint8_t *buffer; - unsigned int length; - unsigned int position; -} NBN_Writer; - -typedef struct NBN_Reader { - uint8_t *buffer; - unsigned int length; - unsigned int position; -} NBN_Reader; - -void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length); -void NBN_Writer_WriteInt8(NBN_Writer *writer, int8_t value); -void NBN_Writer_WriteInt32(NBN_Writer *writer, int32_t value); -void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value); -void NBN_Writer_WriteUInt16(NBN_Writer *writer, uint16_t value); -void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value); -void NBN_Writer_WriteUInt64(NBN_Writer *writer, uint64_t value); -void NBN_Writer_WriteFloat(NBN_Writer *writer, float value); -void NBN_Writer_WriteBool(NBN_Writer *writer, bool value); -void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int length); -void NBN_Writer_WriteString(NBN_Writer *writer, const char *str, unsigned int max_len); - -void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length); -int NBN_Reader_ReadInt8(NBN_Reader *reader, int8_t *value); -int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value); -int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value); -int NBN_Reader_ReadUInt16(NBN_Reader *reader, uint16_t *value); -int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value); -int NBN_Reader_ReadUInt64(NBN_Reader *reader, uint64_t *value); -int NBN_Reader_ReadFloat(NBN_Reader *reader, float *value); -int NBN_Reader_ReadBool(NBN_Reader *reader, bool *value); -int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length); -int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len); - -/* ============================================= */ - typedef uint64_t NBN_Connection_ID; typedef struct NBN_ConnectionHandle { @@ -183,10 +121,6 @@ typedef struct NBN_MessageInfo { // TODO: doc typedef enum NBN_ChannelMode { NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_RELIABLE } NBN_ChannelMode; -/** - * - CLIENT API - - */ - typedef enum NBN_Client_Event { NBN_CLIENT_ERROR = NBN_ERROR, @@ -202,6 +136,73 @@ typedef enum NBN_Client_Event { NBN_CLIENT_MESSAGE_RECEIVED } NBN_Client_Event; +typedef enum NBN_Server_Event { + NBN_SERVER_ERROR = NBN_ERROR, + + NBN_SERVER_NO_EVENT = 0, + + /* A new client has connected */ + NBN_SERVER_NEW_CONNECTION, + + /* A client has disconnected */ + NBN_SERVER_DISCONNECTION, + + /* A message has been received from a client */ + NBN_SERVER_MESSAGE_RECEIVED +} NBN_Server_Event; + +typedef struct NBN_GameServerStats { + float upload_bandwidth; /* Total upload bandwith of the game server */ + float download_bandwidth; /* Total download bandwith of the game server */ +} NBN_GameServerStats; + +typedef struct NBN_DisconnectionInfo { + NBN_Connection_ID conn_id; /* ID if the disconnected connection */ + void *user_data; /* Pointer to user-defined data associated with this connection */ +} NBN_DisconnectionInfo; + +typedef unsigned int NBN_Client_Iterator; + +typedef struct NBN_Writer { + uint8_t *buffer; + unsigned int length; + unsigned int position; +} NBN_Writer; + +typedef struct NBN_Reader { + uint8_t *buffer; + unsigned int length; + unsigned int position; +} NBN_Reader; + +typedef enum NBN_LogLevel { NBN_LOG_ERROR, NBN_LOG_INFO, NBN_LOG_WARNING, NBN_LOG_DEBUG } NBN_LogLevel; + +void NBN_SetLogLevel(NBN_LogLevel); + +void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length); +void NBN_Writer_WriteInt8(NBN_Writer *writer, int8_t value); +void NBN_Writer_WriteInt32(NBN_Writer *writer, int32_t value); +void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value); +void NBN_Writer_WriteUInt16(NBN_Writer *writer, uint16_t value); +void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value); +void NBN_Writer_WriteUInt64(NBN_Writer *writer, uint64_t value); +void NBN_Writer_WriteFloat(NBN_Writer *writer, float value); +void NBN_Writer_WriteBool(NBN_Writer *writer, bool value); +void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int length); +void NBN_Writer_WriteString(NBN_Writer *writer, const char *str, unsigned int max_len); + +void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length); +int NBN_Reader_ReadInt8(NBN_Reader *reader, int8_t *value); +int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value); +int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value); +int NBN_Reader_ReadUInt16(NBN_Reader *reader, uint16_t *value); +int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value); +int NBN_Reader_ReadUInt64(NBN_Reader *reader, uint64_t *value); +int NBN_Reader_ReadFloat(NBN_Reader *reader, float *value); +int NBN_Reader_ReadBool(NBN_Reader *reader, bool *value); +int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length); +int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len); + /** * Initialize the game client with minimal configuration. * @@ -299,35 +300,6 @@ int NBN_GameClient_GetServerCloseCode(void); */ bool NBN_GameClient_IsConnected(void); -/* ============================================= */ - -typedef enum NBN_Server_Event { - NBN_SERVER_ERROR = NBN_ERROR, - - NBN_SERVER_NO_EVENT = 0, - - /* A new client has connected */ - NBN_SERVER_NEW_CONNECTION, - - /* A client has disconnected */ - NBN_SERVER_DISCONNECTION, - - /* A message has been received from a client */ - NBN_SERVER_MESSAGE_RECEIVED -} NBN_Server_Event; - -typedef struct NBN_GameServerStats { - float upload_bandwidth; /* Total upload bandwith of the game server */ - float download_bandwidth; /* Total download bandwith of the game server */ -} NBN_GameServerStats; - -typedef struct NBN_DisconnectionInfo { - NBN_Connection_ID conn_id; /* ID if the disconnected connection */ - void *user_data; /* Pointer to user-defined data associated with this connection */ -} NBN_DisconnectionInfo; - -typedef unsigned int NBN_Client_Iterator; - /** * Initialize the game server with minimal configuration. * @@ -497,8 +469,6 @@ typedef struct NBN_WebRTC_Config { void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config); -/* ========================================================================== */ - #endif // __EMSCRIPTEN__ #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) @@ -531,4 +501,8 @@ void NBN_GameServer_SetPacketDuplication(float v); #endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ +#ifdef __cplusplus +} +#endif + #endif /* NBNET_H */ From 7a6220e75b8b660a4cf34d3383f01a6a07c480db Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sat, 14 Feb 2026 07:04:25 +0100 Subject: [PATCH 57/85] webrtc native wip --- .github/workflows/nbnet.yml | 2 +- nbnet.c | 561 +++++++++++++++++++++++++++++++++++- nbnet.h | 33 ++- net_drivers/webrtc_native.h | 2 +- soak/client.c | 2 - 5 files changed, 578 insertions(+), 22 deletions(-) diff --git a/.github/workflows/nbnet.yml b/.github/workflows/nbnet.yml index 530b0e7..7ef3341 100644 --- a/.github/workflows/nbnet.yml +++ b/.github/workflows/nbnet.yml @@ -202,7 +202,7 @@ jobs: cd soak mkdir build cd build - cmake -DLIBDATACHANNEL_LIBRARY_PATH=${{ github.workspace }}/libdatachannel/build/libdatachannel.so -DLIBDATACHANNEL_INCLUDE_PATH=${{ github.workspace }}/libdatachannel/include -DWEBRTC_NATIVE=ON .. + cmake -DLIBDATACHANNEL_LIBRARY_PATH=${{ github.workspace }}/libdatachannel/build/libdatachannel.so -DLIBDATACHANNEL_INCLUDE_PATH=${{ github.workspace }}/libdatachannel/include -DWEBRTC_NATIVE=ON -DUDP=ON .. make - name: Compile soak test (web) run: ./bin/github-actions/build_soak_web.sh diff --git a/nbnet.c b/nbnet.c index 1539563..bcc3cb8 100644 --- a/nbnet.c +++ b/nbnet.c @@ -26,11 +26,12 @@ #include #include "nbnet.h" +#include "rtc/rtc.h" #define STB_DS_IMPLEMENTATION #include "stb_ds.h" -#include +#include #include #include #include @@ -222,8 +223,18 @@ typedef struct NBN_IPAddress { uint16_t port; } NBN_IPAddress; +#ifdef __EMSCRIPTEN__ + typedef uint32_t NBN_WebRTC_Peer_ID; +#endif + +#ifdef NBN_WEBRTC_NATIVE + +typedef int NBN_WebRTC_Peer_ID; + +#endif + struct NBN_Connection { NBN_ConnectionHandle handle; double last_recv_packet_time; /* Used to detect stale connections */ @@ -241,11 +252,21 @@ struct NBN_Connection { /* Driver-related data attached to the connection */ union { +#ifdef NBN_UDP struct { NBN_IPAddress ip_address; } udp; +#endif // NBN_UDP - NBN_WebRTC_Peer_ID peer_id; +#if defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + struct { + NBN_WebRTC_Peer_ID peer_id; +#ifdef NBN_WEBRTC_NATIVE + int channel_id; + int ws; +#endif // NBN_WEBRTC_NATIVE + } webrtc; +#endif // defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) } driver_data; /* @@ -459,7 +480,7 @@ typedef struct NBN_Driver_Implementation { NBN_Driver_Func_ServerCleanupConnection serv_cleanup_connection; } NBN_Driver_Implementation; -enum NBN_Driver_ID { NBN_DRIVER_UDP = 0x01, NBN_DRIVER_WEBRTC_EMSCRIPTEN }; +enum NBN_Driver_ID { NBN_DRIVER_UDP = 0x01, NBN_DRIVER_WEBRTC_EMSCRIPTEN = 0x02, NBN_DRIVER_WEBRTC_NATIVE = 0x03 }; struct NBN_Driver { int id; @@ -530,6 +551,10 @@ static SOCKET nbn_udp_sock; #error "Cannot compile UDP driver with emscripten" #endif +#ifdef NBN_WEBRTC_NATIVE +#error "Cannot compile native WebRTC driver with emscripten" +#endif + // TODO: add a check for webrtc native as well #include @@ -561,8 +586,6 @@ static NBN_Driver nbn_webrtc_em_driver = {.name = "WebRTC_EMSCRIPTEN", static NBN_WebRTC_Config nbn_wrtc_cfg = {.enable_tls = false, .cert_path = NULL, .key_path = NULL}; -void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config); - #endif // __EMSCRIPTEN__ /** @@ -572,7 +595,14 @@ void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config); #define LogInfo(msg, ...) Log(NBN_LOG_INFO, __FILE__, __LINE__, msg, ##__VA_ARGS__) #define LogWarning(msg, ...) Log(NBN_LOG_WARNING, __FILE__, __LINE__, msg, ##__VA_ARGS__) #define LogError(msg, ...) Log(NBN_LOG_ERROR, __FILE__, __LINE__, msg, ##__VA_ARGS__) + +#ifdef NBN_DEBUG #define LogDebug(msg, ...) Log(NBN_LOG_DEBUG, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define LogDebug(...) \ + ; \ + ; +#endif // NBN_DEBUG static void Log(NBN_LogLevel level, const char *filename, int line, const char *msg, ...); @@ -1197,7 +1227,6 @@ static int Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connecti LogDebug("Reading message number %d from packet %d", i, packet->header.seq_number); static NBN_Message message = {0}; - message.type = NBN_INCOMING_MESSAGE; int msg_len = Connection_ReadNextMessageFromBuffer(endpoint, &msg_reader, &message); if (msg_len < 0) { @@ -1206,6 +1235,8 @@ static int Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connecti return NBN_ERROR; } + message.type = NBN_INCOMING_MESSAGE; + uint8_t channel_id = message.header.channel_id; if (channel_id > NBN_CHANNEL_COUNT - 1) { @@ -1944,10 +1975,10 @@ static int ReadPacketsFromClientDrivers(void) { } NBN_Client_Event NBN_GameClient_Poll(void) { - Endpoint_UpdateTime(&nbn_game_client.endpoint); - NBN_Endpoint *endpoint = &nbn_game_client.endpoint; + Endpoint_UpdateTime(endpoint); + if (nbn_game_client.server_connection->is_stale) return NBN_CLIENT_NO_EVENT; @@ -3147,7 +3178,7 @@ static int UDP_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet) { // =================================================== /** - * ====== WEBRTC JS DRIVER ====== * + * ====== WEBRTC EMSCRIPTEN DRIVER ====== * */ #ifdef __EMSCRIPTEN__ @@ -3199,7 +3230,7 @@ static int WebRTC_Server_RecvPackets(NBN_GameServer *server) { LogInfo("Peer %d has connected", peer_id); conn = CreateClientConnection(NBN_DRIVER_WEBRTC_EMSCRIPTEN, conn_id); - conn->driver_data.peer_id = peer_id; + conn->driver_data.webrtc.peer_id = peer_id; ServerDriver_OnClientConnected(conn); } else { @@ -3218,13 +3249,13 @@ static int WebRTC_Server_RecvPackets(NBN_GameServer *server) { } static void WebRTC_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *conn) { - assert(conn != NULL); + NBN_Assert(conn != NULL); - __js_game_server_close_client_peer(conn->driver_data.peer_id); + __js_game_server_close_client_peer(conn->driver_data.webrtc.peer_id); } static int WebRTC_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *conn) { - return __js_game_server_send_packet_to(packet->buffer, packet->size, conn->driver_data.peer_id); + return __js_game_server_send_packet_to(packet->buffer, packet->size, conn->driver_data.webrtc.peer_id); } static int WebRTC_Client_Start(NBN_GameClient *client, const char *host, uint16_t port) { @@ -3262,7 +3293,508 @@ static int WebRTC_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet) #endif // __EMSCRIPTEN__ -// END OF WEBRTC JS DRIVER +// END OF WEBRTC EMSCRIPTEN DRIVER +// =================================================== + +/** + * ====== WEBRTC NATIVE DRIVER ====== * + * + * WARNING: libdatachannel callbacks can be triggered from different threads. + * Beware of race conditions in those callbacks. + * The callbacks used on the server start with WS_Server_ + * The callbacks used on the client start with WS_Client_ + */ + +#ifdef NBN_WEBRTC_NATIVE + +#include "json.h" + +static NBN_WebRTC_Config nbn_wrtc_cfg; +static int wsserver = -1; + +void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config) { nbn_wrtc_cfg = config; } + +static void WS_OnError(int ws, const char *err_msg, void *user_ptr) { + (void)user_ptr; + + LogError("Error on WS %d: %s", ws, err_msg); +} + +static void WebRTC_Native_Log(rtcLogLevel level, const char *msg) { + switch (level) { + case RTC_LOG_FATAL: + case RTC_LOG_ERROR: + LogError("%s", msg); + break; + + case RTC_LOG_WARNING: + LogWarning("%s", msg); + break; + + case RTC_LOG_INFO: + LogInfo("%s", msg); + break; + + case RTC_LOG_DEBUG: + LogDebug("%s", msg); + break; + + case RTC_LOG_VERBOSE: + LogDebug("%s", msg); + break; + + case RTC_LOG_NONE: + break; + } +} + +static char *ParseSignalingMessage(const char *msg, size_t msg_len, const char *type) { + char *sdp = NULL; + struct json_value_s *root = json_parse(msg, msg_len); // this has to be freed + struct json_object_s *object = (struct json_object_s *)root->payload; + struct json_object_element_s *curr = object->start; + + if (root->type != json_type_object) { + LogDebug("Received an invalid signaling message: %s", msg); + goto leave_free_root; + } + + while (curr != NULL) { + if (strncmp(curr->name->string, "type", 4) == 0) { + struct json_string_s *str = json_value_as_string(curr->value); + + if (strncmp(str->string, type, str->string_size)) { + // unexpected type + LogDebug("Received a signaling message with an unexpected type: %s (expected: %s)", str->string, type); + sdp = NULL; + goto leave_free_root; + } + } else if (strncmp(curr->name->string, "sdp", 3) == 0) { + struct json_string_s *str = json_value_as_string(curr->value); + + if (str) { + size_t len = strnlen(str->string, str->string_size); + + sdp = (char *)malloc(len + 1); + memcpy(sdp, str->string, len + 1); + } + } + + curr = curr->next; + } + +leave_free_root: + free(root); + + return sdp; +} + +static void ProcessSignalingMessage(NBN_WebRTC_Peer_ID peer_id, int ws, const char *msg, int size, const char *type) { + // for some reason the size of the message is negative + // in libdatachannel documentation (https://github.com/paullouisageneau/libdatachannel/blob/master/DOC.md) there is + // mention of: size: if size >= 0, data is interpreted as a binary message of length size, otherwise it is + // interpreted as a null-terminated UTF-8 string. so I guess in this case msg is a null terminated string? I could + // not find more information about this so I decided to go with flipping the size to positive even though it feels + // weird, but it works so... ¯\_(ツ)_/¯ + + if (size < 0) + size *= -1; + size -= 1; + + LogDebug("Received signaling message on WS %d (size: %d): %s", ws, size, msg); + + char *sdp = ParseSignalingMessage(msg, size, type); + + if (!sdp) { + LogWarning("Failed to parse signaling data for WS %d", ws); + return; + } + + LogDebug("Successfully parsed signaling payload (sdp: %s)", sdp); + + int ret = rtcSetRemoteDescription(peer_id, sdp, type); + + if (ret < 0) { + LogError("Failed to set remote description for peer %d (WS: %d): %d", peer_id, ws, ret); + rtcClose(ws); + } + + // IMPORTANT: not sure I can free this because it's passed to rtcSetRemoteDescription + free(sdp); +} + +char *String_ReplaceAll(const char *str, const char *orig, const char *stub) { + const char *s = str; + size_t str_len = strlen(str); + size_t orig_len = strlen(orig); + size_t stub_len = strlen(stub); + size_t occurences = 0; + + while ((s = strstr(s, orig)) != NULL) { + s += orig_len; + occurences++; + } + + size_t res_len = str_len - (occurences * orig_len) + (occurences * stub_len); + char *res = malloc(res_len + 1); + size_t res_offset = 0; + + while ((s = strstr(str, orig)) != NULL) { + size_t len = s - str; + + memcpy(res + res_offset, str, len); + res_offset += len; + memcpy(res + res_offset, stub, stub_len); + res_offset += stub_len; + str = s + orig_len; + } + + res[res_len] = 0; + + return res; +} + +static int ProcessLocalDescription(int ws, const char *sdp, const char *type) { + char *escaped_sdp = String_ReplaceAll(sdp, "\r\n", "\\r\\n"); + size_t signaling_json_size = snprintf(NULL, 0, "{\"type\":\"%s\", \"sdp\":\"%s\"}", type, escaped_sdp) + 1; + char *signaling_json = (char *)malloc(signaling_json_size); + snprintf(signaling_json, signaling_json_size, "{\"type\":\"%s\", \"sdp\":\"%s\"}", type, escaped_sdp); + + LogDebug("Send signaling message of type %s to remote connection: %s", type, signaling_json); + + int ret = 0; + + // pass -1 as the size (assume signaling_json to be a null-terminated string) + if (rtcSendMessage(ws, signaling_json, -1) < 0) { + ret = NBN_ERROR; + } + + free(signaling_json); + free(escaped_sdp); + + return ret; +} + +static void ClosePeer(NBN_Connection *conn) { + int channel_id = conn->driver_data.webrtc.channel_id; + NBN_WebRTC_Peer_ID peer_id = conn->driver_data.webrtc.peer_id; + int ws = conn->driver_data.webrtc.ws; + + LogDebug("Closing peer %d (ws: %d)", peer_id, ws); + + if (channel_id >= 0) { + rtcDeleteDataChannel(channel_id); + } + + rtcDeletePeerConnection(peer_id); + rtcDelete(ws); +} + +static void OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { + LogDebug("Processing local description of type '%s'", type); + + if (strncmp(type, "answer", strlen("answer")) != 0) { + LogWarning("Ignoring local description of type '%s' (expected 'answer')", type); + return; + } + + NBN_Connection *conn = (NBN_Connection *)user_ptr; + int ws = conn->driver_data.webrtc.ws; + NBN_WebRTC_Peer_ID peer_id = conn->driver_data.webrtc.peer_id; + + if (ProcessLocalDescription(ws, sdp, "answer") < 0) { + LogError("Failed to process local description for peer %d, closing peer", peer_id); + ClosePeer(conn); + } +} + +static void Server_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { + LogDebug("Peer %d state changed to %d", pc, state); + + if (state == RTC_CONNECTED) { + NBN_Connection *conn = (NBN_Connection *)user_ptr; + + LogDebug("Peer %d is connected !", pc); + ServerDriver_OnClientConnected(conn); + } +} + +static int CreatePeer(int ws, NBN_WebRTC_Peer_ID *peer_id, int *channel_id, + rtcStateChangeCallbackFunc state_changed_cb) { + rtcConfiguration rtcCfg = {.iceServers = nbn_wrtc_cfg.ice_servers, + .iceServersCount = (int)nbn_wrtc_cfg.ice_servers_count, + .disableAutoNegotiation = false}; + *peer_id = rtcCreatePeerConnection(&rtcCfg); + + if (*peer_id < 0) { + LogError("Failed to create peer: %d", *peer_id); + return NBN_ERROR; + } + + int ret = rtcSetLocalDescriptionCallback(*peer_id, OnLocalDescription); + + if (ret < 0) { + LogError("Failed to register local description callback for peer %d: %d", *peer_id, ret); + return NBN_ERROR; + } + + ret = rtcSetStateChangeCallback(*peer_id, state_changed_cb); + + if (ret < 0) { + LogError("Failed to register state change callback for peer %d: %d", *peer_id, ret); + return NBN_ERROR; + } + rtcDataChannelInit rtcDataChannel = { + .reliability = {.unordered = true, .unreliable = true, .maxPacketLifeTime = 1000, .maxRetransmits = 0}, + .negotiated = true, + .manualStream = true, + .stream = 0}; + *channel_id = rtcCreateDataChannelEx(*peer_id, "unreliable", &rtcDataChannel); + + if (*channel_id < 0) { + LogError("Failed to create data channel for peer %d: %d", *peer_id, *channel_id); + return NBN_ERROR; + } + + LogDebug("Successfully created data channel for peer %d: %d", *peer_id, *channel_id); + + return 0; +} + +static void WS_Server_OnOpen(int ws, void *user_ptr) { + LogDebug("WS %d is open", ws); + + NBN_WebRTC_Peer_ID peer_id; + int channel_id; + + if (CreatePeer(ws, &peer_id, &channel_id, Server_OnPeerStateChanged) < 0) { + LogError("Failed to create peer"); + return; + } + + NBN_Connection_ID conn_id = NBN_BuildConnectionHash(ws, NBN_DRIVER_WEBRTC_NATIVE); + NBN_Connection *conn = CreateClientConnection(NBN_DRIVER_WEBRTC_NATIVE, conn_id); + + conn->driver_data.webrtc.peer_id = peer_id; + conn->driver_data.webrtc.ws = ws; + conn->driver_data.webrtc.channel_id = channel_id; + + rtcSetUserPointer(peer_id, conn); + rtcSetUserPointer(ws, conn); +} + +static void WS_Server_OnClosed(int ws, void *user_ptr) { + LogDebug("WS %d has closed", ws); + + if (user_ptr) { + NBN_Connection *conn = (NBN_Connection *)user_ptr; + NBN_WebRTC_Peer_ID peer_id = conn->driver_data.webrtc.peer_id; + int channel_id = conn->driver_data.webrtc.channel_id; + + LogDebug("Closing WebRTC peer and channel (peer: %d, channel: %d)", peer_id, channel_id); + + ClosePeer(conn); + } +} + +static void WS_Server_OnMessage(int ws, const char *msg, int size, void *user_ptr) { + NBN_Connection *conn = (NBN_Connection *)user_ptr; + NBN_WebRTC_Peer_ID peer_id = conn->driver_data.webrtc.peer_id; + + ProcessSignalingMessage(peer_id, ws, msg, size, "offer"); +} + +static void OnWs_Connection(int wsserver, int ws, void *user_ptr) { + LogDebug("New WS connection %d (user_ptr: %p)", ws, user_ptr); + + rtcSetOpenCallback(ws, WS_Server_OnOpen); + rtcSetClosedCallback(ws, WS_Server_OnClosed); + rtcSetErrorCallback(ws, WS_OnError); + rtcSetMessageCallback(ws, WS_Server_OnMessage); +} + +static int WebRTC_Native_Server_Start(NBN_GameServer *server, uint16_t port) { + rtcInitLogger(nbn_wrtc_cfg.log_level, WebRTC_Native_Log); + rtcPreload(); + + rtcWsServerConfiguration cfg = {.port = port, + .enableTls = nbn_wrtc_cfg.enable_tls, + .certificatePemFile = nbn_wrtc_cfg.cert_path, + .keyPemFile = nbn_wrtc_cfg.key_path, + .keyPemPass = nbn_wrtc_cfg.passphrase}; + + wsserver = rtcCreateWebSocketServer(&cfg, OnWs_Connection); + + if (wsserver < 0) { + LogError("Failed to start WS server (code: %d)", wsserver); + return NBN_ERROR; + } + + return 0; +} + +static void WebRTC_Native_Server_Stop(NBN_GameServer *server) { + if (wsserver >= 0) { + rtcDeleteWebSocketServer(wsserver); + } + + rtcCleanup(); +} + +static int WebRTC_Native_Server_RecvPackets(NBN_GameServer *server) { + static NBN_Packet packet = {0}; + const int buffer_size = sizeof(packet.buffer); + int size = buffer_size; + + for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { + NBN_Connection *conn = server->clients[i].value; + int channel_id = conn->driver_data.webrtc.channel_id; + + while (rtcReceiveMessage(channel_id, (char *)packet.buffer, &size) == RTC_ERR_SUCCESS) { + if (Packet_InitRead(&packet, server->endpoint.protocol_id, size) < 0) + continue; + + packet.sender = conn; + size = buffer_size; + + ServerDriver_OnClientPacketReceived(&packet); + } + } + + return 0; +} + +static void WebRTC_Native_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *conn) { + NBN_Assert(conn != NULL); + ClosePeer(conn); +} + +static int WebRTC_Native_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *conn) { + NBN_WebRTC_Peer_ID peer_id = conn->driver_data.webrtc.peer_id; + int channel_id = conn->driver_data.webrtc.channel_id; + + if (rtcSendMessage(channel_id, (char *)packet->buffer, packet->size) < 0) { + LogError("rtcSendMessage failed for peer %d", peer_id); + + return NBN_ERROR; + } + + return 0; +} + +static bool wrtc_client_connected; + +static void Client_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { + LogDebug("Server peer state changed to %d", pc, state); + + if (state == RTC_CONNECTED) { + LogDebug("Server peer is connected !", pc); + wrtc_client_connected = true; + } +} + +static void WS_Client_OnOpen(int ws, void *user_ptr) { + LogDebug("WS %d is open, creating peer...", ws); + + NBN_WebRTC_Peer_ID peer_id; + int channel_id; + + if (CreatePeer(ws, &peer_id, &channel_id, Client_OnPeerStateChanged) < 0) { + LogError("Failed to create peer"); + return; + } + + LogDebug("Successfully created peer: %d", peer_id); + + NBN_Connection *server_conn = nbn_game_client.server_connection; + + NBN_Assert(server_conn != NULL); + rtcSetUserPointer(peer_id, server_conn); + rtcSetUserPointer(ws, server_conn); +} + +static void WS_Client_OnClosed(int ws, void *user_ptr) { + LogDebug("WS %d has closed", ws); + + if (user_ptr) { + NBN_Connection *conn = (NBN_Connection *)user_ptr; + + ClosePeer(conn); + } +} + +static void WS_Client_OnMessage(int ws, const char *msg, int size, void *user_ptr) { + NBN_Connection *conn = (NBN_Connection *)user_ptr; + NBN_WebRTC_Peer_ID peer_id = conn->driver_data.webrtc.peer_id; + + ProcessSignalingMessage(peer_id, ws, msg, size, "answer"); +} + +static int WebRTC_Native_Client_Start(NBN_GameClient *client, const char *host, uint16_t port) { + wrtc_client_connected = false; + + rtcInitLogger(nbn_wrtc_cfg.log_level, WebRTC_Native_Log); + rtcPreload(); + + char ws_addr[256] = {0}; + // TODO: wss? + snprintf(ws_addr, sizeof(ws_addr), "ws://%s:%d", host, port); + + int cli_ws; + + if ((cli_ws = rtcCreateWebSocket(ws_addr)) < 0) { + LogError("Failed to create websocket"); + return NBN_ERROR; + } + + LogDebug("Successfully created client WS: %d", cli_ws); + + rtcSetOpenCallback(cli_ws, WS_Client_OnOpen); + rtcSetClosedCallback(cli_ws, WS_Client_OnClosed); + rtcSetErrorCallback(cli_ws, WS_OnError); + rtcSetMessageCallback(cli_ws, WS_Client_OnMessage); + + // wait for the connection to be established + const float delay = 0.3f; + const long timeout = 5 * 1e9; // 5 seconds to connect + + struct timespec rqtp; + rqtp.tv_sec = 0; + rqtp.tv_nsec = delay * 1e9; + + float current_time_sec = 0; + + while (true) { +#if defined(_WIN32) || defined(_WIN64) + Sleep(delay * 1000); +#else + if (nanosleep(&rqtp, NULL) < 0) { + LogError("nanosleep failed"); + return NBN_ERROR; + } +#endif + current_time_sec += delay; + + if (current_time_sec >= timeout || wrtc_client_connected) { + break; + } + } + + return wrtc_client_connected ? 0 : NBN_ERROR; +} + +static void WebRTC_Native_Client_Stop(NBN_GameClient *client) { + if (wrtc_client_connected) { + ClosePeer(client->server_connection); + } + + wrtc_client_connected = false; + rtcCleanup(); +} + +#endif // NBN_WEBRTC_NATIVE + +// END OF WEBRTC NATIVE DRIVER // =================================================== /** @@ -3525,7 +4057,6 @@ extern void Log(NBN_LogLevel level, const char *filename, int line, const char * * under the terms of the MIT license. See `log.c` for details. */ -#include #include static const char *level_names[] = {"ERROR", "INFO", "WARNING", "DEBUG"}; diff --git a/nbnet.h b/nbnet.h index 7e86cd4..5da2b58 100644 --- a/nbnet.h +++ b/nbnet.h @@ -457,9 +457,7 @@ NBN_GameServerStats NBN_GameServer_GetStats(void); #ifdef __EMSCRIPTEN__ -/** - * - EMSCRIPTEN WEBRTC DRIVER SPECIFIC API - - */ +/* EMSCRIPTEN WEBRTC DRIVER SPECIFIC API */ typedef struct NBN_WebRTC_Config { bool enable_tls; @@ -469,8 +467,37 @@ typedef struct NBN_WebRTC_Config { void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config); +// TODO: ice servers currently hard coded in driver js code +#define NBN_WEBRTC_DEFAULT_CONFIG (NBN_WebRTC_Config){.enable_tls = false, .cert_path = NULL, .key_path = NULL}; + #endif // __EMSCRIPTEN__ +#ifdef NBN_WEBRTC_NATIVE + +/* NATIVE WEBRTC DRIVER SPECIFIC API */ + +#include + +typedef struct NBN_WebRTC_Config { + bool enable_tls; + const char *cert_path; + const char *key_path; + const char *passphrase; + const char **ice_servers; + unsigned int ice_servers_count; + rtcLogLevel log_level; +} NBN_WebRTC_Config; + +#define NBN_WEBRTC_DEFAULT_CONFIG(ice_servers, ice_servers_count) \ + (NBN_WebRTC_Config) { \ + .enable_tls = false, .cert_path = NULL, .key_path = NULL, .passphrase = NULL, .ice_servers = ice_servers, \ + .ice_servers_count = ice_servers_count, .log_level = RTC_LOG_ERROR \ + } + +void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config); + +#endif + #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) void NBN_GameClient_SetPing(float v); diff --git a/net_drivers/webrtc_native.h b/net_drivers/webrtc_native.h index d053bc5..73f5bf3 100644 --- a/net_drivers/webrtc_native.h +++ b/net_drivers/webrtc_native.h @@ -110,7 +110,7 @@ static char *NBN_WebRTC_Native_ParseSignalingMessage(const char *msg, size_t msg struct json_object_element_s *curr = object->start; if (root->type != json_type_object) { - NBN_LogDebug("Received an invalid signaling message: %s", msg); + LogDebug("Received an invalid signaling message: %s", msg); goto leave_free_root; } diff --git a/soak/client.c b/soak/client.c index c61cb64..875aaa1 100644 --- a/soak/client.c +++ b/soak/client.c @@ -278,8 +278,6 @@ int main(int argc, char *argv[]) { .log_level = RTC_LOG_VERBOSE}; NBN_WebRTC_Native_Register(cfg); - } else { - NBN_UDP_Register(); } #endif // WEBRTC_NATIVE From 9274b890e19ffbd18d0a2ed7a671694bca815310 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sat, 14 Feb 2026 07:30:27 +0100 Subject: [PATCH 58/85] webrtc native driver --- nbnet.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 7 deletions(-) diff --git a/nbnet.c b/nbnet.c index bcc3cb8..a6fc9e5 100644 --- a/nbnet.c +++ b/nbnet.c @@ -26,7 +26,6 @@ #include #include "nbnet.h" -#include "rtc/rtc.h" #define STB_DS_IMPLEMENTATION #include "stb_ds.h" @@ -584,10 +583,40 @@ static NBN_Driver nbn_webrtc_em_driver = {.name = "WebRTC_EMSCRIPTEN", .serv_send_packet_to = WebRTC_Server_SendPacketTo, .serv_cleanup_connection = WebRTC_Server_CleanupConnection}}; -static NBN_WebRTC_Config nbn_wrtc_cfg = {.enable_tls = false, .cert_path = NULL, .key_path = NULL}; +static NBN_WebRTC_Config nbn_wrtc_cfg = NBN_WEBRTC_DEFAULT_CONFIG; #endif // __EMSCRIPTEN__ +#ifdef NBN_WEBRTC_NATIVE + +static int WebRTC_Native_Client_Start(NBN_GameClient *client, const char *host, uint16_t port); +static void WebRTC_Native_Client_Stop(NBN_GameClient *client); +static int WebRTC_Native_Client_RecvPackets(NBN_GameClient *client); +static int WebRTC_Native_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet); + +static int WebRTC_Native_Server_Start(NBN_GameServer *server, uint16_t port); +static void WebRTC_Native_Server_Stop(NBN_GameServer *server); +static int WebRTC_Native_Server_RecvPackets(NBN_GameServer *server); +static int WebRTC_Native_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *connection); +static void WebRTC_Native_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *connection); + +static NBN_Driver nbn_webrtc_native_driver = { + .name = "WebRTC_NATIVE", + .impl = {// Client implementation + .cli_start = WebRTC_Native_Client_Start, + .cli_stop = WebRTC_Native_Client_Stop, + .cli_recv_packets = WebRTC_Native_Client_RecvPackets, + .cli_send_packet = WebRTC_Native_Client_SendPacket, + + // Server implementation + .serv_start = WebRTC_Native_Server_Start, + .serv_stop = WebRTC_Native_Server_Stop, + .serv_recv_packets = WebRTC_Native_Server_RecvPackets, + .serv_send_packet_to = WebRTC_Native_Server_SendPacketTo, + .serv_cleanup_connection = WebRTC_Native_Server_CleanupConnection}}; + +#endif // NBN_WEBRTC_NATIVE + /** * ====== LOGGING ====== */ @@ -3490,7 +3519,7 @@ static void ClosePeer(NBN_Connection *conn) { rtcDelete(ws); } -static void OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { +static void Server_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { LogDebug("Processing local description of type '%s'", type); if (strncmp(type, "answer", strlen("answer")) != 0) { @@ -3520,7 +3549,7 @@ static void Server_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { } static int CreatePeer(int ws, NBN_WebRTC_Peer_ID *peer_id, int *channel_id, - rtcStateChangeCallbackFunc state_changed_cb) { + rtcDescriptionCallbackFunc on_rtc_description_cb, rtcStateChangeCallbackFunc state_changed_cb) { rtcConfiguration rtcCfg = {.iceServers = nbn_wrtc_cfg.ice_servers, .iceServersCount = (int)nbn_wrtc_cfg.ice_servers_count, .disableAutoNegotiation = false}; @@ -3531,7 +3560,7 @@ static int CreatePeer(int ws, NBN_WebRTC_Peer_ID *peer_id, int *channel_id, return NBN_ERROR; } - int ret = rtcSetLocalDescriptionCallback(*peer_id, OnLocalDescription); + int ret = rtcSetLocalDescriptionCallback(*peer_id, on_rtc_description_cb); if (ret < 0) { LogError("Failed to register local description callback for peer %d: %d", *peer_id, ret); @@ -3567,7 +3596,7 @@ static void WS_Server_OnOpen(int ws, void *user_ptr) { NBN_WebRTC_Peer_ID peer_id; int channel_id; - if (CreatePeer(ws, &peer_id, &channel_id, Server_OnPeerStateChanged) < 0) { + if (CreatePeer(ws, &peer_id, &channel_id, Server_OnLocalDescription, Server_OnPeerStateChanged) < 0) { LogError("Failed to create peer"); return; } @@ -3684,6 +3713,20 @@ static int WebRTC_Native_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet static bool wrtc_client_connected; +static void Client_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { + LogDebug("Processing local description of type '%s'", type); + + if (strncmp(type, "offer", strlen("offer")) != 0) { + LogWarning("Ignoring local description of type '%s' (expected 'offer')", type); + return; + } + + NBN_Connection *conn = (NBN_Connection *)user_ptr; + int ws = conn->driver_data.webrtc.ws; + + ProcessLocalDescription(ws, sdp, "offer"); +} + static void Client_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { LogDebug("Server peer state changed to %d", pc, state); @@ -3699,7 +3742,7 @@ static void WS_Client_OnOpen(int ws, void *user_ptr) { NBN_WebRTC_Peer_ID peer_id; int channel_id; - if (CreatePeer(ws, &peer_id, &channel_id, Client_OnPeerStateChanged) < 0) { + if (CreatePeer(ws, &peer_id, &channel_id, Client_OnLocalDescription, Client_OnPeerStateChanged) < 0) { LogError("Failed to create peer"); return; } @@ -3792,6 +3835,36 @@ static void WebRTC_Native_Client_Stop(NBN_GameClient *client) { rtcCleanup(); } +static int WebRTC_Native_Client_RecvPackets(NBN_GameClient *client) { + static NBN_Packet packet = {0}; + const int buffer_size = sizeof(packet.buffer); + int size = buffer_size; + int channel_id = client->server_connection->driver_data.webrtc.channel_id; + + while (rtcReceiveMessage(channel_id, (char *)packet.buffer, &size) == RTC_ERR_SUCCESS) { + if (Packet_InitRead(&packet, client->endpoint.protocol_id, size) < 0) + continue; + + packet.sender = NULL; + size = buffer_size; + + ClientDriver_OnPacketReceived(&packet); + } + + return 0; +} + +static int WebRTC_Native_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet) { + int channel_id = client->server_connection->driver_data.webrtc.channel_id; + + if (rtcSendMessage(channel_id, (char *)packet->buffer, packet->size) < 0) { + LogError("rtcSendMessage failed"); + return NBN_ERROR; + } + + return 0; +} + #endif // NBN_WEBRTC_NATIVE // END OF WEBRTC NATIVE DRIVER From 35bd6ff235fddeae2e3d88266c99245854fc4b06 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sat, 14 Feb 2026 07:41:00 +0100 Subject: [PATCH 59/85] plug in webrtc native driver --- nbnet.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/nbnet.c b/nbnet.c index a6fc9e5..6e7096e 100644 --- a/nbnet.c +++ b/nbnet.c @@ -1721,6 +1721,12 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, NBN_Con break; #endif // NBN_UDP +#ifdef NBN_WEBRTC_NATIVE + case NBN_DRIVER_WEBRTC_NATIVE: + connection->driver = &nbn_webrtc_native_driver; + break; +#endif // NBN_WEBRTC_NATIVE + #ifdef __EMSCRIPTEN__ case NBN_DRIVER_WEBRTC_EMSCRIPTEN: connection->driver = &nbn_webrtc_em_driver; @@ -1871,6 +1877,17 @@ static int StartClientDrivers(const char *host, uint16_t port) { driver_count++; #endif // NBN_UDP +#ifdef NBN_WEBRTC_NATIVE + nbn_game_client.server_connection = CreateServerConnection(NBN_DRIVER_WEBRTC_NATIVE); + + if (nbn_webrtc_native_driver.impl.cli_start(&nbn_game_client, host, port) < 0) { + LogError("Failed to start driver %s", nbn_webrtc_native_driver.name); + return NBN_ERROR; + } + + driver_count++; +#endif // NBN_WEBRTC_NATIVE + #ifdef __EMSCRIPTEN__ nbn_game_client.server_connection = CreateServerConnection(NBN_DRIVER_WEBRTC_EMSCRIPTEN); @@ -1963,6 +1980,10 @@ void NBN_GameClient_Stop(void) { nbn_udp_driver.impl.cli_stop(&nbn_game_client); #endif // NBN_UDP +#ifdef NBN_WEBRTC_NATIVE + nbn_webrtc_native_driver.impl.cli_stop(&nbn_game_client); +#endif // NBN_WEBRTC_NATIVE + #ifdef __EMSCRIPTEN__ nbn_webrtc_em_driver.impl.cli_stop(&nbn_game_client); #endif // __EMSCRIPTEN__ @@ -1993,6 +2014,13 @@ static int ReadPacketsFromClientDrivers(void) { } #endif // NBN_UDP +#ifdef NBN_WEBRTC_NATIVE + if (nbn_webrtc_native_driver.impl.cli_recv_packets(&nbn_game_client) < 0) { + LogError("Failed to read packets from driver %s", nbn_webrtc_native_driver.name); + return NBN_ERROR; + } +#endif // NBN_WEBRTC_NATIVE + #ifdef __EMSCRIPTEN__ if (nbn_webrtc_em_driver.impl.cli_recv_packets(&nbn_game_client) < 0) { LogError("Failed to read packets from driver %s", nbn_webrtc_em_driver.name); @@ -2278,6 +2306,15 @@ static int StartServerDrivers(uint16_t port) { driver_count++; #endif // NBN_UDP +#ifdef NBN_WEBRTC_NATIVE + if (nbn_webrtc_native_driver.impl.serv_start(&nbn_game_server, port) < 0) { + LogError("Failed to start driver %s", nbn_webrtc_native_driver.name); + return NBN_ERROR; + } + + driver_count++; +#endif // NBN_WEBRTC_NATIVE + #ifdef __EMSCRIPTEN__ if (nbn_webrtc_em_driver.impl.serv_start(&nbn_game_server, port) < 0) { LogError("Failed to start driver %s", nbn_webrtc_em_driver.name); @@ -2330,6 +2367,10 @@ void NBN_GameServer_Stop(void) { nbn_udp_driver.impl.serv_stop(&nbn_game_server); #endif // NBN_UDP +#ifdef NBN_WEBRTC_NATIVE + nbn_webrtc_native_driver.impl.serv_stop(&nbn_game_server); +#endif // NBN_WEBRTC_NATIVE + #ifdef __EMSCRIPTEN__ nbn_webrtc_em_driver.impl.serv_stop(&nbn_game_server); #endif // __EMSCRIPTEN__ @@ -2385,6 +2426,12 @@ static void ReadPacketsFromServerDrivers(void) { } #endif // NBN_UDP +#ifdef NBN_WEBRTC_NATIVE + if (nbn_webrtc_native_driver.impl.serv_recv_packets(&nbn_game_server) < 0) { + LogError("Failed to read packets from driver %s", nbn_webrtc_native_driver.name); + } +#endif // NBN_WEBRTC_NATIVE + #ifdef __EMSCRIPTEN__ if (nbn_webrtc_em_driver.impl.serv_recv_packets(&nbn_game_server) < 0) { LogError("Failed to read packets from driver %s", nbn_webrtc_em_driver.name); From 3d53894eb821ff6423c18b2e1d638587470c58a4 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sat, 14 Feb 2026 07:41:58 +0100 Subject: [PATCH 60/85] remove net_drivers folder --- net_drivers/json.h => json.h | 0 net_drivers/webrtc.h | 240 ------ net_drivers/webrtc/js/api.js | 141 ---- net_drivers/webrtc/js/game_client.js | 95 --- net_drivers/webrtc/js/game_server.js | 137 ---- net_drivers/webrtc/js/index.js | 30 - net_drivers/webrtc/js/logger.js | 46 -- net_drivers/webrtc/js/nbnet.js | 23 - net_drivers/webrtc/js/peer.js | 245 ------- .../webrtc/js/standalone/connection.js | 17 - .../webrtc/js/standalone/signaling_client.js | 86 --- .../webrtc/js/standalone/signaling_server.js | 79 -- net_drivers/webrtc/package-lock.json | 594 --------------- net_drivers/webrtc/package.json | 13 - net_drivers/webrtc_native.h | 683 ------------------ 15 files changed, 2429 deletions(-) rename net_drivers/json.h => json.h (100%) delete mode 100644 net_drivers/webrtc.h delete mode 100644 net_drivers/webrtc/js/api.js delete mode 100644 net_drivers/webrtc/js/game_client.js delete mode 100644 net_drivers/webrtc/js/game_server.js delete mode 100644 net_drivers/webrtc/js/index.js delete mode 100644 net_drivers/webrtc/js/logger.js delete mode 100644 net_drivers/webrtc/js/nbnet.js delete mode 100644 net_drivers/webrtc/js/peer.js delete mode 100644 net_drivers/webrtc/js/standalone/connection.js delete mode 100644 net_drivers/webrtc/js/standalone/signaling_client.js delete mode 100644 net_drivers/webrtc/js/standalone/signaling_server.js delete mode 100644 net_drivers/webrtc/package-lock.json delete mode 100644 net_drivers/webrtc/package.json delete mode 100644 net_drivers/webrtc_native.h diff --git a/net_drivers/json.h b/json.h similarity index 100% rename from net_drivers/json.h rename to json.h diff --git a/net_drivers/webrtc.h b/net_drivers/webrtc.h deleted file mode 100644 index cd29cd3..0000000 --- a/net_drivers/webrtc.h +++ /dev/null @@ -1,240 +0,0 @@ -/* - -Copyright (C) 2024 BIAGINI Nathan - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -*/ - -/* - --- NBNET WEBRTC DRIVER --- - - WebRTC driver using a single unreliable data channel for the nbnet library. - - How to use: - - 1. Include this header *once* after the nbnet header in the same file where you defined the NBNET_IMPL macro - 2. Call NBN_WebRTC_Register in both your client and server code before calling NBN_GameClient_Start or - NBN_GameServer_Start -*/ - -#include -#include - -#ifndef NBNET_H -#include "../nbnet.h" -#endif - -typedef struct NBN_WebRTC_Config { - bool enable_tls; - const char *cert_path; - const char *key_path; -} NBN_WebRTC_Config; - -void NBN_WebRTC_Register(NBN_WebRTC_Config config); - -#ifdef NBNET_IMPL - -#if !defined(EXTERN_C) -#if defined(__cplusplus) -#define NBN_EXTERN extern "C" -#else -#define NBN_EXTERN extern -#endif -#endif - -#include - -#define NBN_WEBRTC_DRIVER_ID 1 -#define NBN_WEBRTC_DRIVER_NAME "WebRTC" - -typedef struct { - uint32_t id; - NBN_Connection *conn; -} NBN_WebRTC_Peer; - -#pragma region Game server - -/* --- JS API --- */ - -NBN_EXTERN void __js_game_server_init(uint32_t, bool, const char *, const char *); -NBN_EXTERN int __js_game_server_start(uint16_t); -NBN_EXTERN int __js_game_server_dequeue_packet(uint32_t *, uint8_t *); -NBN_EXTERN int __js_game_server_send_packet_to(uint8_t *, unsigned int, uint32_t); -NBN_EXTERN void __js_game_server_close_client_peer(unsigned int); -NBN_EXTERN void __js_game_server_stop(void); - -/* --- Driver implementation --- */ - -typedef struct NBN_WebRTC_Server { - struct { - int key; - NBN_WebRTC_Peer *value; - } *peers; -} NBN_WebRTC_Server; - -static NBN_WebRTC_Server nbn_wrtc_serv = {NULL, 0}; -static NBN_WebRTC_Config nbn_wrtc_cfg; - -static int NBN_WebRTC_ServStart(uint32_t protocol_id, uint16_t port) { - __js_game_server_init(protocol_id, nbn_wrtc_cfg.enable_tls, nbn_wrtc_cfg.key_path, nbn_wrtc_cfg.cert_path); - - if (__js_game_server_start(port) < 0) - return -1; - - nbn_wrtc_serv.protocol_id = protocol_id; - - hmdefault(nbn_wrtc_serv.peers, NULL); - - return 0; -} - -static void NBN_WebRTC_ServStop(void) { - __js_game_server_stop(); - hmfree(nbn_wrtc_serv.peers); -} - -static int NBN_WebRTC_ServRecvPackets(void) { - static NBN_Packet packet = {0}; - uint32_t peer_id; - unsigned int len; - - while ((len = __js_game_server_dequeue_packet(&peer_id, packet.buffer)) > 0) { - NBN_WebRTC_Peer *peer = hmget(nbn_wrtc_serv.peers, peer_id); - - if (peer == NULL) { - if (GameServer_GetClientCount() >= NBN_MAX_CLIENTS) - continue; - - NBN_LogTrace("Peer %d has connected", peer_id); - - peer = (NBN_WebRTC_Peer *)malloc(sizeof(NBN_WebRTC_Peer)); - - peer->id = peer_id; - peer->conn = NBN_GameServer_CreateClientConnection(NBN_WEBRTC_DRIVER_ID, peer, peer_id); - - hmput(nbn_wrtc_serv.peers, peer_id, peer); - - NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, peer->conn); - } - - if (NBN_Packet_InitRead(&packet, nbn_wrtc_serv.protocol_id, len) < 0) - continue; - - packet.sender = peer->conn; - - NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet); - } - - return 0; -} - -static void NBN_WebRTC_ServRemoveClientConnection(NBN_Connection *conn) { - assert(conn != NULL); - - __js_game_server_close_client_peer(conn->id); - - NBN_WebRTC_Peer *peer = (NBN_WebRTC_Peer *)conn->driver_data; - int ret = hmdel(nbn_wrtc_serv.peers, peer->id); - - if (ret == 1) { - NBN_LogDebug("Destroyed peer %d", peer->id); - - free(peer); - } -} - -static int NBN_WebRTC_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *conn) { - return __js_game_server_send_packet_to(packet->buffer, packet->size, conn->id); -} - -#pragma endregion /* Game server */ - -#pragma region Game client - -/* --- JS API --- */ - -NBN_EXTERN void __js_game_client_init(uint32_t, bool); -NBN_EXTERN int __js_game_client_start(const char *, uint16_t); -NBN_EXTERN int __js_game_client_dequeue_packet(uint8_t *); -NBN_EXTERN int __js_game_client_send_packet(uint8_t *, unsigned int); -NBN_EXTERN void __js_game_client_close(void); - -/* --- Driver implementation --- */ - -typedef struct NBN_WebRTC_Client { - uint32_t protocol_id; - NBN_Connection *server_conn; -} NBN_WebRTC_Client; - -static NBN_WebRTC_Client nbn_wrtc_cli = {0, NULL}; - -static int NBN_WebRTC_CliStart(uint32_t protocol_id, const char *host, uint16_t port) { - __js_game_client_init(protocol_id, nbn_wrtc_cfg.enable_tls); - - nbn_wrtc_cli.protocol_id = protocol_id; - nbn_wrtc_cli.server_conn = NBN_GameClient_CreateServerConnection(NBN_WEBRTC_DRIVER_ID, NULL); - - int res; - - if ((res = __js_game_client_start(host, port)) < 0) - return -1; - - return 0; -} - -static void NBN_WebRTC_CliStop(void) { __js_game_client_close(); } - -static int NBN_WebRTC_CliRecvPackets(void) { - static NBN_Packet packet = {0}; - unsigned int len; - - while ((len = __js_game_client_dequeue_packet(packet.buffer)) > 0) { - if (NBN_Packet_InitRead(&packet, nbn_wrtc_cli.protocol_id, len) < 0) - continue; - - NBN_Driver_RaiseEvent(NBN_DRIVER_CLI_PACKET_RECEIVED, &packet); - } - - return 0; -} - -static int NBN_WebRTC_CliSendPacket(NBN_Packet *packet) { - return __js_game_client_send_packet(packet->buffer, packet->size); -} - -#pragma endregion /* Game client */ - -#pragma region Driver registering - -void NBN_WebRTC_Register(NBN_WebRTC_Config config) { - NBN_DriverImplementation driver_impl = {// Client implementation - NBN_WebRTC_CliStart, NBN_WebRTC_CliStop, NBN_WebRTC_CliRecvPackets, - NBN_WebRTC_CliSendPacket, - - // Server implementation - NBN_WebRTC_ServStart, NBN_WebRTC_ServStop, NBN_WebRTC_ServRecvPackets, - NBN_WebRTC_ServSendPacketTo, NBN_WebRTC_ServRemoveClientConnection}; - - nbn_wrtc_cfg = config; - - NBN_Driver_Register(NBN_WEBRTC_DRIVER_ID, NBN_WEBRTC_DRIVER_NAME, driver_impl); -} - -#pragma endregion /* Driver registering */ - -#endif /* NBNET_IMPL */ diff --git a/net_drivers/webrtc/js/api.js b/net_drivers/webrtc/js/api.js deleted file mode 100644 index 510c318..0000000 --- a/net_drivers/webrtc/js/api.js +++ /dev/null @@ -1,141 +0,0 @@ -/* - -Copyright (C) 2024 BIAGINI Nathan - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -*/ - -// --- Game server API --- - -mergeInto(LibraryManager.library, { - __js_game_server_send_packet_to__proxy: 'sync', - __js_game_server_dequeue_packet__proxy: 'sync', - __js_game_server_dequeue_packet__deps: ['$writeArrayToMemory'], - - __js_game_server_init: function (protocol_id, use_https, key_pem, cert_pem) { - const nbnet = require('nbnet') - - const signalingServer = new nbnet.Standalone.SignalingServer( - protocol_id, - use_https ? { https: true, key: UTF8ToString(key_pem), cert: UTF8ToString(cert_pem) } : {} - ) - - this.gameServer = new nbnet.GameServer(signalingServer) - }, - - __js_game_server_start: function (port) { - return Asyncify.handleSleep(function (wakeUp) { - this.gameServer.start(port).then(() => { - wakeUp(0) - }).catch(_ => { - wakeUp(-1) - }) - }) - }, - - __js_game_server_dequeue_packet: function(peerIdPtr, bufferPtr) { - const packet = this.gameServer.packets.shift() - - if (packet) { - const packetData = packet[0] - const packetSenderId = packet[1] - const byteArray = new Uint8Array(packetData) - - setValue(peerIdPtr, packetSenderId, 'i32') - writeArrayToMemory(byteArray, bufferPtr) - - return packetData.byteLength - } else { - return 0 - } - }, - - __js_game_server_send_packet_to: function (packetPtr, packetSize, peerId) { - const data = new Uint8Array(HEAPU8.subarray(packetPtr, packetPtr + packetSize)) - - this.gameServer.send(data, peerId) - }, - - __js_game_server_close_client_peer: function(peerId) { - this.gameServer.closePeer(peerId) - }, - - __js_game_server_stop: function() { - this.gameServer.stop() - } -}) - -// --- Game client API --- - -mergeInto(LibraryManager.library, { - __js_game_client_send_packet__proxy: 'sync', - __js_game_client_dequeue_packet__proxy: 'sync', - __js_game_client_dequeue_packet__deps: ['$writeArrayToMemory'], - - __js_game_client_init: function(protocol_id, use_https) { - let nbnet - - if (typeof window === 'undefined') { - // we are running in node so we can use require - nbnet = require('nbnet') - } else { - // we are running in a web browser so we used to "browserified" nbnet (see nbnet.js) - nbnet = Module.nbnet - } - - const signalingClient = new nbnet.Standalone.SignalingClient(protocol_id, { https: use_https }) - - this.gameClient = new nbnet.GameClient(signalingClient) - }, - - __js_game_client_start: function(hostPtr, port) { - return Asyncify.handleSleep(function (wakeUp) { - this.gameClient.connect(UTF8ToString(hostPtr), port).then(() => { - wakeUp(0) - }).catch(_ => { - wakeUp(-1) - }) - }) - }, - - __js_game_client_dequeue_packet: function(bufferPtr) { - const packet = this.gameClient.packets.shift() - - if (packet) { - const byteArray = new Uint8Array(packet) - - writeArrayToMemory(byteArray, bufferPtr) - - return packet.byteLength - } else { - return 0 - } - }, - - __js_game_client_send_packet: function (packetPtr, packetSize) { - const data = new Uint8Array(HEAPU8.subarray(packetPtr, packetPtr + packetSize)) - - this.gameClient.send(data) - }, - - __js_game_client_close: function() { - Asyncify.handleSleep(function (wakeUp) { - this.gameClient.close().then(wakeUp).catch(wakeUp) - }); - } -}) diff --git a/net_drivers/webrtc/js/game_client.js b/net_drivers/webrtc/js/game_client.js deleted file mode 100644 index 5e7fe5b..0000000 --- a/net_drivers/webrtc/js/game_client.js +++ /dev/null @@ -1,95 +0,0 @@ -/* - -Copyright (C) 2024 BIAGINI Nathan - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -*/ - -const loggerFactory = require('./logger.js') -const Peer = require('./peer.js') - -function GameClient(signalingClient) { - this.signalingClient = signalingClient - this.peer = new Peer(0, signalingClient) - this.packets = [] - this.logger = loggerFactory.createLogger('GameClient') - - this.signalingClient.onDataReceived = (data) => { - this.peer.notifySignalingData(data) - } - - this.signalingClient.onClosed = () => { - // this.onClosed() - } - - this.peer.onError = (err) => { - this.logger.info('Peer has ecountered an error: %s. Closing connection', err) - - this.signalingClient.close() - } - - this.peer.onClosed = (err) => { - this.logger.info('Peer closed') - - this.signalingClient.close() - } - - this.peer.onPacketReceived = (packet) => { - this.packets.push(packet) - } -} - -GameClient.prototype.connect = function(host, port) { - this.logger.info('Connecting...') - - return new Promise((resolve, reject) => { - this.signalingClient.connect(host, port).then(() => { - this.logger.info('Creating server peer...') - - this.peer.onConnected = () => { - this.logger.info('Connected') - - resolve() - } - - this.peer.onConnectionError = (err) => { - this.logger.info('Connection failed: %s', err) - - reject(err) - } - - this.peer.connect() - }).catch((err) => { - reject(err) - }) - }) -} - -GameClient.prototype.send = function(data) { - this.peer.send(data) -} - -GameClient.prototype.close = function() { - return new Promise((resolve, reject) => { - this.signalingClient.close() - .then(() => resolve()) - .catch(() => reject()) - }) -} - -module.exports = GameClient diff --git a/net_drivers/webrtc/js/game_server.js b/net_drivers/webrtc/js/game_server.js deleted file mode 100644 index 51c53f8..0000000 --- a/net_drivers/webrtc/js/game_server.js +++ /dev/null @@ -1,137 +0,0 @@ -/* - -Copyright (C) 2024 BIAGINI Nathan - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -*/ - -const loggerFactory = require('./logger.js') -const Peer = require('./peer.js') - -function GameServer(signalingServer) { - this.signalingServer = signalingServer - this.peers = {} - this.packets = [] - this.nextPeerId = 1; // nbnet connection ids start at 1 - this.logger = loggerFactory.createLogger('GameServer') -} - -GameServer.prototype.start = function(port) { - if (this.signalingServer.isSecure()) { - this.logger.info('Starting (HTTPS is enabled)...') - } else { - this.logger.info('Starting (HTTPS is disabled)...') - } - - return new Promise((resolve, reject) => { - this.signalingServer.onConnection = (connection) => { handleConnection(this, connection) } - - this.signalingServer.start(port).then(() => { - this.logger.info('Started') - - resolve() - }).catch((err) => { - this.logger.error('Failed to start: %s', err) - - reject(err) - }) - }) -} - -GameServer.prototype.send = function(packet, peerId) { - const peer = this.peers[peerId] - - if (peer) { - peer.send(packet) - } else { - this.logger.warn("Trying to send packet to an unknown peer: " + peerId) - } -} - -GameServer.prototype.closePeer = function(peerId) { - const peer = this.peers[peerId] - - if (peer) { - peer.close() - } -} - -GameServer.prototype.stop = function() { - this.signalingServer.stop() -} - -function handleConnection(gameServer, connection) { - const peer = new Peer(gameServer.nextPeerId++, connection) - - peer.onConnected = () => { - gameServer.logger.info('Peer %d is connected', peer.id) - - // gameServer.onPeerConnected(peer.id) - } - - peer.onClosed = () => { - gameServer.logger.info('Peer %d has disconnected', peer.id) - - removePeer(gameServer, peer) - // gameServer.onPeerDisconnected(peer.id) - } - - peer.onError = (err) => { - gameServer.logger.info('Peer %d has ecountered an error: %s. Closing peer', peer.id, err) - - peer.close() - removePeer(gameServer, peer) // make sure the peer is acutally removed, may not be needed - } - - peer.onPacketReceived = (packet) => { - if (gameServer.onPacketReceived) { - gameServer.onPacketReceived(packet, peer.id) - } else { - gameServer.packets.push([packet, peer.id]) - } - } - - gameServer.logger.info('Created new peer (id: %d) for connection %d', peer.id, connection.id) - - gameServer.peers[peer.id] = peer - - connection.onMessageReceived = (msg) => { - gameServer.logger.info('Received signaling message for connection %s: %s', connection.id, msg) - - peer.notifySignalingData(JSON.parse(msg)) - } - - connection.onClosed = () => { - gameServer.logger.info('Connection %s closed, will close peer %d', connection.id, peer.id) - - peer.close() - } -} - -function removePeer(gameServer, peer) { - gameServer.logger.info('Removing peer %d', peer.id) - - for (const peerId in gameServer.peers) { - if (gameServer.peers.hasOwnProperty(peerId) && gameServer.peers[peerId].id === peer.id) { - delete gameServer.peers[peerId] - return - } - } -} - -module.exports = GameServer diff --git a/net_drivers/webrtc/js/index.js b/net_drivers/webrtc/js/index.js deleted file mode 100644 index 58525ff..0000000 --- a/net_drivers/webrtc/js/index.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - -Copyright (C) 2024 BIAGINI Nathan - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -*/ - -module.exports = { - GameServer: require('./game_server.js'), - GameClient: require('./game_client.js'), - Standalone: { - SignalingServer: require('./standalone/signaling_server.js'), - SignalingClient: require('./standalone/signaling_client.js') - } -} diff --git a/net_drivers/webrtc/js/logger.js b/net_drivers/webrtc/js/logger.js deleted file mode 100644 index e1ef7b7..0000000 --- a/net_drivers/webrtc/js/logger.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - -Copyright (C) 2024 BIAGINI Nathan - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -*/ - -var exports = module.exports = {} - -const { createLogger, format, transports } = require('winston') -const { combine, timestamp, label, printf } = format - -exports.createLogger = function(name, options = {}) { - const loggerFormat = combine( - label({ label: name }), - timestamp(), - format.colorize(), - format.splat(), - printf(({ level, message, label, timestamp }) => `${timestamp} [${label}] ${level}: ${message}`) - ) - - const logger = createLogger({ - level: 'info', - format: loggerFormat, - transports: [ - new transports.Console() - ] - }) - - return logger -} diff --git a/net_drivers/webrtc/js/nbnet.js b/net_drivers/webrtc/js/nbnet.js deleted file mode 100644 index 8e1163b..0000000 --- a/net_drivers/webrtc/js/nbnet.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - -Copyright (C) 2024 BIAGINI Nathan - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -*/ - -Module.nbnet = require('./index.js') diff --git a/net_drivers/webrtc/js/peer.js b/net_drivers/webrtc/js/peer.js deleted file mode 100644 index 8d83bc7..0000000 --- a/net_drivers/webrtc/js/peer.js +++ /dev/null @@ -1,245 +0,0 @@ -/* - -Copyright (C) 2024 BIAGINI Nathan - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -*/ - -const webrtc = require('@roamhq/wrtc') -const RTCPeerConnection = webrtc.RTCPeerConnection -const RTCSessionDescription = webrtc.RTCSessionDescription -const loggerFactory = require('./logger.js') - -function Peer(id, connection) { - this.id = id - this.connection = connection - this.candidates = [] - this.peerConnection = new RTCPeerConnection({ 'iceServers': [{ 'urls': 'stun:stun01.sipphone.com' }] }) - this.channel = this.peerConnection.createDataChannel('unreliable', - { negotiated: true, id: 0, maxRetransmits: 0, ordered: false }) - this.channel.binaryType = 'arraybuffer' - - // peer connection event listeners - this.peerConnection.addEventListener('icecandidate', ({ candidate }) => { onIceCandidate(this, candidate) }) - this.peerConnection.addEventListener('signalingstatechange', () => { onSignalingStateChange(this) }) - this.peerConnection.addEventListener('icegatheringstatechange', () => { onIceGatheringStateChanged(this) }) - this.peerConnection.addEventListener('icecandidateerror', (ev) => { - this.logger.error('Error while gathering ice candidates: %s', ev.errorCode) - }) - - this.channel.addEventListener('open', () => { onDataChannelOpened(this, this.channel) }) - this.channel.addEventListener('error', () => { onDataChannelError(this, this.channel) }) - this.channel.addEventListener('message', (ev) => { onPacketReceived(this, ev.data) }) - - this.logger = loggerFactory.createLogger(`Peer(${this.id})`) -} - -Peer.prototype.connect = function() { - this.state = 'connecting' - - // OfferToReceiveAudio & OfferToReceiveVideo seems to be required in Chrome even if we only need data channels - this.peerConnection.createOffer({ mandatory: { OfferToReceiveAudio: true, OfferToReceiveVideo: true } }).then((description) => { - this.peerConnection.setLocalDescription(description).then(() => { - this.logger.info('Offer created and set as local description') - - this.connection.send(description) - }).catch((err) => { - raiseError(this, `setLocalDescription: ${err}`) - }) - }).catch((err) => { - raiseError(this, `createOffer: ${err}`) - }) -} - -Peer.prototype.close = function() { - this.logger.info('Closing peer...') - - this.peerConnection.close() - this.connection.close() -} - -Peer.prototype.notifySignalingData = function(data) { - if ('type' in data) { - if (data.type === 'offer') { - handleOffer(this, data) - } else if (data.type === 'answer') { - handleAnswer(this, data) - } - } else if ('candidate' in data) { - handleCandidate(this, data.candidate) - } else if ('signaling' in data) { - if (data.signaling.ready_for_candidates) { - this.logger.info('Remote peer is ready to receive our ice candidates') - - this.isRemotePeerReadyToReceiveRemoteIceCandidates = true - } - } -} - -Peer.prototype.send = function(data) { - try { - this.channel.send(data) - } catch(err) { - this.logger.error('Failed to send: ' + err) - - this.close() - } -} - -function onIceCandidate(peer, candidate) { - if (candidate) { - peer.logger.info('Got new candidate, save it to the candidates list') - - peer.candidates.push(candidate) - } -} - -function onSignalingStateChange(peer) { - if (peer.peerConnection.signalingState == 'closed') { - peer.logger.info('Closed') - - peer.onClosed() - } -} - -function onIceGatheringStateChanged(peer) { - peer.logger.info('Ice gathering state changed: %s', peer.peerConnection.iceGatheringState) - - if (peer.peerConnection.iceGatheringState === 'complete') { - peer.logger.info('All candidates gathered, waiting for the remote peer to be ready to receive them') - - waitForRemotePeerToBeReadyToReceiveIceCandidates(peer).then(() => { - if (peer.state !== 'connected') { - sendCandidates(peer) - } - }).catch((err) => { - raiseError(peer, `waitForRemotePeerToBeReadyToReceiveIceCandidates: ${err}`) - }) - } -} - -function handleOffer(peer, offer) { - peer.logger.info('Got offer') - - peer.peerConnection.setRemoteDescription(new RTCSessionDescription(offer)).then(() => { - onRemoteDescriptionSet(peer) - createAnswer(peer) - }).catch((err) => { - raiseError(peer, `setRemoteDescription: ${err}`) - }) -} - -function handleAnswer(peer, answer) { - peer.logger.info('Got answer') - - peer.peerConnection.setRemoteDescription(answer).then(() => { - onRemoteDescriptionSet(peer) - }).catch((err) => { - raiseError(peer, `setRemoteDescription: ${err}`) - }) -} - -function onRemoteDescriptionSet(peer) { - // remote description is set so we can now add the other peer ice candidates - // notify the other peer that we are ready to treat his ice candidates - // addIceCandidate fail if remote description is not set - - peer.logger.info('Remote description set, notify remote peer that we are ready to receive his ice candidates') - - peer.connection.send({ signaling: { ready_for_candidates: true } }) -} - -function handleCandidate(peer, candidate) { - peer.logger.info(`Got candidate: ${JSON.stringify(candidate)}`) - - if (candidate.candidate) { - peer.peerConnection.addIceCandidate(candidate).then(() => { - peer.logger.info('Candidate added') - }).catch((err) => { - raiseError(peer, `addIceCandidate: ${err}`) - }) - } -} - -function createAnswer(peer) { - peer.peerConnection.createAnswer().then((answer) => { - peer.logger.info('Answer created') - - peer.peerConnection.setLocalDescription(answer).then(() => { - peer.logger.info('Answer set as local description, signaling it') - - peer.connection.send(answer) - }).catch((err) => { - raiseError(peer, `setLocalDescription: ${err}`) - }) - }).catch((err) => { - raiseError(peer, `createAnswer: ${err}`) - }) -} - -function waitForRemotePeerToBeReadyToReceiveIceCandidates(peer) { - return new Promise((resolve, reject) => { - const timeoutId = setTimeout(() => { - reject('timeout') - }, 5000) - - const intervalId = setInterval(() => { - if (peer.state === 'connected' || peer.isRemotePeerReadyToReceiveRemoteIceCandidates) { - clearTimeout(timeoutId) - clearInterval(intervalId) - resolve() - } - }, 500) - }) -} - -function sendCandidates(peer) { - peer.logger.info('Sending all gather candidates...') - - peer.candidates.forEach((candidate) => { - peer.connection.send({ candidate: candidate }) - }) -} - -function onDataChannelOpened(peer, dataChannel) { - peer.logger.info('%s data channel opened (id: %d)', dataChannel.label, dataChannel.id) - - peer.state = 'connected' - - peer.onConnected() -} - -function onDataChannelError(peer, dataChannel, err) { - raiseError(peer, `Data channel ${dataChannel.label} (id: ${dataChannel.id}) error: ${err}`) -} - -function onPacketReceived(peer, packet) { - peer.onPacketReceived(packet) -} - -function raiseError(peer, err) { - peer.logger.error(err) - - if (peer.state === 'connecting' && peer.onConnectionError) { - peer.onConnectionError(this) - } else if (peer.onError) { - peer.onError(err) - } -} - -module.exports = Peer diff --git a/net_drivers/webrtc/js/standalone/connection.js b/net_drivers/webrtc/js/standalone/connection.js deleted file mode 100644 index f1b1d0d..0000000 --- a/net_drivers/webrtc/js/standalone/connection.js +++ /dev/null @@ -1,17 +0,0 @@ -function Connection(connection) { - this.connection = connection - this.id = connection.remoteAddress - - connection.on('message', (msg) => { this.onMessageReceived(msg.utf8Data) }) - connection.on('close', () => { this.onClosed() }) -} - -Connection.prototype.send = function(data) { - this.connection.sendUTF(JSON.stringify(data)) -} - -Connection.prototype.close = function() { - this.connection.close() -} - -module.exports = Connection \ No newline at end of file diff --git a/net_drivers/webrtc/js/standalone/signaling_client.js b/net_drivers/webrtc/js/standalone/signaling_client.js deleted file mode 100644 index ed3d075..0000000 --- a/net_drivers/webrtc/js/standalone/signaling_client.js +++ /dev/null @@ -1,86 +0,0 @@ -const loggerFactory = require('../logger.js') - -function SignalingClient(protocol_id, options) { - this.protocol = protocol_id.toString() - this.logger = loggerFactory.createLogger('StandaloneSignalingClient') - this.connected = false - this.options = options -} - -SignalingClient.prototype.connect = function(host, port) { - return new Promise((resolve, reject) => { - const uri = this.options['https'] ? `wss://${host}:${port}` : `ws://${host}:${port}` - - this.logger.info(this.options['https']) - this.logger.info(`Connecting to ${uri}...`) - - const WebSocket = require('websocket').w3cwebsocket - - this.ws = new WebSocket(uri) - - this.ws.onclose = (ev) => { - if (this.connected) { - this.logger.error('Connection closed') - - this.connected = false - - this.onClosed() - } else { - this.logger.error('Connection failed') - - reject() - } - } - - this.ws.onopen = () => { - this.logger.info('Connected') - - this.connected = true - - clearTimeout(timeoutId) - resolve() - } - - this.ws.onmessage = (ev) => { - this.logger.info('Received signaling data: %s', ev.data) - - this.onDataReceived(JSON.parse(ev.data)) - } - - const timeoutId = setTimeout(() => { - this.logger.error('Connection timeout') - - reject() - }, 3000) - }) -} - -SignalingClient.prototype.send = function(data) { - this.logger.info('Send signaling data: %s', data) - - this.ws.send(JSON.stringify(data)) -} - -SignalingClient.prototype.close = function() { - this.logger.info('Closing...') - - const WebSocket = require('websocket').w3cwebsocket - - return new Promise((resolve, reject) => { - if (this.ws.readyState != WebSocket.OPEN) { - this.logger.warn('Not opened') - - reject() - } else { - this.ws.onclose = (_) => { - this.logger.info('Closed') - - resolve() - } - - this.ws.close() - } - }) -} - -module.exports = SignalingClient diff --git a/net_drivers/webrtc/js/standalone/signaling_server.js b/net_drivers/webrtc/js/standalone/signaling_server.js deleted file mode 100644 index ec6ed86..0000000 --- a/net_drivers/webrtc/js/standalone/signaling_server.js +++ /dev/null @@ -1,79 +0,0 @@ -const Connection = require('./connection.js') -const loggerFactory = require('../logger.js') - -function SignalingServer(protocol_id, options) { - this.protocol = protocol_id.toString() - this.logger = loggerFactory.createLogger('StandaloneSignalingServer') - this.options = options -} - -SignalingServer.prototype.start = function(port) { - return new Promise((resolve, reject) => { - this.logger.info('Starting (protocol: %s)...', this.protocol) - - var server - - if (this.options['https']) { - const fs = require('fs') - - server = createHttpsServer(this, fs.readFileSync(this.options['key']), fs.readFileSync(this.options['cert'])) - } else { - server = createHttpServer(this) - } - - const WebSocketServer = require('websocket').server - - this.wsServer = new WebSocketServer({ - httpServer: server, - autoAcceptConnections: false - }) - - this.wsServer.on('request', (request) => { - this.logger.info('New connection') - - try { - this.onConnection(new Connection(request.accept(null, request.origin))) - } catch (err) { - this.logger.error('Connection rejected: %s', err) - } - }) - - server.listen(port, () => { - this.logger.info('Started, listening on port %d...', port); - - resolve() - }) - }) -} - -SignalingServer.prototype.stop = function() { - if (this.wsServer) { - this.wsServer.shutDown() - } else { - this.logger.error("Not started") - } -} - -SignalingServer.prototype.isSecure = function() { - return this.options['https'] -} - -function createHttpServer(signalingServer) { - return require('http').createServer((request, response) => { - signalingServer.logger.info('Received request for ' + request.url) - - response.writeHead(404) - response.end() - }) -} - -function createHttpsServer(signalingServer, key, cert) { - return require('https').createServer({ key: key, cert: cert }, (request, response) => { - signalingServer.logger.info('Received request for ' + request.url) - - response.writeHead(404) - response.end() - }) -} - -module.exports = SignalingServer diff --git a/net_drivers/webrtc/package-lock.json b/net_drivers/webrtc/package-lock.json deleted file mode 100644 index e925308..0000000 --- a/net_drivers/webrtc/package-lock.json +++ /dev/null @@ -1,594 +0,0 @@ -{ - "name": "nbnet", - "version": "1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "nbnet", - "version": "1.0", - "license": "MIT", - "dependencies": { - "@roamhq/wrtc": "^0.8.0", - "websocket": "^1.0.31", - "winston": "^3.2.1" - } - }, - "node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", - "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", - "license": "MIT", - "dependencies": { - "@so-ric/colorspace": "^1.1.6", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "node_modules/@roamhq/wrtc": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc/-/wrtc-0.8.0.tgz", - "integrity": "sha512-C0V/nqc4/2xzORI5qa4mIeN/8UO3ywN1kInrJ9u6GljFx0D18JMUJEqe8yYHa61RrEeoWN3PKdW++k8TocSx/A==", - "license": "BSD-2-Clause", - "optionalDependencies": { - "@roamhq/wrtc-darwin-arm64": "0.8.0", - "@roamhq/wrtc-darwin-x64": "0.8.0", - "@roamhq/wrtc-linux-arm64": "0.8.1", - "@roamhq/wrtc-linux-x64": "0.8.1", - "@roamhq/wrtc-win32-x64": "0.8.0", - "domexception": "^4.0.0" - } - }, - "node_modules/@roamhq/wrtc-darwin-arm64": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-darwin-arm64/-/wrtc-darwin-arm64-0.8.0.tgz", - "integrity": "sha512-OtV2KWO7zOG3L8TF3KCt9aucynVCD/ww2xeXXgg+FLkya3ca0uzehN8EQJ3BL4tkInksbFJ2ssyu9cehfJ3ZuA==", - "cpu": [ - "arm64" - ], - "license": "BSD-2-Clause", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@roamhq/wrtc-darwin-x64": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-darwin-x64/-/wrtc-darwin-x64-0.8.0.tgz", - "integrity": "sha512-VY7Vzt/SDDDCpW//h8GW9bOZrOr8gWXPZVD9473ypl4jyBIoO57yyLbHzd1G0vBUkS6szsHlQCz1WwpI30YL+g==", - "cpu": [ - "x64" - ], - "license": "BSD-2-Clause", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@roamhq/wrtc-linux-arm64": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-linux-arm64/-/wrtc-linux-arm64-0.8.1.tgz", - "integrity": "sha512-FBJLLazlWkGQUXaokC/rTbrUQbb0CNFYry52fZGstufrGLTWu+g4HcwXdVvxh1tnVtVMvkQGk+mlOL52sCxw0A==", - "cpu": [ - "arm64" - ], - "license": "BSD-2-Clause", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@roamhq/wrtc-linux-x64": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-linux-x64/-/wrtc-linux-x64-0.8.1.tgz", - "integrity": "sha512-I9oWG7b4uvWO1IOR/aF34n+ID6TKVuSs0jd19h5KdhfRtw7FFh9xxuwN9rONPxLVa6fS0q+MCZgAf8Scz89L8Q==", - "cpu": [ - "x64" - ], - "license": "BSD-2-Clause", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@roamhq/wrtc-win32-x64": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-win32-x64/-/wrtc-win32-x64-0.8.0.tgz", - "integrity": "sha512-R2fxl41BLWPiP4eaTHGLzbbVvRjx1mV/OsgINCvawO7Hwz5Zx9I45+Fhrw3hd4n5amIeSG9VIF7Kz8eeTFXTGQ==", - "cpu": [ - "x64" - ], - "license": "BSD-2-Clause", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@so-ric/colorspace": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", - "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", - "license": "MIT", - "dependencies": { - "color": "^5.0.2", - "text-hex": "1.0.x" - } - }, - "node_modules/@types/triple-beam": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", - "license": "MIT" - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/bufferutil": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.1.0.tgz", - "integrity": "sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, - "node_modules/color": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", - "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", - "license": "MIT", - "dependencies": { - "color-convert": "^3.1.3", - "color-string": "^2.1.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/color-convert": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", - "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", - "license": "MIT", - "dependencies": { - "color-name": "^2.0.0" - }, - "engines": { - "node": ">=14.6" - } - }, - "node_modules/color-name": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", - "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", - "license": "MIT", - "engines": { - "node": ">=12.20" - } - }, - "node_modules/color-string": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", - "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", - "license": "MIT", - "dependencies": { - "color-name": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/d": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", - "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.64", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "deprecated": "Use your platform's native DOMException instead", - "license": "MIT", - "optional": true, - "dependencies": { - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", - "license": "MIT" - }, - "node_modules/es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "hasInstallScript": true, - "license": "ISC", - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", - "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", - "license": "ISC", - "dependencies": { - "d": "^1.0.2", - "ext": "^1.7.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "license": "ISC", - "dependencies": { - "type": "^2.7.2" - } - }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", - "license": "MIT" - }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", - "license": "MIT" - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "license": "MIT" - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", - "license": "MIT" - }, - "node_modules/logform": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", - "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", - "license": "MIT", - "dependencies": { - "@colors/colors": "1.6.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/logform/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "license": "ISC" - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "license": "MIT", - "dependencies": { - "fn.name": "1.x.x" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safe-stable-stringify": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", - "license": "MIT" - }, - "node_modules/triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "license": "MIT", - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/type": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", - "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", - "license": "ISC" - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "license": "MIT", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "license": "BSD-2-Clause", - "optional": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/websocket": { - "version": "1.0.35", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", - "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", - "license": "Apache-2.0", - "dependencies": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.63", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/winston": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", - "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", - "license": "MIT", - "dependencies": { - "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.8", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.7.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.9.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/winston-transport": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", - "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", - "license": "MIT", - "dependencies": { - "logform": "^2.7.0", - "readable-stream": "^3.6.2", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "license": "MIT", - "engines": { - "node": ">=0.10.32" - } - } - } -} diff --git a/net_drivers/webrtc/package.json b/net_drivers/webrtc/package.json deleted file mode 100644 index 89e4355..0000000 --- a/net_drivers/webrtc/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "nbnet", - "version": "1.0", - "description": "WebRTC driver for the nbnet library", - "author": "BIAGINI Nathan", - "license": "MIT", - "main": "js/index.js", - "dependencies": { - "websocket": "^1.0.31", - "winston": "^3.2.1", - "@roamhq/wrtc": "^0.8.0" - } -} diff --git a/net_drivers/webrtc_native.h b/net_drivers/webrtc_native.h deleted file mode 100644 index 73f5bf3..0000000 --- a/net_drivers/webrtc_native.h +++ /dev/null @@ -1,683 +0,0 @@ -/* - -Copyright (C) 2024 BIAGINI Nathan - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -*/ - -/* - --- NBNET NATIVE WEBRTC DRIVER --- - - WebRTC driver for the nbnet library, using a single unreliable data channel. As opposed to the other emscripten/JS - based WebRTC driver (webrtc.h), this one is fully written in C99 and can be compiled as a native application. - - Dependencies: - - 1. libdatachannel (https://github.com/paullouisageneau/libdatachannel) - 2. json.h (https://github.com/sheredom/json.h) - - How to use: - - 1. Include this header *once* after the nbnet header in the same file where you defined the NBNET_IMPL macro - 2. Call NBN_WebRTC_Native_Register in both your client and server code before calling NBN_GameClient_Start or - NBN_GameServer_Start -*/ - -#include "json.h" -#include -#include -#include -#include -#include - -#ifndef NBNET_H -#include "../nbnet.h" -#endif - -#define NBN_WEBRTC_NATIVE_DRIVER_ID 2 -#define NBN_WEBRTC_NATIVE_DRIVER_NAME "WebRTC_Native" - -typedef struct NBN_WebRTC_Native_Config { - bool enable_tls; - const char *cert_path; - const char *key_path; - const char *passphrase; - const char **ice_servers; - unsigned int ice_servers_count; - rtcLogLevel log_level; -} NBN_WebRTC_Native_Config; - -static NBN_WebRTC_Native_Config nbn_wrtc_c_cfg; - -void NBN_WebRTC_Native_Register(NBN_WebRTC_Native_Config config); -void NBN_WebRTC_Native_Unregister(void); - -#ifdef NBNET_IMPL - -typedef struct { - int id; - int channel_id; - int ws; - NBN_Connection *conn; -} NBN_WebRTC_Native_Peer; - -static void NBN_WebRTC_Native_DestroyPeer(NBN_WebRTC_Native_Peer *peer); - -#pragma region String utils - -// IMPORTANT: res needs to be pre allocated and big enough to old the resulting string -static void NBN_WebRTC_Native_StringReplaceAll(char *res, const char *str, const char *a, const char *b) { - char *substr = (char *)strstr(str, a); - size_t len_a = strlen(a); - size_t len_b = strlen(b); - - if (substr) { - int pos = substr - str; - - strncpy(res, str, pos); - strncpy(res + pos, b, len_b); - - NBN_WebRTC_Native_StringReplaceAll(res + pos + len_b, str + pos + len_a, a, b); - } else { - strncpy(res, str, strlen(str) + 1); - } -} - -#pragma endregion /* String utils */ - -#pragma region WebRTC common - -static char *NBN_WebRTC_Native_ParseSignalingMessage(const char *msg, size_t msg_len, const char *type) { - char *sdp = NULL; - struct json_value_s *root = json_parse(msg, msg_len); // this has to be freed - - struct json_object_s *object = (struct json_object_s *)root->payload; - struct json_object_element_s *curr = object->start; - - if (root->type != json_type_object) { - LogDebug("Received an invalid signaling message: %s", msg); - goto leave_free_root; - } - - while (curr != NULL) { - if (strncmp(curr->name->string, "type", 4) == 0) { - struct json_string_s *str = json_value_as_string(curr->value); - - if (strncmp(str->string, type, strlen(type))) { - // unexpected type - NBN_LogDebug("Received a signaling message with an unexpected type: %s (expected: %s)", str->string, - type); - sdp = NULL; - goto leave_free_root; - } - } else if (strncmp(curr->name->string, "sdp", 3) == 0) { - struct json_string_s *str = json_value_as_string(curr->value); - - if (str) { - // strdup equivalent using malloc, make sure this get freed - size_t len = strlen(str->string); - - sdp = (char *)malloc(len + 1); - memcpy(sdp, str->string, len + 1); - } - } - - curr = curr->next; - } - -leave_free_root: - free(root); - - return sdp; -} - -static char *NBN_WebRTC_Native_EscapeSDP(const char *sdp) { - size_t len = strlen(sdp) * 2; // TODO: kinda lame way of making sure it's going to be big enough, find a better way - char *escaped_sdp = (char *)malloc(len); - - NBN_WebRTC_Native_StringReplaceAll(escaped_sdp, sdp, "\r\n", "\\r\\n"); - - return escaped_sdp; -} - -static void NBN_WebRTC_Native_Log(rtcLogLevel level, const char *msg) { - switch (level) { - case RTC_LOG_FATAL: - case RTC_LOG_ERROR: - NBN_LogError("%s", msg); - break; - - case RTC_LOG_WARNING: - NBN_LogWarning("%s", msg); - break; - - case RTC_LOG_INFO: - NBN_LogInfo("%s", msg); - break; - - case RTC_LOG_DEBUG: - NBN_LogDebug("%s", msg); - break; - - case RTC_LOG_VERBOSE: - NBN_LogTrace("%s", msg); - break; - - case RTC_LOG_NONE: - break; - } -} - -static void NBN_WebRTC_Native_OnWsError(int ws, const char *err_msg, void *user_ptr) { - (void)user_ptr; - - NBN_LogError("Error on WS %d: %s", ws, err_msg); -} - -static NBN_WebRTC_Native_Peer *NBN_WebRTC_Native_CreatePeer(int ws, rtcDescriptionCallbackFunc on_rtc_description_cb, - rtcStateChangeCallbackFunc on_state_change_cb) { - rtcConfiguration rtcCfg = {.iceServers = nbn_wrtc_c_cfg.ice_servers, - .iceServersCount = (int)nbn_wrtc_c_cfg.ice_servers_count, - .disableAutoNegotiation = false}; - int peer_id = rtcCreatePeerConnection(&rtcCfg); - - if (peer_id < 0) { - NBN_LogError("Failed to create peer: %d", peer_id); - return NULL; - } - - NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)malloc(sizeof(NBN_WebRTC_Native_Peer)); - - peer->id = peer_id; - peer->ws = ws; - peer->channel_id = -1; - - rtcSetUserPointer(peer_id, peer); - rtcSetUserPointer(ws, peer); - - int ret = rtcSetLocalDescriptionCallback(peer->id, on_rtc_description_cb); - - if (ret < 0) { - NBN_LogError("Failed to register local description callback for peer %d: %d", peer->id, ret); - NBN_WebRTC_Native_DestroyPeer(peer); - return NULL; - } - - ret = rtcSetStateChangeCallback(peer_id, on_state_change_cb); - - if (ret < 0) { - NBN_LogError("Failed to register state change callback for peer %d: %d", peer_id, ret); - NBN_WebRTC_Native_DestroyPeer(peer); - return NULL; - } - rtcDataChannelInit rtcDataChannel = { - .reliability = {.unordered = true, .unreliable = true, .maxPacketLifeTime = 1000, .maxRetransmits = 0}, - .negotiated = true, - .manualStream = true, - .stream = 0}; - int channel_id = rtcCreateDataChannelEx(peer_id, "unreliable", &rtcDataChannel); - - if (channel_id < 0) { - NBN_LogError("Failed to create data channel for peer %d: %d", peer_id, channel_id); - NBN_WebRTC_Native_DestroyPeer(peer); - return NULL; - } - - peer->channel_id = channel_id; - - NBN_LogDebug("Successfully created data channel for peer %d: %d", peer_id, channel_id); - - return peer; -} - -static void NBN_WebRTC_Native_DestroyPeer(NBN_WebRTC_Native_Peer *peer) { - NBN_LogDebug("Destroying peer %d", peer->id); - - if (peer->channel_id >= 0) { - rtcDeleteDataChannel(peer->channel_id); - } - - rtcDeletePeerConnection(peer->id); - rtcDelete(peer->ws); - free(peer); -} - -static void NBN_WebRTC_Native_ProcessLocalDescription(NBN_WebRTC_Native_Peer *peer, const char *sdp, const char *type) { - char *escaped_sdp = NBN_WebRTC_Native_EscapeSDP(sdp); - size_t signaling_json_size = snprintf(NULL, 0, "{\"type\":\"%s\", \"sdp\":\"%s\"}", type, escaped_sdp) + 1; - - char *signaling_json = (char *)malloc(signaling_json_size); - - snprintf(signaling_json, signaling_json_size, "{\"type\":\"%s\", \"sdp\":\"%s\"}", type, escaped_sdp); - NBN_LogDebug("Send signaling message of type %s to remote connection: %s", type, signaling_json); - - // pass -1 as the size (assume signaling_json to be a null-terminated string) - if (rtcSendMessage(peer->ws, signaling_json, -1) < 0) { - NBN_WebRTC_Native_DestroyPeer(peer); - } - free(signaling_json); - free(escaped_sdp); -} - -static void NBN_WebRTC_Native_ProcessSignalingMessage(NBN_WebRTC_Native_Peer *peer, int ws, const char *msg, int size, - const char *type) { - // for some reason the size of the message is negative - // in libdatachannel documentation (https://github.com/paullouisageneau/libdatachannel/blob/master/DOC.md) there is - // mention of: size: if size >= 0, data is interpreted as a binary message of length size, otherwise it is - // interpreted as a null-terminated UTF-8 string. so I guess in this case msg is a null terminated string? I could - // not find more information about this so I decided to go with flipping the size to positive even though it feels - // weird, but it works so... ¯\_(ツ)_/¯ - - if (size < 0) - size *= -1; - size -= 1; - - NBN_LogDebug("Received signaling message on WS %d (size: %d): %s", ws, size, msg); - - char *sdp = NBN_WebRTC_Native_ParseSignalingMessage(msg, size, type); - - if (!sdp) { - NBN_LogWarning("Failed to parse signaling data for WS %d", ws); - return; - } - - NBN_LogDebug("Successfully parsed signaling payload (sdp: %s)", sdp); - - int ret = rtcSetRemoteDescription(peer->id, sdp, type); - - if (ret < 0) { - NBN_LogError("Failed to set remote description for peer %d (WS: %d): %d", peer->id, ws, ret); - rtcClose(ws); - } - - // IMPORTANT: not sure I can free this because it's passed to rtcSetRemoteDescription - free(sdp); -} - -#pragma endregion /* WebRTC common */ - -#pragma region Game server - -typedef struct NBN_WebRTC_Native_Server { - int wsserver; - struct { - int key; - NBN_WebRTC_Native_Peer *value; - } *peers; - uint16_t ws_port; - uint32_t protocol_id; -} NBN_WebRTC_Native_Server; - -static NBN_WebRTC_Native_Server nbn_wrtc_native_serv = {0, NULL, 0, 0}; - -static void NBN_WebRTC_Native_Serv_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { - NBN_LogDebug("Processing local description of type '%s'", type); - - if (strncmp(type, "answer", strlen("answer")) != 0) { - NBN_LogWarning("Ignoring local description of type '%s' (expected 'answer')", type); - return; - } - - NBN_WebRTC_Native_ProcessLocalDescription((NBN_WebRTC_Native_Peer *)user_ptr, sdp, "answer"); -} - -static void NBN_WebRTC_Native_Serv_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { - NBN_LogDebug("Peer %d state changed to %d", pc, state); - - if (state == RTC_CONNECTED) { - NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)user_ptr; - - NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, peer->conn); - NBN_LogDebug("Peer %d is connected !", pc); - } -} - -static void NBN_WebRTC_Native_Serv_OnWsOpen(int ws, void *user_ptr) { - NBN_LogDebug("WS %d is open", ws); - - NBN_WebRTC_Native_Peer *peer = NBN_WebRTC_Native_CreatePeer(ws, NBN_WebRTC_Native_Serv_OnLocalDescription, - NBN_WebRTC_Native_Serv_OnPeerStateChanged); - - if (!peer) { - NBN_LogError("Failed to create peer"); - return; - } - - peer->conn = NBN_GameServer_CreateClientConnection(NBN_WEBRTC_NATIVE_DRIVER_ID, peer, peer->id); - hmput(nbn_wrtc_native_serv.peers, peer->id, peer); -} - -static void NBN_WebRTC_Native_Serv_OnWsClosed(int ws, void *user_ptr) { - NBN_LogDebug("WS %d has closed", ws); - - if (user_ptr) { - NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)user_ptr; - - NBN_LogDebug("Closing WebRTC peer and channel (peer: %d, channel: %d)", peer->id, peer->channel_id); - - rtcClose(peer->channel_id); - rtcClosePeerConnection(peer->id); - } -} - -static void NBN_WebRTC_Native_Serv_OnWsMessage(int ws, const char *msg, int size, void *user_ptr) { - NBN_WebRTC_Native_ProcessSignalingMessage((NBN_WebRTC_Native_Peer *)user_ptr, ws, msg, size, "offer"); -} - -static void NBN_WebRTC_Native_Serv_OnWsConnection(int wsserver, int ws, void *user_ptr) { - NBN_LogDebug("New WS connection %d (user_ptr: %p)", ws, user_ptr); - - rtcSetOpenCallback(ws, NBN_WebRTC_Native_Serv_OnWsOpen); - rtcSetClosedCallback(ws, NBN_WebRTC_Native_Serv_OnWsClosed); - rtcSetErrorCallback(ws, NBN_WebRTC_Native_OnWsError); - rtcSetMessageCallback(ws, NBN_WebRTC_Native_Serv_OnWsMessage); -} - -static int NBN_WebRTC_Native_ServStart(uint32_t protocol_id, uint16_t port) { - nbn_wrtc_native_serv.ws_port = port; - nbn_wrtc_native_serv.protocol_id = protocol_id; - nbn_wrtc_native_serv.wsserver = -1; - - rtcInitLogger(nbn_wrtc_c_cfg.log_level, NBN_WebRTC_Native_Log); - rtcPreload(); - - rtcWsServerConfiguration cfg = {.port = port, - .enableTls = nbn_wrtc_c_cfg.enable_tls, - .certificatePemFile = nbn_wrtc_c_cfg.cert_path, - .keyPemFile = nbn_wrtc_c_cfg.key_path, - .keyPemPass = nbn_wrtc_c_cfg.passphrase}; - - int wsserver = rtcCreateWebSocketServer(&cfg, NBN_WebRTC_Native_Serv_OnWsConnection); - - if (wsserver < 0) { - NBN_LogError("Failed to start WS server (code: %d)", wsserver); - return NBN_ERROR; - } - - nbn_wrtc_native_serv.wsserver = wsserver; - - hmdefault(nbn_wrtc_native_serv.peers, NULL); - - return 0; -} - -static void NBN_WebRTC_Native_ServStop(void) { - hmfree(nbn_wrtc_native_serv.peers); - - if (nbn_wrtc_native_serv.wsserver >= 0) { - rtcDeleteWebSocketServer(nbn_wrtc_native_serv.wsserver); - } - - rtcCleanup(); -} - -static int NBN_WebRTC_Native_ServRecvPackets(void) { - static NBN_Packet packet = {0}; - const int buffer_size = sizeof(packet.buffer); - int size = buffer_size; - - for (unsigned int i = 0; i < hmlen(nbn_wrtc_native_serv.peers); i++) { - NBN_WebRTC_Native_Peer *peer = nbn_wrtc_native_serv.peers[i].value; - - while (rtcReceiveMessage(peer->channel_id, (char *)packet.buffer, &size) == RTC_ERR_SUCCESS) { - if (NBN_Packet_InitRead(&packet, nbn_wrtc_native_serv.protocol_id, size) < 0) - continue; - - packet.sender = peer->conn; - NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet); - size = buffer_size; - } - } - - return 0; -} - -static void NBN_WebRTC_Native_ServRemoveClientConnection(NBN_Connection *conn) { - NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)conn->driver_data; - - int ret = hmdel(nbn_wrtc_native_serv.peers, peer->id); - - NBN_Assert(ret == 1); - NBN_WebRTC_Native_DestroyPeer(peer); -} - -static int NBN_WebRTC_Native_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *conn) { - if (!conn->is_accepted) - return 0; - - NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)conn->driver_data; - - if (rtcSendMessage(peer->channel_id, (char *)packet->buffer, packet->size) < 0) { - NBN_LogError("rtcSendMessage failed for peer %d", peer->id); - } - - return 0; -} - -#pragma endregion /* Game server */ - -#pragma region Game client - -typedef struct NBN_WebRTC_Native_Client { - uint32_t protocol_id; - bool is_connected; - NBN_WebRTC_Native_Peer *peer; -} NBN_WebRTC_Native_Client; - -static NBN_WebRTC_Native_Client nbn_wrtc_native_cli = {0, false, false}; - -static void NBN_WebRTC_Native_Cli_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { - NBN_LogDebug("Processing local description of type '%s'", type); - - if (strncmp(type, "offer", strlen("offer")) != 0) { - NBN_LogWarning("Ignoring local description of type '%s' (expected 'offer')", type); - return; - } - - NBN_WebRTC_Native_ProcessLocalDescription((NBN_WebRTC_Native_Peer *)user_ptr, sdp, "offer"); -} - -static void NBN_WebRTC_Native_Cli_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { - NBN_LogDebug("Server peer state changed to %d", pc, state); - - if (state == RTC_CONNECTED) { - NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)user_ptr; - - NBN_LogDebug("Server peer is connected !", pc); - nbn_wrtc_native_cli.is_connected = true; - } -} - -static void NBN_WebRTC_Native_Cli_OnWsOpen(int ws, void *user_ptr) { - NBN_LogDebug("WS %d is open, creating peer...", ws); - - NBN_WebRTC_Native_Peer *peer = NBN_WebRTC_Native_CreatePeer(ws, NBN_WebRTC_Native_Cli_OnLocalDescription, - NBN_WebRTC_Native_Cli_OnPeerStateChanged); - - if (!peer) { - NBN_LogError("Failed to create peer"); - return; - } - - NBN_LogDebug("Successfully created peer: %d", peer->id); - - peer->conn = NBN_GameClient_CreateServerConnection(NBN_WEBRTC_NATIVE_DRIVER_ID, peer); - nbn_wrtc_native_cli.peer = peer; -} - -static void NBN_WebRTC_Native_Cli_OnWsClosed(int ws, void *user_ptr) { - NBN_LogDebug("WS %d has closed", ws); - - if (user_ptr) { - NBN_WebRTC_Native_Peer *peer = (NBN_WebRTC_Native_Peer *)user_ptr; - - NBN_LogDebug("Destroying server peer (peer: %d, channel: %d)", peer->id, peer->channel_id); - NBN_WebRTC_Native_DestroyPeer(peer); - } -} - -static void NBN_WebRTC_Native_Cli_OnWsMessage(int ws, const char *msg, int size, void *user_ptr) { - NBN_WebRTC_Native_ProcessSignalingMessage((NBN_WebRTC_Native_Peer *)user_ptr, ws, msg, size, "answer"); -} - -static int AttemptConnection(void) { - double waitTime = 1.0 / 3.0; // wait time between connection attempts in seconds - - int retries = 9; // approximatively 3 seconds to get connected - - struct timespec rqtp; - - rqtp.tv_sec = 0; - rqtp.tv_nsec = waitTime * 1e9; - - while (true) { -#if defined(_WIN32) || defined(_WIN64) - Sleep(waitTime * 1000); -#else - if (nanosleep(&rqtp, NULL) < 0) { - NBN_LogError("nanosleep failed"); - return NBN_ERROR; - } -#endif - if (--retries <= 0 || nbn_wrtc_native_cli.is_connected) { - break; - } - } - - if (!nbn_wrtc_native_cli.is_connected) { - NBN_LogError("Failed to connect"); - - return NBN_ERROR; - } - - return 0; -} - -static int NBN_WebRTC_Native_CliStart(uint32_t protocol_id, const char *host, uint16_t port) { - rtcInitLogger(nbn_wrtc_c_cfg.log_level, NBN_WebRTC_Native_Log); - rtcPreload(); - - char ws_addr[256] = {0}; - - snprintf(ws_addr, sizeof(ws_addr), "ws://%s:%d", host, port); - - int cli_ws; - - if ((cli_ws = rtcCreateWebSocket(ws_addr)) < 0) { - NBN_LogError("Failed to create websocket"); - return NBN_ERROR; - } - - nbn_wrtc_native_cli.protocol_id = protocol_id; - - NBN_LogDebug("Successfully created client WS: %d", cli_ws); - - rtcSetOpenCallback(cli_ws, NBN_WebRTC_Native_Cli_OnWsOpen); - rtcSetClosedCallback(cli_ws, NBN_WebRTC_Native_Cli_OnWsClosed); - rtcSetErrorCallback(cli_ws, NBN_WebRTC_Native_OnWsError); - rtcSetMessageCallback(cli_ws, NBN_WebRTC_Native_Cli_OnWsMessage); - - return AttemptConnection(); -} - -static void NBN_WebRTC_Native_CliStop(void) { - NBN_WebRTC_Native_Peer *peer = nbn_wrtc_native_cli.peer; - - if (peer) { - NBN_WebRTC_Native_DestroyPeer(peer); - } - - nbn_wrtc_native_cli.is_connected = false; - rtcCleanup(); -} - -static int NBN_WebRTC_Native_CliRecvPackets(void) { - static NBN_Packet packet = {0}; - const int buffer_size = sizeof(packet.buffer); - int size = buffer_size; - NBN_WebRTC_Native_Peer *peer = nbn_wrtc_native_cli.peer; - - while (rtcReceiveMessage(peer->channel_id, (char *)packet.buffer, &size) == RTC_ERR_SUCCESS) { - if (NBN_Packet_InitRead(&packet, nbn_wrtc_native_cli.protocol_id, size) < 0) - continue; - - packet.sender = peer->conn; - NBN_Driver_RaiseEvent(NBN_DRIVER_CLI_PACKET_RECEIVED, &packet); - size = buffer_size; - } - - return 0; -} - -static int NBN_WebRTC_Native_CliSendPacket(NBN_Packet *packet) { - NBN_WebRTC_Native_Peer *peer = nbn_wrtc_native_cli.peer; - - if (rtcSendMessage(peer->channel_id, (char *)packet->buffer, packet->size) < 0) { - NBN_LogError("rtcSendMessage failed for peer %d", peer->id); - return NBN_ERROR; - } - - return 0; -} - -#pragma endregion /* Game client */ - -void NBN_WebRTC_Native_Register(NBN_WebRTC_Native_Config config) { - const char **ice_servers = (const char **)malloc(sizeof(char *) * config.ice_servers_count); - - for (unsigned int i = 0; i < config.ice_servers_count; i++) { - // strdup equivalent using malloc, make sure this get freed - const char *str = config.ice_servers[i]; - size_t len = strlen(str); - char *dup_str = (char *)malloc(len + 1); - - memcpy(dup_str, str, len + 1); - ice_servers[i] = dup_str; - } - - nbn_wrtc_c_cfg.ice_servers = ice_servers; - nbn_wrtc_c_cfg.ice_servers_count = config.ice_servers_count; - nbn_wrtc_c_cfg.enable_tls = config.enable_tls; - - if (config.enable_tls) { - nbn_wrtc_c_cfg.cert_path = config.cert_path; - nbn_wrtc_c_cfg.key_path = config.key_path; - nbn_wrtc_c_cfg.passphrase = config.passphrase; - } - - NBN_DriverImplementation driver_impl = {// Client implementation - NBN_WebRTC_Native_CliStart, NBN_WebRTC_Native_CliStop, - NBN_WebRTC_Native_CliRecvPackets, NBN_WebRTC_Native_CliSendPacket, - - // Server implementation - NBN_WebRTC_Native_ServStart, NBN_WebRTC_Native_ServStop, - NBN_WebRTC_Native_ServRecvPackets, NBN_WebRTC_Native_ServSendPacketTo, - NBN_WebRTC_Native_ServRemoveClientConnection}; - - NBN_Driver_Register(NBN_WEBRTC_NATIVE_DRIVER_ID, NBN_WEBRTC_NATIVE_DRIVER_NAME, driver_impl); -} - -void NBN_WebRTC_Native_Unregister(void) { - for (unsigned int i = 0; i < nbn_wrtc_c_cfg.ice_servers_count; i++) { - free((void *)nbn_wrtc_c_cfg.ice_servers[i]); - } - - free(nbn_wrtc_c_cfg.ice_servers); -} - -#endif // NBNET_IMPL From 3bb6d76b0b1b77e480743081a452a0f27de54f27 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sat, 14 Feb 2026 07:42:37 +0100 Subject: [PATCH 61/85] fix soak cmake script --- soak/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/soak/CMakeLists.txt b/soak/CMakeLists.txt index aba4959..21567c2 100644 --- a/soak/CMakeLists.txt +++ b/soak/CMakeLists.txt @@ -54,8 +54,8 @@ if (WEBRTC_NATIVE) message("Compiling with WebRTC native driver") - target_compile_definitions(server PUBLIC WEBRTC_NATIVE) - target_compile_definitions(client PUBLIC WEBRTC_NATIVE) + target_compile_definitions(server PUBLIC NBN_WEBRTC_NATIVE) + target_compile_definitions(client PUBLIC NBN_WEBRTC_NATIVE) target_link_libraries(server ${LIBDATACHANNEL_LIBRARY_PATH} m) target_link_libraries(client ${LIBDATACHANNEL_LIBRARY_PATH} m) From c4dd73cf0197de741d06adf1247b50a8e5aa283c Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sat, 14 Feb 2026 09:00:50 +0100 Subject: [PATCH 62/85] move webrtc js code in webrtc folder --- examples/echo/CMakeLists.txt | 4 +- examples/echo/package-lock.json | 6 +- examples/echo/package.json | 2 +- examples/raylib/CMakeLists.txt | 4 +- examples/raylib/package-lock.json | 6 +- examples/raylib/package.json | 2 +- webrtc/package-lock.json | 594 ++++++++++++++++++++++++++++++ webrtc/package.json | 13 + 8 files changed, 619 insertions(+), 12 deletions(-) create mode 100644 webrtc/package-lock.json create mode 100644 webrtc/package.json diff --git a/examples/echo/CMakeLists.txt b/examples/echo/CMakeLists.txt index b8bcc6e..ca29831 100644 --- a/examples/echo/CMakeLists.txt +++ b/examples/echo/CMakeLists.txt @@ -80,7 +80,7 @@ endif (UNIX) if (EMSCRIPTEN) set(ASYNCIFY_IMPORTS "[\"__js_game_server_start\", \"__js_game_client_start\", \"__js_game_client_close\"]") - set_target_properties(echo_server PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/api.js \ + set_target_properties(echo_server PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../webrtc/js/api.js \ -s ALLOW_MEMORY_GROWTH=1 \ -s TOTAL_MEMORY=30MB \ -s EXIT_RUNTIME=1 \ @@ -88,7 +88,7 @@ if (EMSCRIPTEN) -s ASYNCIFY \ -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") - set_target_properties(echo_client PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/api.js \ + set_target_properties(echo_client PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../webrtc/js/api.js \ -s ALLOW_MEMORY_GROWTH=1 \ -s TOTAL_MEMORY=30MB \ -s EXIT_RUNTIME=1 \ diff --git a/examples/echo/package-lock.json b/examples/echo/package-lock.json index 6b4d0aa..87126cd 100644 --- a/examples/echo/package-lock.json +++ b/examples/echo/package-lock.json @@ -5,10 +5,10 @@ "packages": { "": { "dependencies": { - "nbnet": "file:../../net_drivers/webrtc" + "nbnet": "file:../../webrtc" } }, - "../../net_drivers/webrtc": { + "../../webrtc": { "name": "nbnet", "version": "1.0", "license": "MIT", @@ -19,7 +19,7 @@ } }, "node_modules/nbnet": { - "resolved": "../../net_drivers/webrtc", + "resolved": "../../webrtc", "link": true } } diff --git a/examples/echo/package.json b/examples/echo/package.json index b7289f6..5826448 100644 --- a/examples/echo/package.json +++ b/examples/echo/package.json @@ -4,6 +4,6 @@ "client": "node build_web/echo_client.js" }, "dependencies": { - "nbnet": "file:../../net_drivers/webrtc" + "nbnet": "file:../../webrtc" } } diff --git a/examples/raylib/CMakeLists.txt b/examples/raylib/CMakeLists.txt index 58a6a51..7f1ee98 100644 --- a/examples/raylib/CMakeLists.txt +++ b/examples/raylib/CMakeLists.txt @@ -80,14 +80,14 @@ unset(USE_HTTPS) if (EMSCRIPTEN) set(ASYNCIFY_IMPORTS "[\"__js_game_server_start\", \"__js_game_client_start\", \"__js_game_client_close\"]") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=30MB -s EXIT_RUNTIME=1 -s ASSERTIONS=1 -s ASYNCIFY -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/api.js") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../webrtc/js/api.js") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s USE_GLFW=3 --shell-file ${CMAKE_CURRENT_SOURCE_DIR}/shell.html") set_target_properties(raylib_client PROPERTIES SUFFIX ".html") add_custom_command( TARGET raylib_client POST_BUILD - COMMAND browserify ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/nbnet.js -o nbnet_bundle.js) + COMMAND browserify ${CMAKE_CURRENT_SOURCE_DIR}/../../webrtc/js/nbnet.js -o nbnet_bundle.js) endif() if (APPLE) diff --git a/examples/raylib/package-lock.json b/examples/raylib/package-lock.json index 55a8370..bac9863 100644 --- a/examples/raylib/package-lock.json +++ b/examples/raylib/package-lock.json @@ -5,10 +5,10 @@ "packages": { "": { "dependencies": { - "nbnet": "file:../../net_drivers/webrtc" + "nbnet": "file:../../webrtc" } }, - "../../net_drivers/webrtc": { + "../../webrtc": { "name": "nbnet", "version": "1.0", "license": "MIT", @@ -19,7 +19,7 @@ } }, "node_modules/nbnet": { - "resolved": "../../net_drivers/webrtc", + "resolved": "../../webrtc", "link": true } } diff --git a/examples/raylib/package.json b/examples/raylib/package.json index 4476b29..03e752a 100644 --- a/examples/raylib/package.json +++ b/examples/raylib/package.json @@ -3,6 +3,6 @@ "server": "node raylib_server.js" }, "dependencies": { - "nbnet": "file:../../net_drivers/webrtc" + "nbnet": "file:../../webrtc" } } diff --git a/webrtc/package-lock.json b/webrtc/package-lock.json new file mode 100644 index 0000000..e925308 --- /dev/null +++ b/webrtc/package-lock.json @@ -0,0 +1,594 @@ +{ + "name": "nbnet", + "version": "1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nbnet", + "version": "1.0", + "license": "MIT", + "dependencies": { + "@roamhq/wrtc": "^0.8.0", + "websocket": "^1.0.31", + "winston": "^3.2.1" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "license": "MIT", + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@roamhq/wrtc": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@roamhq/wrtc/-/wrtc-0.8.0.tgz", + "integrity": "sha512-C0V/nqc4/2xzORI5qa4mIeN/8UO3ywN1kInrJ9u6GljFx0D18JMUJEqe8yYHa61RrEeoWN3PKdW++k8TocSx/A==", + "license": "BSD-2-Clause", + "optionalDependencies": { + "@roamhq/wrtc-darwin-arm64": "0.8.0", + "@roamhq/wrtc-darwin-x64": "0.8.0", + "@roamhq/wrtc-linux-arm64": "0.8.1", + "@roamhq/wrtc-linux-x64": "0.8.1", + "@roamhq/wrtc-win32-x64": "0.8.0", + "domexception": "^4.0.0" + } + }, + "node_modules/@roamhq/wrtc-darwin-arm64": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@roamhq/wrtc-darwin-arm64/-/wrtc-darwin-arm64-0.8.0.tgz", + "integrity": "sha512-OtV2KWO7zOG3L8TF3KCt9aucynVCD/ww2xeXXgg+FLkya3ca0uzehN8EQJ3BL4tkInksbFJ2ssyu9cehfJ3ZuA==", + "cpu": [ + "arm64" + ], + "license": "BSD-2-Clause", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@roamhq/wrtc-darwin-x64": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@roamhq/wrtc-darwin-x64/-/wrtc-darwin-x64-0.8.0.tgz", + "integrity": "sha512-VY7Vzt/SDDDCpW//h8GW9bOZrOr8gWXPZVD9473ypl4jyBIoO57yyLbHzd1G0vBUkS6szsHlQCz1WwpI30YL+g==", + "cpu": [ + "x64" + ], + "license": "BSD-2-Clause", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@roamhq/wrtc-linux-arm64": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@roamhq/wrtc-linux-arm64/-/wrtc-linux-arm64-0.8.1.tgz", + "integrity": "sha512-FBJLLazlWkGQUXaokC/rTbrUQbb0CNFYry52fZGstufrGLTWu+g4HcwXdVvxh1tnVtVMvkQGk+mlOL52sCxw0A==", + "cpu": [ + "arm64" + ], + "license": "BSD-2-Clause", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@roamhq/wrtc-linux-x64": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@roamhq/wrtc-linux-x64/-/wrtc-linux-x64-0.8.1.tgz", + "integrity": "sha512-I9oWG7b4uvWO1IOR/aF34n+ID6TKVuSs0jd19h5KdhfRtw7FFh9xxuwN9rONPxLVa6fS0q+MCZgAf8Scz89L8Q==", + "cpu": [ + "x64" + ], + "license": "BSD-2-Clause", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@roamhq/wrtc-win32-x64": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@roamhq/wrtc-win32-x64/-/wrtc-win32-x64-0.8.0.tgz", + "integrity": "sha512-R2fxl41BLWPiP4eaTHGLzbbVvRjx1mV/OsgINCvawO7Hwz5Zx9I45+Fhrw3hd4n5amIeSG9VIF7Kz8eeTFXTGQ==", + "cpu": [ + "x64" + ], + "license": "BSD-2-Clause", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/bufferutil": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.1.0.tgz", + "integrity": "sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/color": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "license": "MIT", + "optional": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "license": "ISC" + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "license": "ISC" + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/websocket": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", + "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", + "license": "Apache-2.0", + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.63", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/winston": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.8", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", + "engines": { + "node": ">=0.10.32" + } + } + } +} diff --git a/webrtc/package.json b/webrtc/package.json new file mode 100644 index 0000000..89e4355 --- /dev/null +++ b/webrtc/package.json @@ -0,0 +1,13 @@ +{ + "name": "nbnet", + "version": "1.0", + "description": "WebRTC driver for the nbnet library", + "author": "BIAGINI Nathan", + "license": "MIT", + "main": "js/index.js", + "dependencies": { + "websocket": "^1.0.31", + "winston": "^3.2.1", + "@roamhq/wrtc": "^0.8.0" + } +} From fe2e50db605460bdd5dbde746fee6d7c3ff3db3b Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sat, 14 Feb 2026 09:01:25 +0100 Subject: [PATCH 63/85] fix webrtc native driver --- nbnet.c | 23 ++++++++++++++++++++--- nbnet.h | 20 +++++++++++++++++--- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/nbnet.c b/nbnet.c index 6e7096e..a8140f6 100644 --- a/nbnet.c +++ b/nbnet.c @@ -526,7 +526,8 @@ static int UDP_Server_RecvPackets(NBN_GameServer *server); static int UDP_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *connection); static void UDP_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *connection); -static NBN_Driver nbn_udp_driver = {.name = "UDP", +static NBN_Driver nbn_udp_driver = {.id = NBN_DRIVER_UDP, + .name = "UDP", .impl = {// Client implementation .cli_start = UDP_Client_Start, .cli_stop = UDP_Client_Stop, @@ -569,7 +570,8 @@ static int WebRTC_Server_RecvPackets(NBN_GameServer *server); static int WebRTC_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *connection); static void WebRTC_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *connection); -static NBN_Driver nbn_webrtc_em_driver = {.name = "WebRTC_EMSCRIPTEN", +static NBN_Driver nbn_webrtc_em_driver = {.id = NBN_DRIVER_WEBRTC_EMSCRIPTEN, + .name = "WebRTC_EMSCRIPTEN", .impl = {// Client implementation .cli_start = WebRTC_Client_Start, .cli_stop = WebRTC_Client_Stop, @@ -601,6 +603,7 @@ static int WebRTC_Native_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet static void WebRTC_Native_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *connection); static NBN_Driver nbn_webrtc_native_driver = { + .id = NBN_DRIVER_WEBRTC_NATIVE, .name = "WebRTC_NATIVE", .impl = {// Client implementation .cli_start = WebRTC_Native_Client_Start, @@ -615,6 +618,8 @@ static NBN_Driver nbn_webrtc_native_driver = { .serv_send_packet_to = WebRTC_Native_Server_SendPacketTo, .serv_cleanup_connection = WebRTC_Native_Server_CleanupConnection}}; +static NBN_WebRTC_Config nbn_wrtc_cfg = NBN_WEBRTC_DEFAULT_CONFIG; + #endif // NBN_WEBRTC_NATIVE /** @@ -1874,6 +1879,7 @@ static int StartClientDrivers(const char *host, uint16_t port) { return NBN_ERROR; } + LogInfo("%s driver started", nbn_udp_driver.name); driver_count++; #endif // NBN_UDP @@ -1885,6 +1891,7 @@ static int StartClientDrivers(const char *host, uint16_t port) { return NBN_ERROR; } + LogInfo("%s driver started", nbn_webrtc_native_driver.name); driver_count++; #endif // NBN_WEBRTC_NATIVE @@ -1896,6 +1903,7 @@ static int StartClientDrivers(const char *host, uint16_t port) { return NBN_ERROR; } + LogInfo("%s driver started", nbn_webrtc_em_driver.name); driver_count++; #endif // __EMSCRIPTEN__ @@ -2303,6 +2311,7 @@ static int StartServerDrivers(uint16_t port) { return NBN_ERROR; } + LogInfo("%s driver started", nbn_udp_driver.name); driver_count++; #endif // NBN_UDP @@ -2312,6 +2321,7 @@ static int StartServerDrivers(uint16_t port) { return NBN_ERROR; } + LogInfo("%s driver started", nbn_webrtc_native_driver.name); driver_count++; #endif // NBN_WEBRTC_NATIVE @@ -2321,6 +2331,7 @@ static int StartServerDrivers(uint16_t port) { return NBN_ERROR; } + LogInfo("%s driver started", nbn_webrtc_em_driver.name); driver_count++; #endif // __EMSCRIPTEN__ @@ -2912,6 +2923,8 @@ static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev) { // at this point we know it's a connection request NBN_Assert(message_info.type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE); + LogDebug("Received a connection request from client %ul", sender->handle.id); + if (message_info.length < 4) { LogError("Connection request invalid length"); @@ -3262,7 +3275,7 @@ static int UDP_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet) { /** * JS API * - * See net_drivers/webrtc/js for the implementation of these functions. + * See webrtc/js folder for the implementation of these functions. */ extern void __js_game_server_init(uint32_t, bool, const char *, const char *); @@ -3724,6 +3737,10 @@ static int WebRTC_Native_Server_RecvPackets(NBN_GameServer *server) { for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { NBN_Connection *conn = server->clients[i].value; + + if (conn->driver->id != NBN_DRIVER_WEBRTC_NATIVE) + continue; + int channel_id = conn->driver_data.webrtc.channel_id; while (rtcReceiveMessage(channel_id, (char *)packet.buffer, &size) == RTC_ERR_SUCCESS) { diff --git a/nbnet.h b/nbnet.h index 5da2b58..c0d127a 100644 --- a/nbnet.h +++ b/nbnet.h @@ -488,10 +488,24 @@ typedef struct NBN_WebRTC_Config { rtcLogLevel log_level; } NBN_WebRTC_Config; -#define NBN_WEBRTC_DEFAULT_CONFIG(ice_servers, ice_servers_count) \ +static const char *default_ice_servers[] = {"stun:stun01.sipphone.com"}; + +#ifdef NBN_DEBUG + +#define NBN_DEFAULT_RTC_LOG_LEVEL RTC_LOG_DEBUG + +#else + +#define NBN_DEFAULT_RTC_LOG_LEVEL RTC_LOG_ERROR + +#endif // NBN_DEBUG + +#define NBN_WEBRTC_DEFAULT_CONFIG \ (NBN_WebRTC_Config) { \ - .enable_tls = false, .cert_path = NULL, .key_path = NULL, .passphrase = NULL, .ice_servers = ice_servers, \ - .ice_servers_count = ice_servers_count, .log_level = RTC_LOG_ERROR \ + .enable_tls = false, .cert_path = NULL, .key_path = NULL, .passphrase = NULL, \ + .ice_servers = default_ice_servers, \ + .ice_servers_count = sizeof(default_ice_servers) / sizeof(default_ice_servers[0]), \ + .log_level = NBN_DEFAULT_RTC_LOG_LEVEL \ } void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config); From 93e0ccbfa30006ff3db922eb6c97f71d67a919ce Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sat, 14 Feb 2026 09:02:58 +0100 Subject: [PATCH 64/85] fix soak webrtc native --- soak/CMakeLists.txt | 12 +- soak/cli_out | 869 +++++++++++++++++++++++++++++++++++++++++ soak/client.c | 34 +- soak/package-lock.json | 6 +- soak/package.json | 2 +- soak/server.c | 15 +- 6 files changed, 886 insertions(+), 52 deletions(-) create mode 100644 soak/cli_out diff --git a/soak/CMakeLists.txt b/soak/CMakeLists.txt index 21567c2..45851ae 100644 --- a/soak/CMakeLists.txt +++ b/soak/CMakeLists.txt @@ -29,6 +29,9 @@ target_compile_definitions(server PUBLIC SOAK_SERVER) target_compile_definitions(client PUBLIC NBN_CHANNEL_COUNT=4) target_compile_definitions(server PUBLIC NBN_CHANNEL_COUNT=4) +target_include_directories(client PUBLIC "../") +target_include_directories(server PUBLIC "../") + option(WEBRTC_NATIVE OFF) option(UDP OFF) @@ -52,16 +55,13 @@ if (WEBRTC_NATIVE) message(SEND_ERROR "Can't compile WebRTC native driver with emscripten") endif (EMSCRIPTEN) - message("Compiling with WebRTC native driver") + message("Compiling server with WebRTC native driver") target_compile_definitions(server PUBLIC NBN_WEBRTC_NATIVE) - target_compile_definitions(client PUBLIC NBN_WEBRTC_NATIVE) target_link_libraries(server ${LIBDATACHANNEL_LIBRARY_PATH} m) - target_link_libraries(client ${LIBDATACHANNEL_LIBRARY_PATH} m) target_include_directories(server PUBLIC "${LIBDATACHANNEL_INCLUDE_PATH}") - target_include_directories(client PUBLIC "${LIBDATACHANNEL_INCLUDE_PATH}") endif (WEBRTC_NATIVE) @@ -85,7 +85,7 @@ endif (UNIX) if (EMSCRIPTEN) set(ASYNCIFY_IMPORTS "[\"__js_game_server_start\", \"__js_game_client_start\", \"__js_game_client_close\"]") - set_target_properties(server PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../net_drivers/webrtc/js/api.js \ + set_target_properties(server PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../webrtc/js/api.js \ -s EXPORTED_RUNTIME_METHODS=[HEAPU8] \ -s TOTAL_MEMORY=30MB \ -s USE_PTHREADS=1 \ @@ -96,7 +96,7 @@ if (EMSCRIPTEN) -s ASYNCIFY \ -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") - set_target_properties(client PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../net_drivers/webrtc/js/api.js \ + set_target_properties(client PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../webrtc/js/api.js \ -s TOTAL_MEMORY=30MB \ -s USE_PTHREADS=1 \ -s PTHREAD_POOL_SIZE=4 \ diff --git a/soak/cli_out b/soak/cli_out new file mode 100644 index 0000000..3497e5a --- /dev/null +++ b/soak/cli_out @@ -0,0 +1,869 @@ + +> client +> node build_web/client.js --message_count=100 + +w:0,t:0x00000000: addRunDependency wasm-instantiate +w:0,t:0x00000000: locateFile: client.wasm scriptDirectory: /Users/nathb/Dev/GameDev/nbnet/soak/build_web/ +w:0,t:0x00000000: asynchronously preparing wasm +w:0,t:0x00000000: run() called, but dependencies remain, so not running +w:0,t:0x00000000: receiveInstance +w:0,t:0x00000000: assigning exports +w:0,t:0x00000000: removeRunDependency wasm-instantiate +w:0,t:0x00000000: writeStackCookie: 0x00000000 +w:0,t:0x00000000: addRunDependency loading-workers +w:0,t:0x00000000: run() called, but dependencies remain, so not running +w:1,t:0x00000000: addRunDependency wasm-instantiate +w:4,t:0x00000000: addRunDependency wasm-instantiate +w:1,t:0x00000000: receiveInstance +w:1,t:0x00000000: assigning exports +w:4,t:0x00000000: receiveInstance +w:1,t:0x00000000: removeRunDependency wasm-instantiate +w:1,t:0x00000000: initRuntime +w:4,t:0x00000000: assigning exports +w:4,t:0x00000000: removeRunDependency wasm-instantiate +w:2,t:0x00000000: addRunDependency wasm-instantiate +w:4,t:0x00000000: initRuntime +w:2,t:0x00000000: receiveInstance +w:2,t:0x00000000: assigning exports +w:2,t:0x00000000: removeRunDependency wasm-instantiate +w:2,t:0x00000000: initRuntime +w:3,t:0x00000000: addRunDependency wasm-instantiate +w:3,t:0x00000000: receiveInstance +w:3,t:0x00000000: assigning exports +w:3,t:0x00000000: removeRunDependency wasm-instantiate +w:3,t:0x00000000: initRuntime +w:0,t:0x00000000: removeRunDependency loading-workers +w:0,t:0x00000000: writeStackCookie: 0x00000000 +w:0,t:0x00000000: initRuntime +w:0,t:0x00021718: done __wasm_call_ctors +w:0,t:0x00021718: done ATPOSTCTORS +w:4,t:0x00000000: writeStackCookie: 0x00022bf0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:4044: Packet simulator started (Packet loss: 0.000000, Packet duplication: 0.000000, Ping: 0.000000, Jitter: 0.000000) +2026-02-14T07:49:11.311Z [GameClient] info: Connecting... +2026-02-14T07:49:11.312Z [StandaloneSignalingClient] info: 0 +2026-02-14T07:49:11.312Z [StandaloneSignalingClient] info: Connecting to ws://127.0.0.1:42044... +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: proc_exit: 0 +w:0,t:0x00021718: handleException: unwinding: EXITSTATUS=0 +2026-02-14T07:49:11.339Z [StandaloneSignalingClient] info: Connected +2026-02-14T07:49:11.339Z [GameClient] info: Creating server peer... +2026-02-14T07:49:11.345Z [Peer(0)] info: Offer created and set as local description +2026-02-14T07:49:11.346Z [StandaloneSignalingClient] info: Send signaling data: { + type: 'offer', + sdp: 'v=0\r\n' + + 'o=- 5703980242004240308 2 IN IP4 127.0.0.1\r\n' + + 's=-\r\n' + + 't=0 0\r\n' + + 'a=group:BUNDLE 0\r\n' + + 'a=extmap-allow-mixed\r\n' + + 'a=msid-semantic: WMS\r\n' + + 'm=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\n' + + 'c=IN IP4 0.0.0.0\r\n' + + 'a=ice-ufrag:/RvV\r\n' + + 'a=ice-pwd:IwolxOyvYLfRqGBG/XdeiUTA\r\n' + + 'a=ice-options:trickle\r\n' + + 'a=fingerprint:sha-256 F2:71:B9:94:53:D1:10:F4:5B:D8:4D:88:2B:54:B1:B0:68:76:0B:68:53:23:AC:1E:61:DA:4D:36:56:3A:8C:01\r\n' + + 'a=setup:actpass\r\n' + + 'a=mid:0\r\n' + + 'a=sctp-port:5000\r\n' + + 'a=max-message-size:262144\r\n' +} +2026-02-14T07:49:11.351Z [Peer(0)] info: Ice gathering state changed: gathering +2026-02-14T07:49:11.353Z [Peer(0)] info: Got new candidate, save it to the candidates list +2026-02-14T07:49:11.354Z [Peer(0)] info: Got new candidate, save it to the candidates list +2026-02-14T07:49:11.356Z [Peer(0)] info: Got new candidate, save it to the candidates list +2026-02-14T07:49:11.356Z [Peer(0)] info: Got new candidate, save it to the candidates list +2026-02-14T07:49:11.356Z [StandaloneSignalingClient] info: Received signaling data: {"type":"answer", "sdp":"v=0\r\no=rtc 422846998 0 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0\r\na=msid-semantic:WMS *\r\na=ice-options:ice2,trickle\r\na=fingerprint:sha-256 6E:3B:36:4B:60:21:6F:14:66:E4:AC:D3:96:44:41:68:32:E1:34:1D:F7:0D:EB:56:74:C7:CF:D9:C7:C0:0C:68\r\nm=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\nc=IN IP4 0.0.0.0\r\na=mid:0\r\na=sendrecv\r\na=sctp-port:5000\r\na=max-message-size:262144\r\na=setup:active\r\na=ice-ufrag:Rcrb\r\na=ice-pwd:umhyogN/fCkQnBA7G/MEK6\r\na=candidate:1 1 UDP 2116026367 2a02:842a:3620:e701:4d3:7a7e:e243:2e21 63275 typ host\r\na=candidate:2 1 UDP 2114977535 192.168.1.212 63275 typ host\r\n"} +2026-02-14T07:49:11.356Z [Peer(0)] info: Got answer +2026-02-14T07:49:11.358Z [Peer(0)] info: Remote description set, notify remote peer that we are ready to receive his ice candidates +2026-02-14T07:49:11.358Z [StandaloneSignalingClient] info: Send signaling data: { signaling: [Object] } +2026-02-14T07:49:11.359Z [Peer(0)] error: Error while gathering ice candidates: 701 +2026-02-14T07:49:11.359Z [Peer(0)] error: Error while gathering ice candidates: 701 +2026-02-14T07:49:11.359Z [Peer(0)] error: Error while gathering ice candidates: 701 +2026-02-14T07:49:11.359Z [Peer(0)] error: Error while gathering ice candidates: 701 +2026-02-14T07:49:11.407Z [Peer(0)] info: Ice gathering state changed: complete +2026-02-14T07:49:11.408Z [Peer(0)] info: All candidates gathered, waiting for the remote peer to be ready to receive them +2026-02-14T07:49:11.414Z [Peer(0)] info: unreliable data channel opened (id: 0) +2026-02-14T07:49:11.414Z [GameClient] info: Connected +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 INFO /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1903: WebRTC_EMSCRIPTEN driver started +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1791: Enqueue message of type 252 on channel 1 +2026-02-14 08:49:11 INFO /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1949: Started +w:0,t:0x00021718: 08:49:11 INFO /Users/nathb/Dev/GameDev/nbnet/soak/soak.c:73: Soak test initialized (Packet loss: 0.000000, Packet duplication: 0.000000, Ping: 0.000000, Jitter: 0.000000) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 1 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 1 (length: 4, type: 252) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 1 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 1 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 1 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 2 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 2 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 3 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 4 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 3 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 5 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 4 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 4 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 6 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 5 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 5 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 7 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 7 (length: 4, type: 252) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 7 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 6 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 6 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 8 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 7 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 7 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 9 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 8 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 8 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 10 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 11 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 9 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 9 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 12 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 10 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 10 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 13 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 13 (length: 4, type: 252) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 13 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 11 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 11 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 14 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 12 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 12 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 15 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 13 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 13 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 16 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 14 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 14 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 17 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 15 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 15 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 18 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 16 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 16 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 19 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 19 (length: 4, type: 252) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 19 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 17 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 17 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 20 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 21 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 18 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 18 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 22 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 19 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 19 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 23 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 20 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 20 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 24 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 21 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 21 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 25 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 25 (length: 4, type: 252) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 25 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 22 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 22 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 26 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 23 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 23 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 27 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 24 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 24 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 28 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 25 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 25 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 29 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 30 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 26 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 26 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 31 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 31 (length: 4, type: 252) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 31 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 27 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 27 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 32 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 28 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 28 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 33 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 29 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 29 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 34 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 30 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 30 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 35 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 31 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 31 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 36 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 32 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 32 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 37 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 37 (length: 4, type: 252) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 37 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 33 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 33 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 38 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 34 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 34 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 39 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 35 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 35 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 40 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 41 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 36 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 36 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 42 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 37 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 37 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 43 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 43 (length: 4, type: 252) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 43 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 38 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 38 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 44 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 39 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 39 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 45 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 40 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 40 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 46 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 41 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 41 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 47 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 42 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 42 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 48 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 43 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 43 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 49 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 49 (length: 4, type: 252) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 49 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 44 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 44 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 50 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 45 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 45 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 51 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 52 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 46 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 46 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 53 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 47 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 47 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 54 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 48 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 48 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 55 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 55 (length: 4, type: 252) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 55 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 49 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 49 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 56 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 50 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 50 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 57 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 51 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 51 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 58 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 52 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 52 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 59 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 53 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 53 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 60 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 54 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 54 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 61 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 61 (length: 4, type: 252) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 61 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 62 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 55 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 55 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 63 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 56 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 56 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 64 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 57 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 57 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 65 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 58 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 58 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 66 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 59 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 59 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 67 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 67 (length: 4, type: 252) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 67 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 60 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 60 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 68 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 61 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 61 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 69 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 62 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 62 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 70 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 63 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 63 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 71 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 64 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 64 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 72 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 65 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 65 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 73 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 73 (length: 4, type: 252) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 73 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 66 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 66 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 74 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 67 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 67 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 75 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 68 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 68 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 76 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 77 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 diff --git a/soak/client.c b/soak/client.c index 875aaa1..c54f727 100644 --- a/soak/client.c +++ b/soak/client.c @@ -213,8 +213,10 @@ static int Tick(void *data) { int ev; while ((ev = NBN_GameClient_Poll()) != NBN_CLIENT_NO_EVENT) { - if (ev < 0) + if (ev < 0) { + log_error("Error while poling client events"); return -1; + } switch (ev) { case NBN_CLIENT_DISCONNECTED: @@ -230,8 +232,10 @@ static int Tick(void *data) { break; case NBN_CLIENT_MESSAGE_RECEIVED: - if (HandleReceivedMessage(channels) < 0) + if (HandleReceivedMessage(channels) < 0) { + log_error("Error processing received message"); return -1; + } break; } } @@ -264,24 +268,6 @@ int main(int argc, char *argv[]) { SoakOptions options = Soak_GetOptions(); -#ifdef WEBRTC_NATIVE - - if (options.webrtc) { - // Register the native WebRTC driver - const char *ice_servers[] = {"stun:stun01.sipphone.com"}; - NBN_WebRTC_Native_Config cfg = {.ice_servers = ice_servers, - .ice_servers_count = 1, - .enable_tls = false, - .cert_path = NULL, - .key_path = NULL, - .passphrase = NULL, - .log_level = RTC_LOG_VERBOSE}; - - NBN_WebRTC_Native_Register(cfg); - } - -#endif // WEBRTC_NATIVE - NBN_GameClient_Init(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT); for (uint8_t c = 0; c < NBN_CHANNEL_COUNT; c++) { @@ -330,14 +316,6 @@ int main(int argc, char *argv[]) { NBN_GameClient_Stop(); free(channels); -#ifdef WEBRTC_NATIVE - - if (options.webrtc) { - NBN_WebRTC_Native_Unregister(); - } - -#endif // WEBRTC_NATIVE - #ifdef __EMSCRIPTEN__ emscripten_force_exit(ret); #else diff --git a/soak/package-lock.json b/soak/package-lock.json index ddf191e..e9053d7 100644 --- a/soak/package-lock.json +++ b/soak/package-lock.json @@ -5,10 +5,10 @@ "packages": { "": { "dependencies": { - "nbnet": "file:../net_drivers/webrtc" + "nbnet": "file:../webrtc" } }, - "../net_drivers/webrtc": { + "../webrtc": { "name": "nbnet", "version": "1.0", "license": "MIT", @@ -19,7 +19,7 @@ } }, "node_modules/nbnet": { - "resolved": "../net_drivers/webrtc", + "resolved": "../webrtc", "link": true } } diff --git a/soak/package.json b/soak/package.json index e3c88c3..8bf7fd1 100644 --- a/soak/package.json +++ b/soak/package.json @@ -4,6 +4,6 @@ "client": "node build_web/client.js" }, "dependencies": { - "nbnet": "file:../net_drivers/webrtc" + "nbnet": "file:../webrtc" } } diff --git a/soak/server.c b/soak/server.c index 5f6432d..1cc6e8e 100644 --- a/soak/server.c +++ b/soak/server.c @@ -27,6 +27,7 @@ #include #include #include +#include "nbnet.h" #include "soak.h" #include "log.h" @@ -277,20 +278,6 @@ int main(int argc, char *argv[]) { if (Soak_ReadCommandLine(argc, argv) < 0) return -1; -#ifdef WEBRTC_NATIVE - // Register native WebRTC driver - const char *ice_servers[] = {"stun:stun01.sipphone.com"}; - NBN_WebRTC_Native_Config cfg = {.ice_servers = ice_servers, - .ice_servers_count = 1, - .enable_tls = false, - .cert_path = NULL, - .key_path = NULL, - .passphrase = NULL, - .log_level = RTC_LOG_VERBOSE}; - - NBN_WebRTC_Native_Register(cfg); -#endif // WEBRTC_NATIVE - SoakOptions options = Soak_GetOptions(); NBN_GameServer_Init(SOAK_PROTOCOL_NAME, SOAK_PORT); From 5d0e3903b695874829b61006e2d14aedf8936c9c Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sat, 14 Feb 2026 09:08:14 +0100 Subject: [PATCH 65/85] update webrtc native pipeline --- .github/workflows/nbnet.yml | 4 +- bin/github-actions/run_soak.sh | 109 ++++++++++++++++----------------- 2 files changed, 56 insertions(+), 57 deletions(-) diff --git a/.github/workflows/nbnet.yml b/.github/workflows/nbnet.yml index 7ef3341..ce48635 100644 --- a/.github/workflows/nbnet.yml +++ b/.github/workflows/nbnet.yml @@ -202,7 +202,9 @@ jobs: cd soak mkdir build cd build - cmake -DLIBDATACHANNEL_LIBRARY_PATH=${{ github.workspace }}/libdatachannel/build/libdatachannel.so -DLIBDATACHANNEL_INCLUDE_PATH=${{ github.workspace }}/libdatachannel/include -DWEBRTC_NATIVE=ON -DUDP=ON .. + cmake -DLIBDATACHANNEL_LIBRARY_PATH=${{ github.workspace }}/libdatachannel/build/libdatachannel.so \ + -DLIBDATACHANNEL_INCLUDE_PATH=${{ github.workspace }}/libdatachannel/include \ + -DWEBRTC_NATIVE=ON -DUDP=ON -DCMAKE_BUILD_TYPE=Debug .. make - name: Compile soak test (web) run: ./bin/github-actions/build_soak_web.sh diff --git a/bin/github-actions/run_soak.sh b/bin/github-actions/run_soak.sh index b7efda2..5262f21 100755 --- a/bin/github-actions/run_soak.sh +++ b/bin/github-actions/run_soak.sh @@ -9,86 +9,83 @@ MESSAGE_COUNT=500 NODE_CMD="$EMSDK_NODE" run_client() { - client_mode=$1 - echo "Running soak client (run in mode: $client_mode)..." - - if [ "$client_mode" = "WEBRTC_EMSCRIPTEN" ]; then - # WASM WebRTC client - $NODE_CMD build_web/client.js --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_cli_out - elif [ "$client_mode" = "WEBRTC_NATIVE" ]; then - # native WebRTC client - ./build/client --webrtc --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_cli_out - elif [ "$client_mode" = "UDP" ]; then - # UDP client - ./build/client --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_cli_out - else - echo "Unknown client mode" - return 1 - fi - - RESULT=$? - - # when running the soak test in the latest version of emscripten with node 16 - # the client aborts at the end when calling emscripten_force_exit - # I could not figure out why, hence the condition - [[ "$client_mode" = "WEBRTC_EMSCRIPTEN" ]] && EXPECTED_RESULT=7 || EXPECTED_RESULT=0 - - if [ $RESULT -eq $EXPECTED_RESULT ]; then - echo "Soak test completed with success!" - - return 0 - else - echo "Soak test failed! (code: $RESULT)" - echo "Printing the end of client logs..." - tail -n 150 soak_cli_out - echo "Printing the end of server logs..." - tail -n 150 soak_serv_out - - return 1 - fi + client_mode=$1 + echo "Running soak client (run in mode: $client_mode)..." + + if [ "$client_mode" = "WEBRTC_EMSCRIPTEN" ]; then + # WASM WebRTC client + $NODE_CMD build_web/client.js --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_cli_out + elif [ "$client_mode" = "UDP" ]; then + # UDP client + ./build/client --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_cli_out + else + echo "Unknown client mode" + return 1 + fi + + RESULT=$? + + # when running the soak test in the latest version of emscripten with node 16 + # the client aborts at the end when calling emscripten_force_exit + # I could not figure out why, hence the condition + [[ "$client_mode" = "WEBRTC_EMSCRIPTEN" ]] && EXPECTED_RESULT=7 || EXPECTED_RESULT=0 + + if [ $RESULT -eq $EXPECTED_RESULT ]; then + echo "Soak test completed with success!" + + return 0 + else + echo "Soak test failed! (code: $RESULT)" + echo "Printing the end of client logs..." + tail -n 150 soak_cli_out + echo "Printing the end of server logs..." + tail -n 150 soak_serv_out + + return 1 + fi } exit_soak() { - kill -SIGINT $SERV_PID 2>/dev/null + kill -SIGINT $SERV_PID 2>/dev/null - exit $1 + exit $1 } cd soak echo "Starting soak server..." if [ -n "$WEBRTC" ]; then - $NODE_CMD build_web/server.js --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_serv_out & + $NODE_CMD build_web/server.js --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_serv_out & else - ./build/server --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_serv_out & + ./build/server --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_serv_out & fi if [ $? -eq 0 ]; then - SERV_PID=$! + SERV_PID=$! - echo "Server started (PID: $SERV_PID)" - echo "Running soak test..." + echo "Server started (PID: $SERV_PID)" + echo "Running soak test..." else - echo "Failed to start soak server!" - exit 1 + echo "Failed to start soak server!" + exit 1 fi sleep 3 if [ -n "$WEBRTC" ]; then - run_client "WEBRTC_EMSCRIPTEN" + run_client "WEBRTC_EMSCRIPTEN" - exit_soak $? + exit_soak $? elif [ -n "$WEBRTC_NATIVE" ]; then - # run a UDP client, a webrtc WASM client (emscripten) and a native webrtc client (all connecting to the same server) + # run a UDP client, a webrtc WASM client (emscripten) and a native webrtc client (all connecting to the same server) - if run_client "UDP" && run_client "WEBRTC_EMSCRIPTEN" && run_client "WEBRTC_NATIVE"; then - exit_soak 0 - else - exit_soak 1 - fi + if run_client "UDP" && run_client "WEBRTC_EMSCRIPTEN"; then + exit_soak 0 + else + exit_soak 1 + fi else - run_client "UDP" + run_client "UDP" - exit_soak $? + exit_soak $? fi From 3c6305117c9b7d8421972d8353b3c56230cab930 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sat, 14 Feb 2026 09:11:17 +0100 Subject: [PATCH 66/85] fix pipelines --- .github/workflows/nbnet.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/nbnet.yml b/.github/workflows/nbnet.yml index ce48635..a42fb55 100644 --- a/.github/workflows/nbnet.yml +++ b/.github/workflows/nbnet.yml @@ -98,7 +98,7 @@ jobs: npm update npm install -g node-pre-gyp npm install -g node-gyp - cd net_drivers/webrtc + cd webrtc npm install --build-from-resource - name: Compile soak test run: ./bin/github-actions/build_soak_web.sh @@ -154,7 +154,7 @@ jobs: npm update npm install -g node-pre-gyp npm install -g node-gyp - cd net_drivers/webrtc + cd webrtc npm install --build-from-resource - name: Compile soak test run: ./bin/github-actions/build_soak_web.sh @@ -195,7 +195,7 @@ jobs: npm update npm install -g node-pre-gyp npm install -g node-gyp - cd net_drivers/webrtc + cd webrtc npm install --build-from-resource - name: Compile soak test (native) run: | From 72e9da0c252e5a9b5c47453267b1066c0a724db2 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sat, 14 Feb 2026 09:21:21 +0100 Subject: [PATCH 67/85] readd webrtc js code removed by mistake --- .gitignore | 1 - webrtc/js/api.js | 141 +++++++++++++ webrtc/js/game_client.js | 95 +++++++++ webrtc/js/game_server.js | 137 +++++++++++++ webrtc/js/index.js | 30 +++ webrtc/js/logger.js | 46 +++++ webrtc/js/nbnet.js | 23 +++ webrtc/js/peer.js | 245 +++++++++++++++++++++++ webrtc/js/standalone/connection.js | 17 ++ webrtc/js/standalone/signaling_client.js | 86 ++++++++ webrtc/js/standalone/signaling_server.js | 79 ++++++++ 11 files changed, 899 insertions(+), 1 deletion(-) create mode 100644 webrtc/js/api.js create mode 100644 webrtc/js/game_client.js create mode 100644 webrtc/js/game_server.js create mode 100644 webrtc/js/index.js create mode 100644 webrtc/js/logger.js create mode 100644 webrtc/js/nbnet.js create mode 100644 webrtc/js/peer.js create mode 100644 webrtc/js/standalone/connection.js create mode 100644 webrtc/js/standalone/signaling_client.js create mode 100644 webrtc/js/standalone/signaling_server.js diff --git a/.gitignore b/.gitignore index 162e5aa..abc34c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -*.js *.wasm .vscode .ccls diff --git a/webrtc/js/api.js b/webrtc/js/api.js new file mode 100644 index 0000000..510c318 --- /dev/null +++ b/webrtc/js/api.js @@ -0,0 +1,141 @@ +/* + +Copyright (C) 2024 BIAGINI Nathan + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +*/ + +// --- Game server API --- + +mergeInto(LibraryManager.library, { + __js_game_server_send_packet_to__proxy: 'sync', + __js_game_server_dequeue_packet__proxy: 'sync', + __js_game_server_dequeue_packet__deps: ['$writeArrayToMemory'], + + __js_game_server_init: function (protocol_id, use_https, key_pem, cert_pem) { + const nbnet = require('nbnet') + + const signalingServer = new nbnet.Standalone.SignalingServer( + protocol_id, + use_https ? { https: true, key: UTF8ToString(key_pem), cert: UTF8ToString(cert_pem) } : {} + ) + + this.gameServer = new nbnet.GameServer(signalingServer) + }, + + __js_game_server_start: function (port) { + return Asyncify.handleSleep(function (wakeUp) { + this.gameServer.start(port).then(() => { + wakeUp(0) + }).catch(_ => { + wakeUp(-1) + }) + }) + }, + + __js_game_server_dequeue_packet: function(peerIdPtr, bufferPtr) { + const packet = this.gameServer.packets.shift() + + if (packet) { + const packetData = packet[0] + const packetSenderId = packet[1] + const byteArray = new Uint8Array(packetData) + + setValue(peerIdPtr, packetSenderId, 'i32') + writeArrayToMemory(byteArray, bufferPtr) + + return packetData.byteLength + } else { + return 0 + } + }, + + __js_game_server_send_packet_to: function (packetPtr, packetSize, peerId) { + const data = new Uint8Array(HEAPU8.subarray(packetPtr, packetPtr + packetSize)) + + this.gameServer.send(data, peerId) + }, + + __js_game_server_close_client_peer: function(peerId) { + this.gameServer.closePeer(peerId) + }, + + __js_game_server_stop: function() { + this.gameServer.stop() + } +}) + +// --- Game client API --- + +mergeInto(LibraryManager.library, { + __js_game_client_send_packet__proxy: 'sync', + __js_game_client_dequeue_packet__proxy: 'sync', + __js_game_client_dequeue_packet__deps: ['$writeArrayToMemory'], + + __js_game_client_init: function(protocol_id, use_https) { + let nbnet + + if (typeof window === 'undefined') { + // we are running in node so we can use require + nbnet = require('nbnet') + } else { + // we are running in a web browser so we used to "browserified" nbnet (see nbnet.js) + nbnet = Module.nbnet + } + + const signalingClient = new nbnet.Standalone.SignalingClient(protocol_id, { https: use_https }) + + this.gameClient = new nbnet.GameClient(signalingClient) + }, + + __js_game_client_start: function(hostPtr, port) { + return Asyncify.handleSleep(function (wakeUp) { + this.gameClient.connect(UTF8ToString(hostPtr), port).then(() => { + wakeUp(0) + }).catch(_ => { + wakeUp(-1) + }) + }) + }, + + __js_game_client_dequeue_packet: function(bufferPtr) { + const packet = this.gameClient.packets.shift() + + if (packet) { + const byteArray = new Uint8Array(packet) + + writeArrayToMemory(byteArray, bufferPtr) + + return packet.byteLength + } else { + return 0 + } + }, + + __js_game_client_send_packet: function (packetPtr, packetSize) { + const data = new Uint8Array(HEAPU8.subarray(packetPtr, packetPtr + packetSize)) + + this.gameClient.send(data) + }, + + __js_game_client_close: function() { + Asyncify.handleSleep(function (wakeUp) { + this.gameClient.close().then(wakeUp).catch(wakeUp) + }); + } +}) diff --git a/webrtc/js/game_client.js b/webrtc/js/game_client.js new file mode 100644 index 0000000..5e7fe5b --- /dev/null +++ b/webrtc/js/game_client.js @@ -0,0 +1,95 @@ +/* + +Copyright (C) 2024 BIAGINI Nathan + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +*/ + +const loggerFactory = require('./logger.js') +const Peer = require('./peer.js') + +function GameClient(signalingClient) { + this.signalingClient = signalingClient + this.peer = new Peer(0, signalingClient) + this.packets = [] + this.logger = loggerFactory.createLogger('GameClient') + + this.signalingClient.onDataReceived = (data) => { + this.peer.notifySignalingData(data) + } + + this.signalingClient.onClosed = () => { + // this.onClosed() + } + + this.peer.onError = (err) => { + this.logger.info('Peer has ecountered an error: %s. Closing connection', err) + + this.signalingClient.close() + } + + this.peer.onClosed = (err) => { + this.logger.info('Peer closed') + + this.signalingClient.close() + } + + this.peer.onPacketReceived = (packet) => { + this.packets.push(packet) + } +} + +GameClient.prototype.connect = function(host, port) { + this.logger.info('Connecting...') + + return new Promise((resolve, reject) => { + this.signalingClient.connect(host, port).then(() => { + this.logger.info('Creating server peer...') + + this.peer.onConnected = () => { + this.logger.info('Connected') + + resolve() + } + + this.peer.onConnectionError = (err) => { + this.logger.info('Connection failed: %s', err) + + reject(err) + } + + this.peer.connect() + }).catch((err) => { + reject(err) + }) + }) +} + +GameClient.prototype.send = function(data) { + this.peer.send(data) +} + +GameClient.prototype.close = function() { + return new Promise((resolve, reject) => { + this.signalingClient.close() + .then(() => resolve()) + .catch(() => reject()) + }) +} + +module.exports = GameClient diff --git a/webrtc/js/game_server.js b/webrtc/js/game_server.js new file mode 100644 index 0000000..51c53f8 --- /dev/null +++ b/webrtc/js/game_server.js @@ -0,0 +1,137 @@ +/* + +Copyright (C) 2024 BIAGINI Nathan + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +*/ + +const loggerFactory = require('./logger.js') +const Peer = require('./peer.js') + +function GameServer(signalingServer) { + this.signalingServer = signalingServer + this.peers = {} + this.packets = [] + this.nextPeerId = 1; // nbnet connection ids start at 1 + this.logger = loggerFactory.createLogger('GameServer') +} + +GameServer.prototype.start = function(port) { + if (this.signalingServer.isSecure()) { + this.logger.info('Starting (HTTPS is enabled)...') + } else { + this.logger.info('Starting (HTTPS is disabled)...') + } + + return new Promise((resolve, reject) => { + this.signalingServer.onConnection = (connection) => { handleConnection(this, connection) } + + this.signalingServer.start(port).then(() => { + this.logger.info('Started') + + resolve() + }).catch((err) => { + this.logger.error('Failed to start: %s', err) + + reject(err) + }) + }) +} + +GameServer.prototype.send = function(packet, peerId) { + const peer = this.peers[peerId] + + if (peer) { + peer.send(packet) + } else { + this.logger.warn("Trying to send packet to an unknown peer: " + peerId) + } +} + +GameServer.prototype.closePeer = function(peerId) { + const peer = this.peers[peerId] + + if (peer) { + peer.close() + } +} + +GameServer.prototype.stop = function() { + this.signalingServer.stop() +} + +function handleConnection(gameServer, connection) { + const peer = new Peer(gameServer.nextPeerId++, connection) + + peer.onConnected = () => { + gameServer.logger.info('Peer %d is connected', peer.id) + + // gameServer.onPeerConnected(peer.id) + } + + peer.onClosed = () => { + gameServer.logger.info('Peer %d has disconnected', peer.id) + + removePeer(gameServer, peer) + // gameServer.onPeerDisconnected(peer.id) + } + + peer.onError = (err) => { + gameServer.logger.info('Peer %d has ecountered an error: %s. Closing peer', peer.id, err) + + peer.close() + removePeer(gameServer, peer) // make sure the peer is acutally removed, may not be needed + } + + peer.onPacketReceived = (packet) => { + if (gameServer.onPacketReceived) { + gameServer.onPacketReceived(packet, peer.id) + } else { + gameServer.packets.push([packet, peer.id]) + } + } + + gameServer.logger.info('Created new peer (id: %d) for connection %d', peer.id, connection.id) + + gameServer.peers[peer.id] = peer + + connection.onMessageReceived = (msg) => { + gameServer.logger.info('Received signaling message for connection %s: %s', connection.id, msg) + + peer.notifySignalingData(JSON.parse(msg)) + } + + connection.onClosed = () => { + gameServer.logger.info('Connection %s closed, will close peer %d', connection.id, peer.id) + + peer.close() + } +} + +function removePeer(gameServer, peer) { + gameServer.logger.info('Removing peer %d', peer.id) + + for (const peerId in gameServer.peers) { + if (gameServer.peers.hasOwnProperty(peerId) && gameServer.peers[peerId].id === peer.id) { + delete gameServer.peers[peerId] + return + } + } +} + +module.exports = GameServer diff --git a/webrtc/js/index.js b/webrtc/js/index.js new file mode 100644 index 0000000..58525ff --- /dev/null +++ b/webrtc/js/index.js @@ -0,0 +1,30 @@ +/* + +Copyright (C) 2024 BIAGINI Nathan + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +*/ + +module.exports = { + GameServer: require('./game_server.js'), + GameClient: require('./game_client.js'), + Standalone: { + SignalingServer: require('./standalone/signaling_server.js'), + SignalingClient: require('./standalone/signaling_client.js') + } +} diff --git a/webrtc/js/logger.js b/webrtc/js/logger.js new file mode 100644 index 0000000..e1ef7b7 --- /dev/null +++ b/webrtc/js/logger.js @@ -0,0 +1,46 @@ +/* + +Copyright (C) 2024 BIAGINI Nathan + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +*/ + +var exports = module.exports = {} + +const { createLogger, format, transports } = require('winston') +const { combine, timestamp, label, printf } = format + +exports.createLogger = function(name, options = {}) { + const loggerFormat = combine( + label({ label: name }), + timestamp(), + format.colorize(), + format.splat(), + printf(({ level, message, label, timestamp }) => `${timestamp} [${label}] ${level}: ${message}`) + ) + + const logger = createLogger({ + level: 'info', + format: loggerFormat, + transports: [ + new transports.Console() + ] + }) + + return logger +} diff --git a/webrtc/js/nbnet.js b/webrtc/js/nbnet.js new file mode 100644 index 0000000..8e1163b --- /dev/null +++ b/webrtc/js/nbnet.js @@ -0,0 +1,23 @@ +/* + +Copyright (C) 2024 BIAGINI Nathan + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +*/ + +Module.nbnet = require('./index.js') diff --git a/webrtc/js/peer.js b/webrtc/js/peer.js new file mode 100644 index 0000000..8d83bc7 --- /dev/null +++ b/webrtc/js/peer.js @@ -0,0 +1,245 @@ +/* + +Copyright (C) 2024 BIAGINI Nathan + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +*/ + +const webrtc = require('@roamhq/wrtc') +const RTCPeerConnection = webrtc.RTCPeerConnection +const RTCSessionDescription = webrtc.RTCSessionDescription +const loggerFactory = require('./logger.js') + +function Peer(id, connection) { + this.id = id + this.connection = connection + this.candidates = [] + this.peerConnection = new RTCPeerConnection({ 'iceServers': [{ 'urls': 'stun:stun01.sipphone.com' }] }) + this.channel = this.peerConnection.createDataChannel('unreliable', + { negotiated: true, id: 0, maxRetransmits: 0, ordered: false }) + this.channel.binaryType = 'arraybuffer' + + // peer connection event listeners + this.peerConnection.addEventListener('icecandidate', ({ candidate }) => { onIceCandidate(this, candidate) }) + this.peerConnection.addEventListener('signalingstatechange', () => { onSignalingStateChange(this) }) + this.peerConnection.addEventListener('icegatheringstatechange', () => { onIceGatheringStateChanged(this) }) + this.peerConnection.addEventListener('icecandidateerror', (ev) => { + this.logger.error('Error while gathering ice candidates: %s', ev.errorCode) + }) + + this.channel.addEventListener('open', () => { onDataChannelOpened(this, this.channel) }) + this.channel.addEventListener('error', () => { onDataChannelError(this, this.channel) }) + this.channel.addEventListener('message', (ev) => { onPacketReceived(this, ev.data) }) + + this.logger = loggerFactory.createLogger(`Peer(${this.id})`) +} + +Peer.prototype.connect = function() { + this.state = 'connecting' + + // OfferToReceiveAudio & OfferToReceiveVideo seems to be required in Chrome even if we only need data channels + this.peerConnection.createOffer({ mandatory: { OfferToReceiveAudio: true, OfferToReceiveVideo: true } }).then((description) => { + this.peerConnection.setLocalDescription(description).then(() => { + this.logger.info('Offer created and set as local description') + + this.connection.send(description) + }).catch((err) => { + raiseError(this, `setLocalDescription: ${err}`) + }) + }).catch((err) => { + raiseError(this, `createOffer: ${err}`) + }) +} + +Peer.prototype.close = function() { + this.logger.info('Closing peer...') + + this.peerConnection.close() + this.connection.close() +} + +Peer.prototype.notifySignalingData = function(data) { + if ('type' in data) { + if (data.type === 'offer') { + handleOffer(this, data) + } else if (data.type === 'answer') { + handleAnswer(this, data) + } + } else if ('candidate' in data) { + handleCandidate(this, data.candidate) + } else if ('signaling' in data) { + if (data.signaling.ready_for_candidates) { + this.logger.info('Remote peer is ready to receive our ice candidates') + + this.isRemotePeerReadyToReceiveRemoteIceCandidates = true + } + } +} + +Peer.prototype.send = function(data) { + try { + this.channel.send(data) + } catch(err) { + this.logger.error('Failed to send: ' + err) + + this.close() + } +} + +function onIceCandidate(peer, candidate) { + if (candidate) { + peer.logger.info('Got new candidate, save it to the candidates list') + + peer.candidates.push(candidate) + } +} + +function onSignalingStateChange(peer) { + if (peer.peerConnection.signalingState == 'closed') { + peer.logger.info('Closed') + + peer.onClosed() + } +} + +function onIceGatheringStateChanged(peer) { + peer.logger.info('Ice gathering state changed: %s', peer.peerConnection.iceGatheringState) + + if (peer.peerConnection.iceGatheringState === 'complete') { + peer.logger.info('All candidates gathered, waiting for the remote peer to be ready to receive them') + + waitForRemotePeerToBeReadyToReceiveIceCandidates(peer).then(() => { + if (peer.state !== 'connected') { + sendCandidates(peer) + } + }).catch((err) => { + raiseError(peer, `waitForRemotePeerToBeReadyToReceiveIceCandidates: ${err}`) + }) + } +} + +function handleOffer(peer, offer) { + peer.logger.info('Got offer') + + peer.peerConnection.setRemoteDescription(new RTCSessionDescription(offer)).then(() => { + onRemoteDescriptionSet(peer) + createAnswer(peer) + }).catch((err) => { + raiseError(peer, `setRemoteDescription: ${err}`) + }) +} + +function handleAnswer(peer, answer) { + peer.logger.info('Got answer') + + peer.peerConnection.setRemoteDescription(answer).then(() => { + onRemoteDescriptionSet(peer) + }).catch((err) => { + raiseError(peer, `setRemoteDescription: ${err}`) + }) +} + +function onRemoteDescriptionSet(peer) { + // remote description is set so we can now add the other peer ice candidates + // notify the other peer that we are ready to treat his ice candidates + // addIceCandidate fail if remote description is not set + + peer.logger.info('Remote description set, notify remote peer that we are ready to receive his ice candidates') + + peer.connection.send({ signaling: { ready_for_candidates: true } }) +} + +function handleCandidate(peer, candidate) { + peer.logger.info(`Got candidate: ${JSON.stringify(candidate)}`) + + if (candidate.candidate) { + peer.peerConnection.addIceCandidate(candidate).then(() => { + peer.logger.info('Candidate added') + }).catch((err) => { + raiseError(peer, `addIceCandidate: ${err}`) + }) + } +} + +function createAnswer(peer) { + peer.peerConnection.createAnswer().then((answer) => { + peer.logger.info('Answer created') + + peer.peerConnection.setLocalDescription(answer).then(() => { + peer.logger.info('Answer set as local description, signaling it') + + peer.connection.send(answer) + }).catch((err) => { + raiseError(peer, `setLocalDescription: ${err}`) + }) + }).catch((err) => { + raiseError(peer, `createAnswer: ${err}`) + }) +} + +function waitForRemotePeerToBeReadyToReceiveIceCandidates(peer) { + return new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + reject('timeout') + }, 5000) + + const intervalId = setInterval(() => { + if (peer.state === 'connected' || peer.isRemotePeerReadyToReceiveRemoteIceCandidates) { + clearTimeout(timeoutId) + clearInterval(intervalId) + resolve() + } + }, 500) + }) +} + +function sendCandidates(peer) { + peer.logger.info('Sending all gather candidates...') + + peer.candidates.forEach((candidate) => { + peer.connection.send({ candidate: candidate }) + }) +} + +function onDataChannelOpened(peer, dataChannel) { + peer.logger.info('%s data channel opened (id: %d)', dataChannel.label, dataChannel.id) + + peer.state = 'connected' + + peer.onConnected() +} + +function onDataChannelError(peer, dataChannel, err) { + raiseError(peer, `Data channel ${dataChannel.label} (id: ${dataChannel.id}) error: ${err}`) +} + +function onPacketReceived(peer, packet) { + peer.onPacketReceived(packet) +} + +function raiseError(peer, err) { + peer.logger.error(err) + + if (peer.state === 'connecting' && peer.onConnectionError) { + peer.onConnectionError(this) + } else if (peer.onError) { + peer.onError(err) + } +} + +module.exports = Peer diff --git a/webrtc/js/standalone/connection.js b/webrtc/js/standalone/connection.js new file mode 100644 index 0000000..f1b1d0d --- /dev/null +++ b/webrtc/js/standalone/connection.js @@ -0,0 +1,17 @@ +function Connection(connection) { + this.connection = connection + this.id = connection.remoteAddress + + connection.on('message', (msg) => { this.onMessageReceived(msg.utf8Data) }) + connection.on('close', () => { this.onClosed() }) +} + +Connection.prototype.send = function(data) { + this.connection.sendUTF(JSON.stringify(data)) +} + +Connection.prototype.close = function() { + this.connection.close() +} + +module.exports = Connection \ No newline at end of file diff --git a/webrtc/js/standalone/signaling_client.js b/webrtc/js/standalone/signaling_client.js new file mode 100644 index 0000000..ed3d075 --- /dev/null +++ b/webrtc/js/standalone/signaling_client.js @@ -0,0 +1,86 @@ +const loggerFactory = require('../logger.js') + +function SignalingClient(protocol_id, options) { + this.protocol = protocol_id.toString() + this.logger = loggerFactory.createLogger('StandaloneSignalingClient') + this.connected = false + this.options = options +} + +SignalingClient.prototype.connect = function(host, port) { + return new Promise((resolve, reject) => { + const uri = this.options['https'] ? `wss://${host}:${port}` : `ws://${host}:${port}` + + this.logger.info(this.options['https']) + this.logger.info(`Connecting to ${uri}...`) + + const WebSocket = require('websocket').w3cwebsocket + + this.ws = new WebSocket(uri) + + this.ws.onclose = (ev) => { + if (this.connected) { + this.logger.error('Connection closed') + + this.connected = false + + this.onClosed() + } else { + this.logger.error('Connection failed') + + reject() + } + } + + this.ws.onopen = () => { + this.logger.info('Connected') + + this.connected = true + + clearTimeout(timeoutId) + resolve() + } + + this.ws.onmessage = (ev) => { + this.logger.info('Received signaling data: %s', ev.data) + + this.onDataReceived(JSON.parse(ev.data)) + } + + const timeoutId = setTimeout(() => { + this.logger.error('Connection timeout') + + reject() + }, 3000) + }) +} + +SignalingClient.prototype.send = function(data) { + this.logger.info('Send signaling data: %s', data) + + this.ws.send(JSON.stringify(data)) +} + +SignalingClient.prototype.close = function() { + this.logger.info('Closing...') + + const WebSocket = require('websocket').w3cwebsocket + + return new Promise((resolve, reject) => { + if (this.ws.readyState != WebSocket.OPEN) { + this.logger.warn('Not opened') + + reject() + } else { + this.ws.onclose = (_) => { + this.logger.info('Closed') + + resolve() + } + + this.ws.close() + } + }) +} + +module.exports = SignalingClient diff --git a/webrtc/js/standalone/signaling_server.js b/webrtc/js/standalone/signaling_server.js new file mode 100644 index 0000000..ec6ed86 --- /dev/null +++ b/webrtc/js/standalone/signaling_server.js @@ -0,0 +1,79 @@ +const Connection = require('./connection.js') +const loggerFactory = require('../logger.js') + +function SignalingServer(protocol_id, options) { + this.protocol = protocol_id.toString() + this.logger = loggerFactory.createLogger('StandaloneSignalingServer') + this.options = options +} + +SignalingServer.prototype.start = function(port) { + return new Promise((resolve, reject) => { + this.logger.info('Starting (protocol: %s)...', this.protocol) + + var server + + if (this.options['https']) { + const fs = require('fs') + + server = createHttpsServer(this, fs.readFileSync(this.options['key']), fs.readFileSync(this.options['cert'])) + } else { + server = createHttpServer(this) + } + + const WebSocketServer = require('websocket').server + + this.wsServer = new WebSocketServer({ + httpServer: server, + autoAcceptConnections: false + }) + + this.wsServer.on('request', (request) => { + this.logger.info('New connection') + + try { + this.onConnection(new Connection(request.accept(null, request.origin))) + } catch (err) { + this.logger.error('Connection rejected: %s', err) + } + }) + + server.listen(port, () => { + this.logger.info('Started, listening on port %d...', port); + + resolve() + }) + }) +} + +SignalingServer.prototype.stop = function() { + if (this.wsServer) { + this.wsServer.shutDown() + } else { + this.logger.error("Not started") + } +} + +SignalingServer.prototype.isSecure = function() { + return this.options['https'] +} + +function createHttpServer(signalingServer) { + return require('http').createServer((request, response) => { + signalingServer.logger.info('Received request for ' + request.url) + + response.writeHead(404) + response.end() + }) +} + +function createHttpsServer(signalingServer, key, cert) { + return require('https').createServer({ key: key, cert: cert }, (request, response) => { + signalingServer.logger.info('Received request for ' + request.url) + + response.writeHead(404) + response.end() + }) +} + +module.exports = SignalingServer From 2f960074d781b3a3f7692c76c331f7fd2a9ee607 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sat, 14 Feb 2026 18:36:47 +0100 Subject: [PATCH 68/85] add serialization functions --- nbnet.c | 12 ++++++++++++ nbnet.h | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/nbnet.c b/nbnet.c index a8140f6..65a3b91 100644 --- a/nbnet.c +++ b/nbnet.c @@ -672,8 +672,12 @@ void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length) { void NBN_Writer_WriteInt8(NBN_Writer *writer, int8_t value) { NBN_Writer_WriteUInt8(writer, value); } +void NBN_Writer_WriteInt16(NBN_Writer *writer, int16_t value) { NBN_Writer_WriteUInt16(writer, value); } + void NBN_Writer_WriteInt32(NBN_Writer *writer, int32_t value) { NBN_Writer_WriteUInt32(writer, value); } +void NBN_Writer_WriteInt64(NBN_Writer *writer, int64_t value) { NBN_Writer_WriteUInt64(writer, value); } + void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value) { NBN_Assert(writer->position + 1 <= writer->length); @@ -733,10 +737,18 @@ void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length) { int NBN_Reader_ReadInt8(NBN_Reader *reader, int8_t *value) { return NBN_Reader_ReadUInt8(reader, (uint8_t *)value); } +int NBN_Reader_ReadInt16(NBN_Reader *reader, int16_t *value) { + return NBN_Reader_ReadUInt16(reader, (uint16_t *)value); +} + int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value) { return NBN_Reader_ReadUInt32(reader, (uint32_t *)value); } +int NBN_Reader_ReadInt64(NBN_Reader *reader, int64_t *value) { + return NBN_Reader_ReadUInt64(reader, (uint64_t *)value); +} + int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value) { if (reader->position + 1 > reader->length) { return NBN_ERROR; diff --git a/nbnet.h b/nbnet.h index c0d127a..1d0f477 100644 --- a/nbnet.h +++ b/nbnet.h @@ -181,7 +181,9 @@ void NBN_SetLogLevel(NBN_LogLevel); void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length); void NBN_Writer_WriteInt8(NBN_Writer *writer, int8_t value); +void NBN_Writer_WriteInt16(NBN_Writer *writer, int16_t value); void NBN_Writer_WriteInt32(NBN_Writer *writer, int32_t value); +void NBN_Writer_WriteInt64(NBN_Writer *writer, int64_t value); void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value); void NBN_Writer_WriteUInt16(NBN_Writer *writer, uint16_t value); void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value); @@ -193,7 +195,9 @@ void NBN_Writer_WriteString(NBN_Writer *writer, const char *str, unsigned int ma void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length); int NBN_Reader_ReadInt8(NBN_Reader *reader, int8_t *value); +int NBN_Reader_ReadInt16(NBN_Reader *reader, int16_t *value); int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value); +int NBN_Reader_ReadInt64(NBN_Reader *reader, int64_t *value); int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value); int NBN_Reader_ReadUInt16(NBN_Reader *reader, uint16_t *value); int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value); From 6e02adf2a3d8f5a8618f863b80efbe734bd9f406 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sun, 15 Feb 2026 08:56:26 +0100 Subject: [PATCH 69/85] fix conn stale if empty packets + log formatting --- nbnet.c | 52 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/nbnet.c b/nbnet.c index 65a3b91..fb6b5f4 100644 --- a/nbnet.c +++ b/nbnet.c @@ -22,8 +22,6 @@ */ -// TODO: reintroduce webrtc native driver - #include #include "nbnet.h" @@ -1377,7 +1375,7 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn } if (Connection_SendPacket(connection, &packet, packet_entry, time) < 0) { - LogError("Failed to send packet %d to connection %d", packet.header.seq_number, connection->handle.id); + LogError("Failed to send packet %d to connection %lld", packet.header.seq_number, connection->handle.id); return NBN_ERROR; } @@ -1450,7 +1448,7 @@ static int Connection_AckPacket(NBN_Endpoint *endpoint, NBN_Connection *connecti NBN_PacketEntry *packet_entry = Connection_FindSendPacketEntry(connection, ack_packet_seq_number); if (packet_entry && !packet_entry->acked) { - LogDebug("Packet %d acked (connection: %d)", ack_packet_seq_number, connection->handle.id); + LogDebug("Packet %d acked (connection: %lld)", ack_packet_seq_number, connection->handle.id); packet_entry->acked = true; @@ -1530,7 +1528,7 @@ static bool Connection_IsPacketReceived(NBN_Connection *connection, uint16_t pac static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, NBN_PacketEntry *packet_entry, double time) { - LogDebug("Send packet %d to connection %d (messages count: %d)", packet->header.seq_number, connection->handle.id, + LogDebug("Send packet %d to connection %lld (messages count: %d)", packet->header.seq_number, connection->handle.id, packet->header.messages_count); NBN_Assert(packet_entry->messages_count == packet->header.messages_count); @@ -1771,11 +1769,13 @@ static uint32_t Endpoint_BuildProtocolId(const char *protocol_name) { static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *packet, NBN_Connection *connection) { (void)endpoint; - LogDebug("Received packet %d (conn id: %d, ack: %d, messages count: %d)", packet->header.seq_number, + LogDebug("Received packet %d (conn id: %lld, ack: %d, messages count: %d)", packet->header.seq_number, connection->handle.id, packet->header.ack, packet->header.messages_count); - if (Connection_ProcessReceivedPacket(endpoint, connection, packet, endpoint->time) < 0) + if (Connection_ProcessReceivedPacket(endpoint, connection, packet, endpoint->time) < 0) { + LogError("Error when processing packet"); return NBN_ERROR; + } connection->last_recv_packet_time = endpoint->time; connection->downloaded_bytes += packet->size; @@ -2596,7 +2596,7 @@ int NBN_GameServer_EnqueueBroadcastMessage(void) { continue; if (GameServer_EnqueueMessageFor(conn, &endpoint->write_message) < 0) { - LogError("Failed to send message to client %d when broadcasting", conn->handle.id); + LogError("Failed to send message to client %lld when broadcasting", conn->handle.id); ret = NBN_ERROR; break; } @@ -2653,7 +2653,7 @@ int NBN_GameServer_AcceptIncomingConnection(void) { client->is_accepted = true; - LogInfo("Client %d has been accepted into the server", client->handle.id); + LogInfo("Client %lld has been accepted into the server", client->handle.id); return 0; } @@ -2663,7 +2663,7 @@ int NBN_GameServer_RejectIncomingConnectionWithCode(int code) { NBN_Assert(nbn_game_server.last_event.data.connection != NULL); NBN_Connection *conn = nbn_game_server.last_event.data.connection; - LogDebug("Rejecting incoming connection %d (code: %d)", conn->handle.id, code); + LogDebug("Rejecting incoming connection %lld (code: %d)", conn->handle.id, code); return GameServer_CloseClientWithCode(conn, code, false); } @@ -2711,7 +2711,7 @@ static int GameServer_EnqueueMessageFor(NBN_Connection *client, NBN_Message *mes message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); if (Endpoint_EnqueueOutgoingMessage(endpoint, client, message) < 0) { - LogError("Failed to create outgoing message for client %d", client->handle.id); + LogError("Failed to create outgoing message for client %lld", client->handle.id); /* Do not close the client if we failed to send the close client message to avoid infinite loops */ if (message->header.type != NBN_CLIENT_CLOSED_MESSAGE_TYPE) { @@ -2728,7 +2728,7 @@ static void GameServer_AddClient(NBN_Connection *client) { NBN_Assert(hmgeti(nbn_game_server.clients, client->handle.id) == -1); hmput(nbn_game_server.clients, client->handle.id, client); - LogDebug("New client %llu", client->handle.id); + LogDebug("New client %lld", client->handle.id); } static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection) { @@ -2745,7 +2745,7 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool } if (client->is_stale) { - LogDebug("Closing stale connection %d", client->handle.id); + LogDebug("Closing stale connection %lld", client->handle.id); GameServer_AddClientToClosedList(client); client->is_closed = true; @@ -2753,13 +2753,13 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool return 0; } - LogDebug("Closing active connection %d (will send a disconnection message)", client->handle.id); + LogDebug("Closing active connection %lld (will send a disconnection message)", client->handle.id); GameServer_AddClientToClosedList(client); client->is_closed = true; if (!disconnection) { - LogDebug("Send close message for client %d (code: %d)", client->handle.id, code); + LogDebug("Send close message for client %lld (code: %d)", client->handle.id, code); NBN_Writer *writer = NBN_GameServer_CreateMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE, NBN_RESERVED_RELIABLE_CHANNEL_ID); @@ -2847,7 +2847,7 @@ static void GameServer_RemoveClosedClientConnections(void) { NBN_Assert(client->handle.id > 0); if (client->is_stale) { - LogDebug("Remove closed client connection (ID: %d)", client->handle.id); + LogDebug("Remove closed client connection (ID: %lld)", client->handle.id); client->driver->impl.serv_cleanup_connection(&nbn_game_server, client); // Notify the driver to clean up the connection @@ -2906,7 +2906,7 @@ static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev) { } if (message_info.type == NBN_DISCONNECTION_MESSAGE_TYPE) { - LogInfo("Received a disconnection request from client %d (user_data: %p)", sender->handle.id, + LogInfo("Received a disconnection request from client %lld (user_data: %p)", sender->handle.id, sender->handle.user_data); if (GameServer_CloseClientWithCode(sender, -1, true) < 0) { @@ -2935,7 +2935,7 @@ static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev) { // at this point we know it's a connection request NBN_Assert(message_info.type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE); - LogDebug("Received a connection request from client %ul", sender->handle.id); + LogDebug("Received a connection request from client %lld", sender->handle.id); if (message_info.length < 4) { LogError("Connection request invalid length"); @@ -3095,7 +3095,7 @@ static NBN_Connection *UDP_FindOrCreateClientConnectionByAddress(NBN_IPAddress a NBN_Connection *conn = CreateClientConnection(NBN_DRIVER_UDP, conn_id); conn->driver_data.udp.ip_address = address; - LogInfo("New UDP connection (id: %llu, addr: %d, port: %d)", conn->handle.id, address.host, address.port); + LogInfo("New UDP connection (id: %lld, addr: %d, port: %d)", conn->handle.id, address.host, address.port); ServerDriver_OnClientConnected(conn); return conn; @@ -3162,7 +3162,7 @@ static int UDP_Server_RecvPackets(NBN_GameServer *server) { if (bytes <= 0) break; - if (bytes <= NBN_PACKET_HEADER_SIZE) + if (bytes < NBN_PACKET_HEADER_SIZE) continue; if (Packet_InitRead(&packet, server->endpoint.protocol_id, bytes) < 0) { @@ -4185,7 +4185,17 @@ static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimu * ====== LOGGING ====== */ -static int log_level = NBN_LOG_INFO; +#ifdef NBN_DEBUG + +#define NBN_DEFAULT_LOG_LEVEL NBN_LOG_DEBUG + +#else + +#define NBN_DEFAULT_LOG_LEVEL NBN_LOG_INFO + +#endif // NBN_DEBUG + +static int log_level = NBN_DEFAULT_LOG_LEVEL; void NBN_SetLogLevel(NBN_LogLevel level) { log_level = level; } From b1fda6a18ee92fdb84b7abd88cb3ea4bd25d2130 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sun, 15 Feb 2026 08:56:46 +0100 Subject: [PATCH 70/85] update echo example --- examples/echo/CMakeLists.txt | 8 ++++++-- examples/echo/client.c | 36 ------------------------------------ examples/echo/server.c | 7 ++++--- 3 files changed, 10 insertions(+), 41 deletions(-) diff --git a/examples/echo/CMakeLists.txt b/examples/echo/CMakeLists.txt index ca29831..d691f80 100644 --- a/examples/echo/CMakeLists.txt +++ b/examples/echo/CMakeLists.txt @@ -59,8 +59,12 @@ endif (WEBRTC_NATIVE) unset(WEBRTC_NATIVE) -target_compile_definitions(echo_client PUBLIC NBN_DEBUG) -target_compile_definitions(echo_server PUBLIC NBN_DEBUG) +if (CMAKE_BUILD_TYPE MATCHES "Debug") + message("Compiling in Debug mode with packet simulator enabled") + + target_compile_definitions(echo_client PUBLIC NBN_DEBUG NBN_USE_PACKET_SIMULATOR) + target_compile_definitions(echo_server PUBLIC NBN_DEBUG NBN_USE_PACKET_SIMULATOR) +endif() if(WIN32) target_link_libraries(echo_client ws2_32) diff --git a/examples/echo/client.c b/examples/echo/client.c index e946571..79ef70d 100644 --- a/examples/echo/client.c +++ b/examples/echo/client.c @@ -97,8 +97,6 @@ int main(int argc, char *argv[]) { #endif } - NBN_SetLogLevel(NBN_LOG_INFO); - const char *msg = argv[1]; // reserve 4 bytes to write the message length in the message (see the SendEcho function) unsigned int msg_max_len = ECHO_MESSAGE_MAX_LENGTH - 4; @@ -114,40 +112,6 @@ int main(int argc, char *argv[]) { #endif } -#ifdef __EMSCRIPTEN__ - - // Register the WebRTC driver -#ifdef NBN_TLS - NBN_WebRTC_Register((NBN_WebRTC_Config){.enable_tls = true}); -#else - NBN_WebRTC_Register((NBN_WebRTC_Config){.enable_tls = false}); -#endif // NBN_TLS - -#endif // __EMSCRIPTEN__ - -#ifdef NBN_WEBRTC_NATIVE - -#ifdef NBN_TLS - bool enable_tls = true; -#else - bool enable_tls = false; -#endif // NBN_TLS - - const char *ice_servers[] = {"stun:stun01.sipphone.com"}; - NBN_WebRTC_C_Config cfg = {.ice_servers = ice_servers, - .ice_servers_count = 1, - .enable_tls = enable_tls, - .cert_path = NULL, - .key_path = NULL, - .passphrase = NULL, - .log_level = RTC_LOG_VERBOSE}; - - NBN_WebRTC_C_Register(cfg); - -#endif // NBN_WEBRTC_NATIVE - - // Initialize the client - // Start the client with a protocol name (must be the same than the one used by the server) // the server host and port diff --git a/examples/echo/server.c b/examples/echo/server.c index b5cb920..b546858 100644 --- a/examples/echo/server.c +++ b/examples/echo/server.c @@ -34,9 +34,12 @@ static int EchoReceivedMessage(void) { // Get info about the received message NBN_MessageInfo msg_info = NBN_GameServer_GetMessageInfo(); - assert(msg_info.sender->id == conn_id); assert(msg_info.type == ECHO_MESSAGE_TYPE); + log_info("Received message of type %d from %lld", msg_info.type, msg_info.sender->id); + + assert(msg_info.sender->id == conn_id); + // read message data NBN_Reader *reader = NBN_GameServer_ReadMessage(); unsigned int length; @@ -65,8 +68,6 @@ static int EchoReceivedMessage(void) { static bool error = false; int main(int argc, const char **argv) { - NBN_SetLogLevel(NBN_LOG_INFO); - #ifdef __EMSCRIPTEN__ // Register the WebRTC driver From ee4c9d704bdbba2380702c554f34009b6167ee1d Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Fri, 6 Mar 2026 14:17:38 +0100 Subject: [PATCH 71/85] rework channel configuration --- nbnet.c | 513 +++++++++++++++++++++++++++----------------------- nbnet.h | 21 +-- soak/client.c | 29 +-- soak/server.c | 17 +- soak/soak.h | 1 + 5 files changed, 311 insertions(+), 270 deletions(-) diff --git a/nbnet.c b/nbnet.c index fb6b5f4..bf3bdaf 100644 --- a/nbnet.c +++ b/nbnet.c @@ -96,14 +96,10 @@ typedef enum NBN_Driver_ID NBN_Driver_ID; #define NBN_EVENT_QUEUE_CAPACITY 1024 -#define NBN_MAX_MESSAGE_TYPES UINT8_MAX -// IMPORTANT: DO NOT FORGET TO MODIFY 'NBN_RESERVED_MESSAGE_TYPES' if you add or remove library messages -#define NBN_RESERVED_MESSAGE_TYPES 4 /* Number of message types reserved for the library */ - -#define NBN_CLIENT_CLOSED_MESSAGE_TYPE NBN_MAX_MESSAGE_TYPES -#define NBN_CLIENT_ACCEPTED_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 1) -#define NBN_DISCONNECTION_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 2) -#define NBN_CONNECTION_REQUEST_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 3) +#define NBN_CLIENT_CLOSED_MESSAGE_TYPE UINT8_MAX +#define NBN_CLIENT_ACCEPTED_MESSAGE_TYPE (UINT8_MAX - 1) +#define NBN_DISCONNECTION_MESSAGE_TYPE (UINT8_MAX - 2) +#define NBN_CONNECTION_REQUEST_MESSAGE_TYPE (UINT8_MAX - 3) /* * Maximum allowed packet size (including header) in bytes. @@ -121,16 +117,11 @@ typedef enum NBN_Driver_ID NBN_Driver_ID; /* Maximum size of packet's data (NBN_PACKET_MAX_DATA_SIZE + NBN_PACKET_HEADER_SIZE = NBN_PACKET_MAX_SIZE) */ #define NBN_PACKET_MAX_DATA_SIZE (NBN_PACKET_MAX_SIZE - NBN_PACKET_HEADER_SIZE) -/* Maximum number of packets that can be sent in a single flush - * - * IMPORTANT: DO NOT INCREASE THIS, it will break packet acks - */ -#define NBN_CONNECTION_MAX_SENT_PACKET_COUNT 16 - #define NBN_MAX_PACKET_ENTRIES 1024 -#define NBN_RESERVED_UNRELIABLE_CHANNEL_ID 0 -#define NBN_RESERVED_RELIABLE_CHANNEL_ID 1 +#define NBN_MAX_CHANNEL_COUNT 16 +#define NBN_CHANNEL_DEFAULT_BUFFER_SIZE 128 +#define NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE 256 typedef enum NBN_PacketResult { NBN_PACKET_WRITE_ERROR = -1, @@ -185,12 +176,11 @@ typedef struct NBN_Message { NBN_MessageHeader header; NBN_MessageType type; NBN_Connection *sender; - uint8_t data[NBN_MESSAGE_MAX_SIZE]; + uint8_t *data; } NBN_Message; typedef struct NBN_OutgoingMessage { NBN_Message message; - uint16_t id; double last_send_time; bool free; } NBN_OutgoingMessage; @@ -202,7 +192,7 @@ typedef struct NBN_IncomingMessage { struct NBN_Channel { uint8_t id; - NBN_ChannelMode type; + NBN_Channel_Mode mode; uint16_t next_outgoing_message_id; uint16_t next_recv_message_id; uint16_t oldest_unacked_message_id; @@ -210,9 +200,11 @@ struct NBN_Channel { uint16_t last_received_message_id; unsigned int next_outgoing_message_slot; unsigned int outgoing_message_count; - NBN_OutgoingMessage outgoing_messages_buffer[NBN_CHANNEL_BUFFER_SIZE]; - NBN_IncomingMessage incoming_messages_buffer[NBN_CHANNEL_BUFFER_SIZE]; - bool ack_buffer[NBN_CHANNEL_BUFFER_SIZE]; + unsigned int buffer_size; + unsigned int max_message_len; + NBN_OutgoingMessage *outgoing_messages_buffer; + NBN_IncomingMessage *incoming_messages_buffer; + bool *ack_buffer; // TODO: needed? }; typedef struct NBN_IPAddress { @@ -242,11 +234,20 @@ struct NBN_Connection { uint8_t is_accepted : 1; uint8_t is_stale : 1; uint8_t is_closed : 1; - struct NBN_Endpoint *endpoint; - NBN_Driver *driver; /* Network driver used for that connection */ - NBN_Channel channels[NBN_CHANNEL_COUNT]; /* Message channels (sending & receiving) */ + NBN_Driver *driver; /* Network driver used for that connection */ + NBN_Channel *channels; /* Message channels (sending & receiving) */ + unsigned int channel_count; NBN_ConnectionStats stats; + /* + * Packet sequencing & acking + */ + uint16_t next_packet_seq_number; + uint16_t last_received_packet_seq_number; + uint32_t packet_send_seq_buffer[NBN_MAX_PACKET_ENTRIES]; + NBN_PacketEntry packet_send_buffer[NBN_MAX_PACKET_ENTRIES]; + uint32_t packet_recv_seq_buffer[NBN_MAX_PACKET_ENTRIES]; + /* Driver-related data attached to the connection */ union { #ifdef NBN_UDP @@ -265,15 +266,6 @@ struct NBN_Connection { } webrtc; #endif // defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) } driver_data; - - /* - * Packet sequencing & acking - */ - uint16_t next_packet_seq_number; - uint16_t last_received_packet_seq_number; - uint32_t packet_send_seq_buffer[NBN_MAX_PACKET_ENTRIES]; - NBN_PacketEntry packet_send_buffer[NBN_MAX_PACKET_ENTRIES]; - uint32_t packet_recv_seq_buffer[NBN_MAX_PACKET_ENTRIES]; }; typedef union NBN_EventData { @@ -392,19 +384,29 @@ static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimu #endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ +typedef struct NBN_Channel_Config { + NBN_Channel_Mode mode; + unsigned int buffer_size; + unsigned int max_message_len; +} NBN_Channel_Config; + struct NBN_Endpoint { NBN_EventQueue event_queue; uint32_t protocol_id; bool is_server; double time; - NBN_Message write_message; NBN_Writer message_writer; NBN_Reader message_reader; uint8_t server_initial_data_buffer[NBN_SERVER_INITIAL_DATA_MAX_SIZE]; uint8_t connection_request_data_buffer[NBN_CONNECTION_REQUEST_DATA_MAX_SIZE]; unsigned int client_connection_request_data_len; unsigned int server_initial_data_len; - NBN_ChannelMode channel_modes[NBN_CHANNEL_COUNT]; + unsigned int channel_count; + NBN_Channel_Config *channels; + NBN_Message *write_messages; + NBN_Message *current_write_message; + uint8_t default_reliable_channel; + uint8_t default_unreliable_channel; #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) NBN_PacketSimulator packet_simulator; #endif @@ -413,7 +415,8 @@ struct NBN_Endpoint { typedef struct NBN_GameServer_Config { const char *protocol_name; uint16_t port; - NBN_ChannelMode channel_modes[NBN_CHANNEL_COUNT]; + NBN_Channel_Config channels[NBN_MAX_CHANNEL_COUNT]; + unsigned int channel_count; } NBN_GameServer_Config; typedef struct NBN_GameServer { @@ -434,7 +437,8 @@ typedef struct NBN_GameClient_Config { const char *protocol_name; const char *host; uint16_t port; - NBN_ChannelMode channel_modes[NBN_CHANNEL_COUNT]; + NBN_Channel_Config channels[NBN_MAX_CHANNEL_COUNT]; + unsigned int channel_count; } NBN_GameClient_Config; typedef struct NBN_GameClient { @@ -849,7 +853,7 @@ int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len) { */ static void Packet_InitWrite(NBN_Packet *, uint32_t, uint16_t, uint16_t, uint32_t); -static NBN_PacketResult Packet_WriteMessage(NBN_Packet *, NBN_OutgoingMessage *); +static NBN_PacketResult Packet_WriteMessage(NBN_Packet *, NBN_Message *); static int Packet_Seal(NBN_Packet *); static int Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); @@ -869,11 +873,9 @@ static void Packet_InitWrite(NBN_Packet *packet, uint32_t protocol_id, uint16_t memset(packet->buffer, 0, sizeof(packet->buffer)); } -static NBN_PacketResult Packet_WriteMessage(NBN_Packet *packet, NBN_OutgoingMessage *out_msg) { - NBN_Message *message = &out_msg->message; - - LogDebug("Write message %d (type: %d, length: %d) to packet %d", out_msg->id, message->header.type, - message->header.length, packet->header.seq_number); +static NBN_PacketResult Packet_WriteMessage(NBN_Packet *packet, NBN_Message *message) { + LogDebug("Write message %d (type: %d, length: %d, channel: %d) to packet %d", message->header.id, + message->header.type, message->header.length, message->header.channel_id, packet->header.seq_number); if (packet->mode != NBN_PACKET_MODE_WRITE || packet->sealed) return NBN_PACKET_WRITE_ERROR; @@ -889,7 +891,7 @@ static NBN_PacketResult Packet_WriteMessage(NBN_Packet *packet, NBN_OutgoingMess NBN_Writer_Init(&writer, packet->buffer + packet->size, sizeof(packet->buffer) - packet->size); - NBN_Writer_WriteUInt16(&writer, out_msg->id); + NBN_Writer_WriteUInt16(&writer, message->header.id); NBN_Writer_WriteUInt16(&writer, message->header.length); NBN_Writer_WriteUInt8(&writer, message->header.type); NBN_Writer_WriteUInt8(&writer, message->header.channel_id); @@ -991,9 +993,9 @@ static unsigned int Channel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2) { return (id2 >= id1) ? id2 - id1 : (((0xFFFF + 1) - id1) + id2) % 0xFFFF; } -static void Channel_Init(NBN_Channel *channel, uint8_t id, NBN_ChannelMode type) { +static void Channel_Init(NBN_Channel *channel, uint8_t id, NBN_Channel_Config cfg) { channel->id = id; - channel->type = type; + channel->mode = cfg.mode; channel->next_outgoing_message_id = 0; channel->next_recv_message_id = 0; channel->outgoing_message_count = 0; @@ -1001,83 +1003,96 @@ static void Channel_Init(NBN_Channel *channel, uint8_t id, NBN_ChannelMode type) channel->next_outgoing_message_slot = 0; channel->oldest_unacked_message_id = 0; channel->most_recent_message_id = 0; - - for (unsigned int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) { + channel->buffer_size = cfg.buffer_size; + channel->max_message_len = cfg.max_message_len; + channel->outgoing_messages_buffer = malloc(sizeof(NBN_OutgoingMessage) * cfg.buffer_size); + channel->incoming_messages_buffer = malloc(sizeof(NBN_IncomingMessage) * cfg.buffer_size); + channel->ack_buffer = malloc(sizeof(bool) * cfg.buffer_size); + + for (unsigned int i = 0; i < cfg.buffer_size; i++) { + channel->incoming_messages_buffer[i].message.data = malloc(cfg.max_message_len); + channel->outgoing_messages_buffer[i].message.data = malloc(cfg.max_message_len); channel->incoming_messages_buffer[i].free = true; channel->outgoing_messages_buffer[i].free = true; } - for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) + for (int i = 0; i < cfg.buffer_size; i++) { channel->ack_buffer[i] = false; + } } static void Channel_UpdateMessageSendTime(NBN_Channel *channel, uint16_t msg_id, double time) { - NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % channel->buffer_size]; - NBN_Assert(msg_id == out_msg->id); + NBN_Assert(msg_id == out_msg->message.header.id); out_msg->last_send_time = time; } -static bool Channel_AddReceivedMessage(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) { - if (channel->type == NBN_CHANNEL_UNRELIABLE) { - if (SEQUENCE_NUMBER_GT(message->header.id, channel->last_received_message_id)) { - NBN_IncomingMessage *inc_msg = - &channel->incoming_messages_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; +static NBN_IncomingMessage *Channel_AddReceivedMessage(NBN_Channel *channel, NBN_MessageHeader *header) { + // TODO: check if current slot not free + // buffer ran out of slots + + NBN_Assert(header->length <= channel->max_message_len); // TODO: replace with channel msg len + + if (channel->mode == NBN_CHANNEL_UNRELIABLE) { + if (SEQUENCE_NUMBER_GT(header->id, channel->last_received_message_id)) { + NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[header->id % channel->buffer_size]; inc_msg->free = false; - memcpy(&inc_msg->message, message, sizeof(NBN_Message)); + inc_msg->message.type = NBN_INCOMING_MESSAGE; + inc_msg->message.header = *header; - channel->last_received_message_id = message->header.id; + channel->last_received_message_id = header->id; LogDebug("Add incomoing message %d of type %d to unreliable channel %d (last received msg id: %d)", - message->header.id, message->header.type, channel->id, channel->last_received_message_id); + header->id, header->type, channel->id, channel->last_received_message_id); - return true; + return inc_msg; } return false; - } else if (channel->type == NBN_CHANNEL_RELIABLE) { - unsigned int dt = Channel_ComputeMessageIdDelta(message->header.id, channel->most_recent_message_id); + } else if (channel->mode == NBN_CHANNEL_RELIABLE) { + unsigned int dt = Channel_ComputeMessageIdDelta(header->id, channel->most_recent_message_id); LogDebug("Add incomoing message %d of type %d to reliable channel %d (most recent msg id: %d, dt: %d)", - message->header.id, message->header.type, channel->id, channel->most_recent_message_id, dt); + header->id, header->type, channel->id, channel->most_recent_message_id, dt); - if (SEQUENCE_NUMBER_GT(message->header.id, channel->most_recent_message_id)) { - NBN_Assert(dt < NBN_CHANNEL_BUFFER_SIZE); + if (SEQUENCE_NUMBER_GT(header->id, channel->most_recent_message_id)) { + NBN_Assert(dt < channel->buffer_size); - channel->most_recent_message_id = message->header.id; + channel->most_recent_message_id = header->id; } else { - /* This is an old message that has already been received, probably coming from - an out of order late packet. */ - if (dt >= NBN_CHANNEL_BUFFER_SIZE) - return false; + if (dt >= channel->buffer_size) + return NULL; - if (SEQUENCE_NUMBER_LT(message->header.id, channel->next_recv_message_id)) { - return false; + if (SEQUENCE_NUMBER_LT(header->id, channel->next_recv_message_id)) { + return NULL; } } - NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; + NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[header->id % channel->buffer_size]; inc_msg->free = false; - memcpy(&inc_msg->message, message, sizeof(NBN_Message)); + inc_msg->message.type = NBN_INCOMING_MESSAGE; + inc_msg->message.header = *header; - return true; + return inc_msg; } NBN_Abort(); } static bool Channel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *message) { - NBN_Assert(channel->type == NBN_CHANNEL_UNRELIABLE || channel->type == NBN_CHANNEL_RELIABLE); + NBN_Assert(channel->mode == NBN_CHANNEL_UNRELIABLE || channel->mode == NBN_CHANNEL_RELIABLE); + NBN_Assert(message->header.length <= channel->max_message_len); uint16_t msg_id = channel->next_outgoing_message_id; - int index = msg_id % NBN_CHANNEL_BUFFER_SIZE; + int index = msg_id % channel->buffer_size; NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[index]; // make sure the outgoing message is not already in use if (!out_msg->free) { - LogError("No outgoing message available in channel %d (type: %d, outgoing message count: %d)", channel->type, + LogError("No outgoing message available in channel %d (type: %d, outgoing message count: %d)", channel->mode, channel->id, channel->outgoing_message_count); #ifdef NBN_DEBUG NBN_Abort(); @@ -1086,10 +1101,12 @@ static bool Channel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *messag return false; } - out_msg->id = msg_id; out_msg->free = false; out_msg->last_send_time = -1; - memcpy(&out_msg->message, message, sizeof(NBN_Message)); + out_msg->message.type = NBN_OUTGOING_MESSAGE; + out_msg->message.header = message->header; + out_msg->message.header.id = msg_id; + memcpy(out_msg->message.data, message->data, message->header.length); channel->next_outgoing_message_id++; channel->outgoing_message_count++; @@ -1098,10 +1115,10 @@ static bool Channel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *messag } static NBN_Message *Channel_GetNextRecvedMessage(NBN_Channel *channel) { - if (channel->type == NBN_CHANNEL_UNRELIABLE) { + if (channel->mode == NBN_CHANNEL_UNRELIABLE) { while (SEQUENCE_NUMBER_LTE(channel->next_recv_message_id, channel->last_received_message_id)) { NBN_IncomingMessage *inc_msg = - &channel->incoming_messages_buffer[channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; + &channel->incoming_messages_buffer[channel->next_recv_message_id % channel->buffer_size]; uint16_t msg_id = channel->next_recv_message_id; channel->next_recv_message_id++; @@ -1114,9 +1131,9 @@ static NBN_Message *Channel_GetNextRecvedMessage(NBN_Channel *channel) { } return NULL; - } else if (channel->type == NBN_CHANNEL_RELIABLE) { + } else if (channel->mode == NBN_CHANNEL_RELIABLE) { NBN_IncomingMessage *inc_msg = - &channel->incoming_messages_buffer[channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; + &channel->incoming_messages_buffer[channel->next_recv_message_id % channel->buffer_size]; if (!inc_msg->free && inc_msg->message.header.id == channel->next_recv_message_id) { inc_msg->free = true; @@ -1131,21 +1148,22 @@ static NBN_Message *Channel_GetNextRecvedMessage(NBN_Channel *channel) { } } -static bool Channel_GetNextOutgoingMessage(NBN_Channel *channel, NBN_OutgoingMessage *res_out_msg, double time) { - if (channel->type == NBN_CHANNEL_UNRELIABLE) { +static bool Channel_GetNextOutgoingMessage(NBN_Channel *channel, NBN_Message **res_msg, double time) { + if (channel->mode == NBN_CHANNEL_UNRELIABLE) { NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[channel->next_outgoing_message_slot]; if (out_msg->free) return false; - *res_out_msg = *out_msg; + *res_msg = &out_msg->message; out_msg->free = true; + channel->next_outgoing_message_slot++; - channel->next_outgoing_message_slot %= NBN_CHANNEL_BUFFER_SIZE; + channel->next_outgoing_message_slot %= channel->buffer_size; return true; - } else if (channel->type == NBN_CHANNEL_RELIABLE) { - int max_message_id = (channel->oldest_unacked_message_id + (NBN_CHANNEL_BUFFER_SIZE - 1)) % (0xFFFF + 1); + } else if (channel->mode == NBN_CHANNEL_RELIABLE) { + int max_message_id = (channel->oldest_unacked_message_id + (channel->buffer_size - 1)) % (0xFFFF + 1); if (SEQUENCE_NUMBER_LT(channel->next_outgoing_message_id, max_message_id)) max_message_id = channel->next_outgoing_message_id; @@ -1153,11 +1171,11 @@ static bool Channel_GetNextOutgoingMessage(NBN_Channel *channel, NBN_OutgoingMes uint16_t msg_id = channel->oldest_unacked_message_id; while (SEQUENCE_NUMBER_LT(msg_id, max_message_id)) { - NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % channel->buffer_size]; if (!out_msg->free && (out_msg->last_send_time < 0 || time - out_msg->last_send_time >= NBN_MESSAGE_RESEND_DELAY)) { - *res_out_msg = *out_msg; + *res_msg = &out_msg->message; return true; } @@ -1171,7 +1189,7 @@ static bool Channel_GetNextOutgoingMessage(NBN_Channel *channel, NBN_OutgoingMes } static int Channel_OnMessageSent(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) { - if (channel->type != NBN_CHANNEL_UNRELIABLE) { + if (channel->mode != NBN_CHANNEL_UNRELIABLE) { return 0; } @@ -1181,14 +1199,14 @@ static int Channel_OnMessageSent(NBN_Endpoint *endpoint, NBN_Channel *channel, N } static int Channel_OnOutgoingMessageAcked(NBN_Endpoint *endpoint, NBN_Channel *channel, uint16_t msg_id) { - if (channel->type != NBN_CHANNEL_RELIABLE) { + if (channel->mode != NBN_CHANNEL_RELIABLE) { return 0; } - int index = msg_id % NBN_CHANNEL_BUFFER_SIZE; + int index = msg_id % channel->buffer_size; NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[index]; - if (out_msg->free || out_msg->id != msg_id) + if (out_msg->free || out_msg->message.header.id != msg_id) return 0; out_msg->free = true; @@ -1200,9 +1218,9 @@ static int Channel_OnOutgoingMessageAcked(NBN_Endpoint *endpoint, NBN_Channel *c channel->outgoing_message_count--; if (msg_id == channel->oldest_unacked_message_id) { - for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) { + for (int i = 0; i < channel->buffer_size; i++) { uint16_t ack_msg_id = msg_id + i; - int index = ack_msg_id % NBN_CHANNEL_BUFFER_SIZE; + int index = ack_msg_id % channel->buffer_size; if (channel->ack_buffer[index]) { channel->ack_buffer[index] = false; @@ -1226,6 +1244,7 @@ static int Channel_OnOutgoingMessageAcked(NBN_Endpoint *endpoint, NBN_Channel *c * ====== CONNECTION ====== */ +static void Connection_Destroy(NBN_Connection *); static uint32_t Connection_BuildPacketAckBits(NBN_Connection *); static int Connection_DecodePacketHeader(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); static int Connection_AckPacket(NBN_Endpoint *, NBN_Connection *, uint16_t, double time); @@ -1234,8 +1253,8 @@ static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *, u static bool Connection_InsertReceivedPacketEntry(NBN_Connection *, uint16_t); static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *, uint16_t); static bool Connection_IsPacketReceived(NBN_Connection *, uint16_t); -static int Connection_SendPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry *, double); -static int Connection_ReadNextMessageFromBuffer(NBN_Endpoint *, NBN_Reader *, NBN_Message *); +static int Connection_SendPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry *, double, bool); +static int Connection_ReadNextMessageHeader(NBN_Endpoint *, NBN_Reader *, NBN_MessageHeader *); static void Connection_UpdateAveragePing(NBN_Connection *, double); static void Connection_UpdateAveragePacketLoss(NBN_Connection *, uint16_t); static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *, float); @@ -1270,32 +1289,46 @@ static int Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connecti for (int i = 0; i < packet->header.messages_count; i++) { LogDebug("Reading message number %d from packet %d", i, packet->header.seq_number); - static NBN_Message message = {0}; - int msg_len = Connection_ReadNextMessageFromBuffer(endpoint, &msg_reader, &message); + static NBN_MessageHeader header = {0}; + int msg_len = Connection_ReadNextMessageHeader(endpoint, &msg_reader, &header); if (msg_len < 0) { - LogError("Failed to read packet, invalid data"); + LogError("Failed to read packet: invalid message header"); return NBN_ERROR; } - message.type = NBN_INCOMING_MESSAGE; - - uint8_t channel_id = message.header.channel_id; + uint8_t channel_id = header.channel_id; - if (channel_id > NBN_CHANNEL_COUNT - 1) { - LogError("Failed to read packet, message had invalid channel"); + if (channel_id > endpoint->channel_count - 1) { + LogError("Failed to read packet: invalid message channel %d", channel_id); return NBN_ERROR; } NBN_Channel *channel = &connection->channels[channel_id]; - if (Channel_AddReceivedMessage(endpoint, channel, &message)) { - LogDebug("Received message %d (type: %d) on channel %d", message.header.id, message.header.type, - channel->id); + if (msg_len > channel->max_message_len) { + LogError("Failed to read packet: message %d too large for channel %d (%d > %d)", header.id, channel_id, + header.length, channel->max_message_len); + + return NBN_ERROR; + } + + NBN_IncomingMessage *inc_msg = Channel_AddReceivedMessage(channel, &header); + + if (inc_msg) { + if (msg_len > 0) { + if (NBN_Reader_ReadBytes(&msg_reader, inc_msg->message.data, msg_len) < 0) { + LogError("Failed to read message data"); + + return NBN_ERROR; + } + } + + LogDebug("Received message %d (type: %d) on channel %d", header.id, header.type, channel->id); } else { - LogDebug("Message %d was discarded by channel %d", message.header.id, channel->id); + LogDebug("Message %d was discarded by channel %d", header.id, channel->id); } } @@ -1313,26 +1346,27 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); - for (unsigned int i = 0; i < NBN_CHANNEL_COUNT; i++) { + for (unsigned int i = 0; i < endpoint->channel_count; i++) { NBN_Channel *channel = &connection->channels[i]; LogDebug("Flushing channel %d (message count: %d)", channel->id, channel->outgoing_message_count); - NBN_OutgoingMessage out_msg; + NBN_Message *out_msg; unsigned int j = 0; // TODO: use bandwidth to determine how many packets to send at most - while (j < channel->outgoing_message_count && sent_packet_count < NBN_CONNECTION_MAX_SENT_PACKET_COUNT && + while (j < channel->outgoing_message_count && sent_packet_count < 16 && Channel_GetNextOutgoingMessage(channel, &out_msg, time)) { - NBN_Message *message = &out_msg.message; - uint16_t msg_id = out_msg.id; + uint8_t msg_type = out_msg->header.type; + uint16_t msg_id = out_msg->header.id; + uint16_t msg_len = out_msg->header.length; bool message_sent = false; - NBN_PacketResult ret = Packet_WriteMessage(&packet, &out_msg); + NBN_PacketResult ret = Packet_WriteMessage(&packet, out_msg); if (ret == NBN_PACKET_WRITE_OK) { message_sent = true; } else if (ret == NBN_PACKET_WRITE_NO_SPACE) { - if (Connection_SendPacket(connection, &packet, packet_entry, time) < 0) { + if (Connection_SendPacket(connection, &packet, packet_entry, time, endpoint->is_server) < 0) { LogError("Failed to send packet %d", packet.header.seq_number); return NBN_ERROR; @@ -1343,7 +1377,7 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); - NBN_PacketResult ret = Packet_WriteMessage(&packet, &out_msg); + NBN_PacketResult ret = Packet_WriteMessage(&packet, out_msg); if (ret != NBN_PACKET_WRITE_OK) { LogError("Failed to send packet %d", packet.header.seq_number); @@ -1353,7 +1387,7 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn message_sent = true; } else if (ret == NBN_PACKET_WRITE_ERROR) { - LogError("Failed to write message %d of type %d to packet %d", msg_id, message->header.type, + LogError("Failed to write message %d of type %d to packet %d", msg_id, msg_type, packet.header.seq_number); return NBN_ERROR; @@ -1361,20 +1395,20 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn if (message_sent) { LogDebug("Message %d added to packet %d (length: %d, type: %d)", msg_id, packet.header.seq_number, - message->header.length, message->header.type); + msg_len, msg_type); Channel_UpdateMessageSendTime(channel, msg_id, time); packet_entry->messages[packet_entry->messages_count++] = (NBN_MessageEntry){msg_id, channel->id}; - Channel_OnMessageSent(endpoint, channel, message); + Channel_OnMessageSent(endpoint, channel, out_msg); } j++; } } - if (Connection_SendPacket(connection, &packet, packet_entry, time) < 0) { + if (Connection_SendPacket(connection, &packet, packet_entry, time, endpoint->is_server) < 0) { LogError("Failed to send packet %d to connection %lld", packet.header.seq_number, connection->handle.id); return NBN_ERROR; @@ -1425,6 +1459,24 @@ static int Connection_DecodePacketHeader(NBN_Endpoint *endpoint, NBN_Connection return 0; } +static void Connection_Destroy(NBN_Connection *connection) { + for (int i = 0; i < connection->channel_count; i++) { + NBN_Channel *channel = &connection->channels[i]; + + for (int j = 0; j < channel->buffer_size; j++) { + free(channel->incoming_messages_buffer[j].message.data); + free(channel->outgoing_messages_buffer[j].message.data); + } + + free(channel->incoming_messages_buffer); + free(channel->outgoing_messages_buffer); + free(channel->ack_buffer); + } + + free(connection->channels); + free(connection); +} + static uint32_t Connection_BuildPacketAckBits(NBN_Connection *connection) { uint32_t ack_bits = 0; @@ -1527,7 +1579,7 @@ static bool Connection_IsPacketReceived(NBN_Connection *connection, uint16_t pac } static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, NBN_PacketEntry *packet_entry, - double time) { + double time, bool is_server) { LogDebug("Send packet %d to connection %lld (messages count: %d)", packet->header.seq_number, connection->handle.id, packet->header.messages_count); @@ -1541,7 +1593,7 @@ static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, packet_entry->send_time = time; - if (connection->endpoint->is_server) { + if (is_server) { #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) return PacketSimulator_EnqueuePacket(&nbn_game_server.endpoint.packet_simulator, packet, connection); #else @@ -1561,46 +1613,32 @@ static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, } } -static int Connection_ReadNextMessageFromBuffer(NBN_Endpoint *endpoint, NBN_Reader *reader, NBN_Message *message) { - if (NBN_Reader_ReadUInt16(reader, &message->header.id) < 0) { +static int Connection_ReadNextMessageHeader(NBN_Endpoint *endpoint, NBN_Reader *reader, NBN_MessageHeader *header) { + if (NBN_Reader_ReadUInt16(reader, &header->id) < 0) { LogError("Failed to read message id"); return NBN_ERROR; } - if (NBN_Reader_ReadUInt16(reader, &message->header.length) < 0) { + if (NBN_Reader_ReadUInt16(reader, &header->length) < 0) { LogError("Failed to read message length"); return NBN_ERROR; } - if (NBN_Reader_ReadUInt8(reader, &message->header.type) < 0) { + if (NBN_Reader_ReadUInt8(reader, &header->type) < 0) { LogError("Failed to read message type"); return NBN_ERROR; } - if (NBN_Reader_ReadUInt8(reader, &message->header.channel_id) < 0) { + if (NBN_Reader_ReadUInt8(reader, &header->channel_id) < 0) { LogError("Failed to read message channel"); return NBN_ERROR; } - uint16_t msg_len = message->header.length; - - if (msg_len > 0) { - if (msg_len > NBN_MESSAGE_MAX_SIZE) { - LogError("Failed to read message: too big"); - } - - if (NBN_Reader_ReadBytes(reader, message->data, msg_len) < 0) { - LogError("Failed to read message data"); - - return NBN_ERROR; - } - } - - return 0; + return header->length; } static void Connection_UpdateAveragePing(NBN_Connection *connection, double ping) { @@ -1660,7 +1698,7 @@ static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *connection * ====== ENDPOINT ====== */ -static void Endpoint_Init(NBN_Endpoint *, uint32_t, bool, NBN_ChannelMode[NBN_CHANNEL_COUNT]); +static void Endpoint_Init(NBN_Endpoint *, uint32_t, bool, NBN_Channel_Config *, unsigned int); static void Endpoint_Deinit(NBN_Endpoint *); static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, NBN_Connection_ID, NBN_Driver_ID); static uint32_t Endpoint_BuildProtocolId(const char *); @@ -1669,17 +1707,22 @@ static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN static void Endpoint_UpdateTime(NBN_Endpoint *); static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *, uint8_t, uint8_t); -static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_server, - NBN_ChannelMode channel_modes[NBN_CHANNEL_COUNT]) { - if (NBN_CHANNEL_COUNT < 2) { - LogError("At least 2 channels are necessary, check the NBN_CHANNEL_COUNT macro"); - NBN_Abort(); - } +static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_server, NBN_Channel_Config *channels, + unsigned int channel_count) { + NBN_Assert(channel_count >= 2 && channel_count <= NBN_MAX_CHANNEL_COUNT); endpoint->is_server = is_server; endpoint->protocol_id = protocol_id; + endpoint->channel_count = channel_count; + endpoint->channels = malloc(sizeof(NBN_Channel_Config) * channel_count); + + endpoint->write_messages = malloc(sizeof(NBN_Message) * channel_count); + + for (int i = 0; i < channel_count; i++) { + endpoint->write_messages[i].data = malloc(channels[i].max_message_len); + } - memcpy(&endpoint->channel_modes, channel_modes, sizeof(NBN_ChannelMode) * NBN_CHANNEL_COUNT); + memcpy(endpoint->channels, channels, sizeof(NBN_Channel_Config) * channel_count); EventQueue_Init(&endpoint->event_queue); @@ -1692,8 +1735,11 @@ static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_ } static void Endpoint_Deinit(NBN_Endpoint *endpoint) { - (void)endpoint; + for (int i = 0; i < endpoint->channel_count; i++) { + free(endpoint->write_messages[i].data); + } + free(endpoint->write_messages); #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) PacketSimulator_Stop(&endpoint->packet_simulator); #endif @@ -1705,7 +1751,6 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, NBN_Con connection->handle.id = id; connection->handle.user_data = NULL; - connection->endpoint = endpoint; connection->last_recv_packet_time = endpoint->time; connection->next_packet_seq_number = 1; connection->last_received_packet_seq_number = 0; @@ -1724,9 +1769,11 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, NBN_Con NBN_ConnectionStats stats = {0}; connection->stats = stats; + connection->channels = malloc(sizeof(NBN_Channel) * endpoint->channel_count); + connection->channel_count = endpoint->channel_count; - for (int i = 0; i < NBN_CHANNEL_COUNT; i++) { - Channel_Init(&connection->channels[i], i, endpoint->channel_modes[i]); + for (int i = 0; i < endpoint->channel_count; i++) { + Channel_Init(&connection->channels[i], i, endpoint->channels[i]); } switch (driver_id) { @@ -1784,13 +1831,16 @@ static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *pa } static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, uint8_t type, uint8_t channel_id) { - NBN_Message *message = &endpoint->write_message; + NBN_Assert(channel_id < endpoint->channel_count); + + NBN_Message *message = &endpoint->write_messages[channel_id]; + unsigned int max_message_len = endpoint->channels[channel_id].max_message_len; message->header = (NBN_MessageHeader){0, 0, type, channel_id}; message->sender = NULL; - message->type = NBN_OUTGOING_MESSAGE; - endpoint->message_writer.position = 0; + NBN_Writer_Init(&endpoint->message_writer, message->data, max_message_len); + endpoint->current_write_message = message; } static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Message *message) { @@ -1800,9 +1850,6 @@ static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connectio uint8_t channel_id = message->header.channel_id; NBN_Channel *channel = &connection->channels[channel_id]; - NBN_Assert(channel); - NBN_Assert(channel_id != NBN_RESERVED_RELIABLE_CHANNEL_ID || channel->type == NBN_CHANNEL_RELIABLE); - LogDebug("Enqueue message of type %d on channel %d", message->header.type, channel->id); if (!Channel_AddOutgoingMessage(channel, message)) { @@ -1835,18 +1882,6 @@ static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) { // END OF ENDPOINT // =================================================== -static void InitChannelModes(NBN_ChannelMode channel_modes[NBN_CHANNEL_COUNT]) { - // channel 0 is always unreliable, channel 1 is always reliable - // additional channels are reliable by default - for (int i = 0; i < NBN_CHANNEL_COUNT; i++) { - if (i == NBN_RESERVED_UNRELIABLE_CHANNEL_ID) { - channel_modes[i] = NBN_CHANNEL_UNRELIABLE; - } else { - channel_modes[i] = NBN_CHANNEL_RELIABLE; - } - } -} - /** * ====== CLIENT ====== */ @@ -1858,19 +1893,26 @@ static NBN_Connection *CreateServerConnection(NBN_Driver_ID driver_id); void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port) { nbn_game_client.config = (NBN_GameClient_Config){.protocol_name = protocol_name, .host = host, .port = port}; - - InitChannelModes(nbn_game_client.config.channel_modes); nbn_game_client.client_data_writer.position = 0; + + nbn_game_client.endpoint.default_reliable_channel = NBN_GameClient_CreateChannel( + NBN_CHANNEL_RELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); + nbn_game_client.endpoint.default_unreliable_channel = NBN_GameClient_CreateChannel( + NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); } -void NBN_GameClient_SetChannelMode(uint8_t channel_id, NBN_ChannelMode mode) { - if (channel_id == NBN_RESERVED_RELIABLE_CHANNEL_ID && mode != NBN_CHANNEL_RELIABLE) { - LogError("Cannot change the mode of channel %d as it's used internally by the library", - NBN_RESERVED_RELIABLE_CHANNEL_ID); - NBN_Abort(); - } +uint8_t NBN_GameClient_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len) { + NBN_GameClient_Config *cfg = &nbn_game_client.config; + + NBN_Assert(cfg->channel_count < NBN_MAX_CHANNEL_COUNT); - nbn_game_client.config.channel_modes[channel_id] = mode; + uint8_t channel_id = cfg->channel_count; + + cfg->channels[channel_id] = + (NBN_Channel_Config){.mode = mode, .buffer_size = buffer_size, .max_message_len = max_message_len}; + cfg->channel_count++; + + return channel_id; } NBN_Writer *NBN_GameClient_WriteConnectionRequestData(void) { @@ -1929,7 +1971,7 @@ int NBN_GameClient_Start(void) { uint16_t port = config.port; uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - Endpoint_Init(&nbn_game_client.endpoint, protocol_id, false, config.channel_modes); + Endpoint_Init(&nbn_game_client.endpoint, protocol_id, false, config.channels, config.channel_count); int driver_count = StartClientDrivers(host, port); @@ -1946,8 +1988,7 @@ int NBN_GameClient_Start(void) { unsigned int connection_data_len = nbn_game_client.client_data_writer.position; - NBN_Writer *writer = - NBN_GameClient_CreateMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE, NBN_RESERVED_RELIABLE_CHANNEL_ID); + NBN_Writer *writer = NBN_GameClient_CreateReliableMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE); if (connection_data_len > 0) { NBN_Assert(connection_data_len <= sizeof(nbn_game_client.endpoint.connection_request_data_buffer)); @@ -1975,7 +2016,7 @@ void NBN_GameClient_Stop(void) { if (!nbn_game_client.server_connection->is_closed && !nbn_game_client.server_connection->is_stale) { LogInfo("Disconnecting..."); - NBN_GameClient_CreateMessage(NBN_DISCONNECTION_MESSAGE_TYPE, NBN_RESERVED_RELIABLE_CHANNEL_ID); + NBN_GameClient_CreateReliableMessage(NBN_DISCONNECTION_MESSAGE_TYPE); if (NBN_GameClient_EnqueueMessage() < 0) { LogError("Failed to send disconnection message"); @@ -1990,7 +2031,7 @@ void NBN_GameClient_Stop(void) { LogInfo("Disconnected"); } - free(nbn_game_client.server_connection); + Connection_Destroy(nbn_game_client.server_connection); nbn_game_client.server_connection = NULL; } @@ -2080,9 +2121,8 @@ NBN_Client_Event NBN_GameClient_Poll(void) { NBN_Connection *server_conn = nbn_game_client.server_connection; - for (unsigned int i = 0; i < NBN_CHANNEL_COUNT; i++) { + for (unsigned int i = 0; i < endpoint->channel_count; i++) { NBN_Channel *channel = &server_conn->channels[i]; - NBN_Message *msg; while ((msg = Channel_GetNextRecvedMessage(channel)) != NULL) { @@ -2114,28 +2154,26 @@ int NBN_GameClient_Flush(void) { } NBN_Writer *NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id) { - NBN_Assert(channel_id < NBN_CHANNEL_COUNT); - NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - NBN_Writer *writer = &endpoint->message_writer; - NBN_Writer_Init(writer, endpoint->write_message.data, sizeof(endpoint->write_message.data)); + NBN_Assert(channel_id < endpoint->channel_count); + Endpoint_CreateOutgoingMessage(endpoint, type, channel_id); - return writer; + return &endpoint->message_writer; } NBN_Writer *NBN_GameClient_CreateReliableMessage(uint8_t type) { - return NBN_GameClient_CreateMessage(type, NBN_RESERVED_RELIABLE_CHANNEL_ID); + return NBN_GameClient_CreateMessage(type, nbn_game_client.endpoint.default_reliable_channel); } NBN_Writer *NBN_GameClient_CreateUnreliableMessage(uint8_t type) { - return NBN_GameClient_CreateMessage(type, NBN_RESERVED_UNRELIABLE_CHANNEL_ID); + return NBN_GameClient_CreateMessage(type, nbn_game_client.endpoint.default_unreliable_channel); } int NBN_GameClient_EnqueueMessage(void) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - NBN_Message *message = &endpoint->write_message; + NBN_Message *message = endpoint->current_write_message; message->header.length = endpoint->message_writer.position; @@ -2299,19 +2337,27 @@ static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev); void NBN_GameServer_Init(const char *protocol_name, uint16_t port) { nbn_game_server.config = (NBN_GameServer_Config){.protocol_name = protocol_name, .port = port}; - InitChannelModes(nbn_game_server.config.channel_modes); nbn_game_server.server_data_writer.position = 0; hmdefault(nbn_game_server.clients, NULL); + + nbn_game_server.endpoint.default_reliable_channel = NBN_GameServer_CreateChannel( + NBN_CHANNEL_RELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); + nbn_game_server.endpoint.default_unreliable_channel = NBN_GameServer_CreateChannel( + NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); } -void NBN_GameServer_SetChannelMode(uint8_t channel_id, NBN_ChannelMode mode) { - if (channel_id == NBN_RESERVED_RELIABLE_CHANNEL_ID && mode != NBN_CHANNEL_RELIABLE) { - LogError("Cannot change the mode of channel %d as it's used internally by the library", - NBN_RESERVED_RELIABLE_CHANNEL_ID); - NBN_Abort(); - } +uint8_t NBN_GameServer_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len) { + NBN_GameServer_Config *cfg = &nbn_game_server.config; + + NBN_Assert(cfg->channel_count < NBN_MAX_CHANNEL_COUNT); + + uint8_t channel_id = cfg->channel_count; - nbn_game_server.config.channel_modes[channel_id] = mode; + cfg->channels[channel_id] = + (NBN_Channel_Config){.mode = mode, .buffer_size = buffer_size, .max_message_len = max_message_len}; + cfg->channel_count++; + + return channel_id; } static int StartServerDrivers(uint16_t port) { @@ -2356,7 +2402,7 @@ int NBN_GameServer_Start(void) { uint16_t port = config.port; uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - Endpoint_Init(&nbn_game_server.endpoint, protocol_id, true, config.channel_modes); + Endpoint_Init(&nbn_game_server.endpoint, protocol_id, true, config.channels, config.channel_count); nbn_game_server.closed_clients_head = NULL; @@ -2367,7 +2413,7 @@ int NBN_GameServer_Start(void) { NBN_Abort(); } - LogInfo("Started (channel count: %d)", NBN_CHANNEL_COUNT); + LogInfo("Started (channel count: %d)", nbn_game_server.endpoint.channel_count); return 0; } @@ -2381,7 +2427,7 @@ void NBN_GameServer_Stop(void) { NBN_Connection *conn = nbn_game_server.clients[i].value; conn->driver->impl.serv_cleanup_connection(&nbn_game_server, conn); - free(conn); + Connection_Destroy(conn); } hmfree(nbn_game_server.clients); @@ -2478,7 +2524,7 @@ NBN_Server_Event NBN_GameServer_Poll(void) { for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { NBN_Connection *client = nbn_game_server.clients[i].value; - for (unsigned int i = 0; i < NBN_CHANNEL_COUNT; i++) { + for (unsigned int i = 0; i < endpoint->channel_count; i++) { NBN_Channel *channel = &client->channels[i]; if (channel) { @@ -2554,37 +2600,37 @@ int NBN_GameServer_CloseClient(NBN_ConnectionHandle *conn) { } NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id) { - NBN_Assert(channel_id < NBN_CHANNEL_COUNT); NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + + NBN_Assert(channel_id < endpoint->channel_count); + NBN_Writer *writer = &endpoint->message_writer; - NBN_Writer_Init(writer, endpoint->write_message.data, sizeof(endpoint->write_message.data)); Endpoint_CreateOutgoingMessage(endpoint, type, channel_id); return writer; } NBN_Writer *NBN_GameServer_CreateReliableMessage(uint8_t type) { - return NBN_GameServer_CreateMessage(type, NBN_RESERVED_RELIABLE_CHANNEL_ID); + return NBN_GameServer_CreateMessage(type, nbn_game_server.endpoint.default_reliable_channel); } NBN_Writer *NBN_GameServer_CreateUnreliableMessage(uint8_t type) { - return NBN_GameServer_CreateMessage(type, NBN_RESERVED_UNRELIABLE_CHANNEL_ID); + return NBN_GameServer_CreateMessage(type, nbn_game_server.endpoint.default_unreliable_channel); } int NBN_GameServer_EnqueueMessageFor(NBN_ConnectionHandle *conn) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - NBN_Message *message = &endpoint->write_message; - message->header.length = endpoint->message_writer.position; + NBN_Message *message = endpoint->current_write_message; - int ret = GameServer_EnqueueMessageFor(HANDLE_TO_CONN(conn), message); + message->header.length = endpoint->message_writer.position; - return ret; + return GameServer_EnqueueMessageFor(HANDLE_TO_CONN(conn), message); } int NBN_GameServer_EnqueueBroadcastMessage(void) { NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - NBN_Message *message = &endpoint->write_message; + NBN_Message *message = endpoint->current_write_message; message->header.length = endpoint->message_writer.position; int ret = 0; @@ -2595,7 +2641,7 @@ int NBN_GameServer_EnqueueBroadcastMessage(void) { if (!conn->is_accepted || conn->is_closed) continue; - if (GameServer_EnqueueMessageFor(conn, &endpoint->write_message) < 0) { + if (GameServer_EnqueueMessageFor(conn, message) < 0) { LogError("Failed to send message to client %lld when broadcasting", conn->handle.id); ret = NBN_ERROR; break; @@ -2636,8 +2682,7 @@ int NBN_GameServer_AcceptIncomingConnection(void) { unsigned data_length = nbn_game_server.server_data_writer.position; NBN_Connection *client = nbn_game_server.last_event.data.connection; - NBN_Writer *writer = - NBN_GameServer_CreateMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE, NBN_RESERVED_RELIABLE_CHANNEL_ID); + NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); if (data_length > 0) { NBN_Assert(data_length <= sizeof(nbn_game_server.endpoint.server_initial_data_buffer)); @@ -2761,8 +2806,7 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool if (!disconnection) { LogDebug("Send close message for client %lld (code: %d)", client->handle.id, code); - NBN_Writer *writer = - NBN_GameServer_CreateMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE, NBN_RESERVED_RELIABLE_CHANNEL_ID); + NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE); NBN_Writer_WriteInt32(writer, code); NBN_GameServer_EnqueueMessageFor((NBN_ConnectionHandle *)client); } @@ -2857,7 +2901,7 @@ static void GameServer_RemoveClosedClientConnections(void) { // Destroy the connection - free(client); + Connection_Destroy(client); // Remove the connection from the closed clients list @@ -3492,11 +3536,11 @@ static char *ParseSignalingMessage(const char *msg, size_t msg_len, const char * static void ProcessSignalingMessage(NBN_WebRTC_Peer_ID peer_id, int ws, const char *msg, int size, const char *type) { // for some reason the size of the message is negative - // in libdatachannel documentation (https://github.com/paullouisageneau/libdatachannel/blob/master/DOC.md) there is - // mention of: size: if size >= 0, data is interpreted as a binary message of length size, otherwise it is - // interpreted as a null-terminated UTF-8 string. so I guess in this case msg is a null terminated string? I could - // not find more information about this so I decided to go with flipping the size to positive even though it feels - // weird, but it works so... ¯\_(ツ)_/¯ + // in libdatachannel documentation (https://github.com/paullouisageneau/libdatachannel/blob/master/DOC.md) there + // is mention of: size: if size >= 0, data is interpreted as a binary message of length size, otherwise it is + // interpreted as a null-terminated UTF-8 string. so I guess in this case msg is a null terminated string? I + // could not find more information about this so I decided to go with flipping the size to positive even though + // it feels weird, but it works so... ¯\_(ツ)_/¯ if (size < 0) size *= -1; @@ -4158,8 +4202,9 @@ static int PacketSimulator_SendPacket(NBN_PacketSimulator *packet_simulator, NBN } NBN_Driver *driver = receiver->driver; + bool is_server = packet_simulator->endpoint->is_server; - if (receiver->endpoint->is_server) { + if (is_server) { if (receiver->is_stale) return 0; diff --git a/nbnet.h b/nbnet.h index 1d0f477..def6881 100644 --- a/nbnet.h +++ b/nbnet.h @@ -44,21 +44,6 @@ extern "C" { #define NBN_CONNECTION_REQUEST_DATA_MAX_SIZE 256 #endif -// TODO: doc -#ifndef NBN_CHANNEL_COUNT -#define NBN_CHANNEL_COUNT 2 -#endif - -// TODO: doc -#ifndef NBN_MESSAGE_MAX_SIZE -#define NBN_MESSAGE_MAX_SIZE 256 -#endif - -// TODO: doc -#ifndef NBN_CHANNEL_BUFFER_SIZE -#define NBN_CHANNEL_BUFFER_SIZE 256 -#endif - // TODO: doc #ifndef NBN_MESSAGE_RESEND_DELAY #define NBN_MESSAGE_RESEND_DELAY 0.1 /* Number of seconds before a message is resent (reliable messages redundancy) */ @@ -119,7 +104,7 @@ typedef struct NBN_MessageInfo { } NBN_MessageInfo; // TODO: doc -typedef enum NBN_ChannelMode { NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_RELIABLE } NBN_ChannelMode; +typedef enum NBN_Channel_Mode { NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_RELIABLE } NBN_Channel_Mode; typedef enum NBN_Client_Event { NBN_CLIENT_ERROR = NBN_ERROR, @@ -218,7 +203,7 @@ int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len); void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port); // TODO: doc -void NBN_GameClient_SetChannelMode(uint8_t channel_id, NBN_ChannelMode mode); +uint8_t NBN_GameClient_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len); // TODO: doc NBN_Writer *NBN_GameClient_WriteConnectionRequestData(void); @@ -314,7 +299,7 @@ bool NBN_GameClient_IsConnected(void); void NBN_GameServer_Init(const char *protocol_name, uint16_t port); // TODO: doc -void NBN_GameServer_SetChannelMode(uint8_t channel_id, NBN_ChannelMode mode); +uint8_t NBN_GameServer_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len); /** * Start the game server with the provided configuration. diff --git a/soak/client.c b/soak/client.c index c54f727..c9711e1 100644 --- a/soak/client.c +++ b/soak/client.c @@ -22,9 +22,11 @@ */ #include +#include #include #include #include +#include "nbnet.h" #include "soak.h" #include "log.h" @@ -128,7 +130,9 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { } static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) { - SoakChannel *channel = &channels[channel_id]; + assert(channel_id >= 2 && channel_id < SOAK_CHANNEL_COUNT + 2); + + SoakChannel *channel = &channels[channel_id - 2]; unsigned int msg_id; unsigned int data_length; static uint8_t recv_buffer[SOAK_MESSAGE_BIG_MAX_DATA_LENGTH]; @@ -155,7 +159,7 @@ static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) if (data_length != entry->length) { log_error("Expected message %d to have length %d but was %d (channel_id: %d)", msg_id, entry->length, - data_length); + data_length, channel_id); return -1; } @@ -181,7 +185,7 @@ static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) done_channel_count++; } - if (done_channel_count >= NBN_CHANNEL_COUNT) { + if (done_channel_count >= SOAK_CHANNEL_COUNT) { log_info("Received all soak message echoes on all channels"); Soak_Stop(); @@ -270,8 +274,11 @@ int main(int argc, char *argv[]) { NBN_GameClient_Init(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT); - for (uint8_t c = 0; c < NBN_CHANNEL_COUNT; c++) { - NBN_GameClient_SetChannelMode(c, NBN_CHANNEL_RELIABLE); + for (uint8_t c = 0; c < SOAK_CHANNEL_COUNT; c++) { + uint8_t channel_id = NBN_GameClient_CreateChannel(NBN_CHANNEL_RELIABLE, 128, 256); + + // channels 0 and 1 are the default nbnet channels + assert(channel_id == 2 + c); } if (NBN_GameClient_Start() < 0) { @@ -290,14 +297,14 @@ int main(int argc, char *argv[]) { } unsigned int message_count = options.message_count; - unsigned int message_per_channel = message_count / NBN_CHANNEL_COUNT; - unsigned int leftover_message_count = message_count % NBN_CHANNEL_COUNT; - SoakChannel *channels = (SoakChannel *)malloc(sizeof(SoakChannel) * NBN_CHANNEL_COUNT); + unsigned int message_per_channel = message_count / SOAK_CHANNEL_COUNT; + unsigned int leftover_message_count = message_count % SOAK_CHANNEL_COUNT; + SoakChannel *channels = (SoakChannel *)malloc(sizeof(SoakChannel) * SOAK_CHANNEL_COUNT); - for (int c = 0; c < NBN_CHANNEL_COUNT; c++) { + for (int c = 0; c < SOAK_CHANNEL_COUNT; c++) { SoakChannel *channel = &channels[c]; - channel->id = c; // channels 0 and 1 are reserved by the library + channel->id = 2 + c; // channels 0 and 1 are the default nbnet channels channel->next_msg_id = 1; channel->sent_message_count = 0; channel->last_recved_message_id = 0; @@ -309,7 +316,7 @@ int main(int argc, char *argv[]) { } } - channels[NBN_CHANNEL_COUNT - 1].message_count += leftover_message_count; + channels[SOAK_CHANNEL_COUNT - 1].message_count += leftover_message_count; int ret = Soak_MainLoop(Tick, channels); diff --git a/soak/server.c b/soak/server.c index 1cc6e8e..64898af 100644 --- a/soak/server.c +++ b/soak/server.c @@ -70,12 +70,12 @@ static void HandleNewConnection(void) { soak_client->error = false; soak_client->is_closed = false; - soak_client->channels = (SoakChannel *)malloc(sizeof(SoakChannel) * NBN_CHANNEL_COUNT); + soak_client->channels = (SoakChannel *)malloc(sizeof(SoakChannel) * SOAK_CHANNEL_COUNT); - for (unsigned int c = 0; c < NBN_CHANNEL_COUNT; c++) { + for (unsigned int c = 0; c < SOAK_CHANNEL_COUNT; c++) { SoakChannel *channel = &soak_client->channels[c]; - channel->id = c; + channel->id = 2 + c; channel->recved_messages_count = 0; channel->last_recved_message_id = 0; @@ -116,7 +116,7 @@ static void EchoReceivedSoakMessages(void) { if (soak_client->is_closed) continue; - for (unsigned int c = 0; c < NBN_CHANNEL_COUNT; c++) { + for (unsigned int c = 0; c < SOAK_CHANNEL_COUNT; c++) { SoakChannel *channel = &soak_client->channels[c]; while (channel->echo_queue.count > 0) { @@ -157,7 +157,7 @@ static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_ConnectionHandle *s if (soak_client->error) return 0; - SoakChannel *channel = &soak_client->channels[channel_id]; + SoakChannel *channel = &soak_client->channels[channel_id - 2]; unsigned int msg_id; unsigned int data_length; static uint8_t recv_buffer[SOAK_MESSAGE_BIG_MAX_DATA_LENGTH]; @@ -282,8 +282,11 @@ int main(int argc, char *argv[]) { NBN_GameServer_Init(SOAK_PROTOCOL_NAME, SOAK_PORT); - for (uint8_t c = 0; c < NBN_CHANNEL_COUNT; c++) { - NBN_GameServer_SetChannelMode(c, NBN_CHANNEL_RELIABLE); + for (uint8_t c = 0; c < SOAK_CHANNEL_COUNT; c++) { + uint8_t channel_id = NBN_GameServer_CreateChannel(NBN_CHANNEL_RELIABLE, 128, 256); + + // channels 0 and 1 are the default nbnet channels + assert(channel_id == 2 + c); } if (NBN_GameServer_Start()) { diff --git a/soak/soak.h b/soak/soak.h index 1776ff5..a8b5261 100644 --- a/soak/soak.h +++ b/soak/soak.h @@ -54,6 +54,7 @@ #define SOAK_DONE 1 #define SOAK_CLIENT_MAX_PENDING_MESSAGES 50 // max number of unacked messages at a time #define SOAK_SERVER_FULL_CODE 1234 +#define SOAK_CHANNEL_COUNT 4 typedef struct { unsigned int message_count; From 54f7b293c76b73226d44e90ae54093517166a252 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Fri, 6 Mar 2026 17:16:36 +0100 Subject: [PATCH 72/85] fix bug in packet reading code --- nbnet.c | 14 +++++++++----- soak/CMakeLists.txt | 5 +++-- soak/client.c | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/nbnet.c b/nbnet.c index bf3bdaf..437757d 100644 --- a/nbnet.c +++ b/nbnet.c @@ -1050,13 +1050,10 @@ static NBN_IncomingMessage *Channel_AddReceivedMessage(NBN_Channel *channel, NBN return inc_msg; } - return false; + return NULL; } else if (channel->mode == NBN_CHANNEL_RELIABLE) { unsigned int dt = Channel_ComputeMessageIdDelta(header->id, channel->most_recent_message_id); - LogDebug("Add incomoing message %d of type %d to reliable channel %d (most recent msg id: %d, dt: %d)", - header->id, header->type, channel->id, channel->most_recent_message_id, dt); - if (SEQUENCE_NUMBER_GT(header->id, channel->most_recent_message_id)) { NBN_Assert(dt < channel->buffer_size); @@ -1070,6 +1067,9 @@ static NBN_IncomingMessage *Channel_AddReceivedMessage(NBN_Channel *channel, NBN } } + LogDebug("Add incomoing message %d of type %d to reliable channel %d (most recent msg id: %d, dt: %d)", + header->id, header->type, channel->id, channel->most_recent_message_id, dt); + NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[header->id % channel->buffer_size]; inc_msg->free = false; @@ -1301,7 +1301,7 @@ static int Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connecti uint8_t channel_id = header.channel_id; if (channel_id > endpoint->channel_count - 1) { - LogError("Failed to read packet: invalid message channel %d", channel_id); + LogError("Failed to read packet: invalid channel %d", channel_id); return NBN_ERROR; } @@ -1329,6 +1329,10 @@ static int Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connecti LogDebug("Received message %d (type: %d) on channel %d", header.id, header.type, channel->id); } else { LogDebug("Message %d was discarded by channel %d", header.id, channel->id); + + // NBN_Reader_ReadBytes is not called for discarded messages, so we need to + // advance the reader position "manually" + msg_reader.position += msg_len; } } diff --git a/soak/CMakeLists.txt b/soak/CMakeLists.txt index 45851ae..d9b6ad4 100644 --- a/soak/CMakeLists.txt +++ b/soak/CMakeLists.txt @@ -15,8 +15,6 @@ unset(CPP_COMPILE) add_executable(client client.c soak.c log.c cargs.c ../nbnet.c) add_executable(server server.c soak.c log.c cargs.c ../nbnet.c) -add_compile_options(-Wall -Wextra -Wpedantic -std=c99) - if (CMAKE_BUILD_TYPE MATCHES "Debug") message("Compiling in Debug mode with packet simulator enabled") @@ -24,6 +22,9 @@ if (CMAKE_BUILD_TYPE MATCHES "Debug") target_compile_definitions(server PUBLIC NBN_DEBUG NBN_USE_PACKET_SIMULATOR) endif() +target_compile_options(server PUBLIC -Wall -Wextra -std=c99) +target_compile_options(client PUBLIC -Wall -Wextra -std=c99) + target_compile_definitions(client PUBLIC SOAK_CLIENT) target_compile_definitions(server PUBLIC SOAK_SERVER) target_compile_definitions(client PUBLIC NBN_CHANNEL_COUNT=4) diff --git a/soak/client.c b/soak/client.c index c9711e1..5e2c652 100644 --- a/soak/client.c +++ b/soak/client.c @@ -245,7 +245,7 @@ static int Tick(void *data) { } if (connected) { - for (unsigned int c = 0; c < NBN_CHANNEL_COUNT; c++) { + for (unsigned int c = 0; c < SOAK_CHANNEL_COUNT; c++) { SoakChannel *channel = &channels[c]; if (SendSoakMessages(channel, channel->id) < 0) { From 2f7fd86d0e42404b90810d64a08b78d4f89c786b Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sat, 7 Mar 2026 17:37:16 +0100 Subject: [PATCH 73/85] include time.h in soak test --- soak/soak.c | 1 + 1 file changed, 1 insertion(+) diff --git a/soak/soak.c b/soak/soak.c index 22d5dbd..3c35404 100644 --- a/soak/soak.c +++ b/soak/soak.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "log.h" #ifdef __EMSCRIPTEN__ From d5f91f72e3cf7bde245409690b2bcdefe1f5253c Mon Sep 17 00:00:00 2001 From: Nathan BIAGINI Date: Sat, 7 Mar 2026 17:47:36 +0100 Subject: [PATCH 74/85] fix soak test compilation --- nbnet.c | 32 +++++++++++++++++++++++--------- soak/CMakeLists.txt | 6 ++---- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/nbnet.c b/nbnet.c index 437757d..47bebee 100644 --- a/nbnet.c +++ b/nbnet.c @@ -59,13 +59,22 @@ #include #include + +#if _POSIX_C_SOURCE >= 199309L + #include +#else + +#include + +#endif // _POSIX_C_SOURCE >= 199309L + #ifndef CLOCK_MONOTONIC_RAW #define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC #endif -#endif +#endif // NBN_PLATFORM_WINDOWS typedef struct NBN_Connection NBN_Connection; typedef struct NBN_Endpoint NBN_Endpoint; @@ -3923,23 +3932,28 @@ static int WebRTC_Native_Client_Start(NBN_GameClient *client, const char *host, // wait for the connection to be established const float delay = 0.3f; - const long timeout = 5 * 1e9; // 5 seconds to connect - - struct timespec rqtp; - rqtp.tv_sec = 0; - rqtp.tv_nsec = delay * 1e9; - + const long timeout = 5; // 5 seconds to connect float current_time_sec = 0; while (true) { #if defined(_WIN32) || defined(_WIN64) Sleep(delay * 1000); -#else +#elif _POSIX_C_SOURCE >= 199309L + struct timespec rqtp; + rqtp.tv_sec = 0; + rqtp.tv_nsec = delay * 1e9; + if (nanosleep(&rqtp, NULL) < 0) { LogError("nanosleep failed"); - return NBN_ERROR; + NBN_Abort(); + } +#else + if (usleep(delay * 1e6) < 0) { + LogError("usleep failed"); + NBN_Abort(); } #endif + current_time_sec += delay; if (current_time_sec >= timeout || wrtc_client_connected) { diff --git a/soak/CMakeLists.txt b/soak/CMakeLists.txt index d9b6ad4..3a24349 100644 --- a/soak/CMakeLists.txt +++ b/soak/CMakeLists.txt @@ -22,13 +22,11 @@ if (CMAKE_BUILD_TYPE MATCHES "Debug") target_compile_definitions(server PUBLIC NBN_DEBUG NBN_USE_PACKET_SIMULATOR) endif() -target_compile_options(server PUBLIC -Wall -Wextra -std=c99) -target_compile_options(client PUBLIC -Wall -Wextra -std=c99) +target_compile_options(server PUBLIC -Wall -Wextra) +target_compile_options(client PUBLIC -Wall -Wextra) target_compile_definitions(client PUBLIC SOAK_CLIENT) target_compile_definitions(server PUBLIC SOAK_SERVER) -target_compile_definitions(client PUBLIC NBN_CHANNEL_COUNT=4) -target_compile_definitions(server PUBLIC NBN_CHANNEL_COUNT=4) target_include_directories(client PUBLIC "../") target_include_directories(server PUBLIC "../") From 42f910c6ca9c6350bd68cb5c2024195b5782f9e6 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Fri, 13 Mar 2026 08:51:06 +0100 Subject: [PATCH 75/85] increase msg count to 5000 in soak test pipeline --- bin/github-actions/run_soak.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/github-actions/run_soak.sh b/bin/github-actions/run_soak.sh index 5262f21..80c7282 100755 --- a/bin/github-actions/run_soak.sh +++ b/bin/github-actions/run_soak.sh @@ -5,7 +5,7 @@ PACKET_DUPLICATION=0.2 PING=0.15 JITTER=0.1 CHANNEL_COUNT=3 -MESSAGE_COUNT=500 +MESSAGE_COUNT=5000 NODE_CMD="$EMSDK_NODE" run_client() { From d26d258f7d4f353d0bbd62887719f9575e1fcf77 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Fri, 13 Mar 2026 09:16:36 +0100 Subject: [PATCH 76/85] simplify API --- nbnet.c | 258 +++++++++++++++++++------------------------------- nbnet.h | 25 ++--- soak/client.c | 7 +- soak/server.c | 15 +-- 4 files changed, 117 insertions(+), 188 deletions(-) diff --git a/nbnet.c b/nbnet.c index 47bebee..f8e2e16 100644 --- a/nbnet.c +++ b/nbnet.c @@ -179,17 +179,15 @@ typedef struct NBN_MessageHeader { uint8_t channel_id; } NBN_MessageHeader; -typedef enum { NBN_OUTGOING_MESSAGE, NBN_INCOMING_MESSAGE } NBN_MessageType; - typedef struct NBN_Message { NBN_MessageHeader header; - NBN_MessageType type; NBN_Connection *sender; uint8_t *data; } NBN_Message; typedef struct NBN_OutgoingMessage { NBN_Message message; + NBN_Writer writer; double last_send_time; bool free; } NBN_OutgoingMessage; @@ -404,7 +402,6 @@ struct NBN_Endpoint { uint32_t protocol_id; bool is_server; double time; - NBN_Writer message_writer; NBN_Reader message_reader; uint8_t server_initial_data_buffer[NBN_SERVER_INITIAL_DATA_MAX_SIZE]; uint8_t connection_request_data_buffer[NBN_CONNECTION_REQUEST_DATA_MAX_SIZE]; @@ -412,8 +409,6 @@ struct NBN_Endpoint { unsigned int server_initial_data_len; unsigned int channel_count; NBN_Channel_Config *channels; - NBN_Message *write_messages; - NBN_Message *current_write_message; uint8_t default_reliable_channel; uint8_t default_unreliable_channel; #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) @@ -1020,12 +1015,13 @@ static void Channel_Init(NBN_Channel *channel, uint8_t id, NBN_Channel_Config cf for (unsigned int i = 0; i < cfg.buffer_size; i++) { channel->incoming_messages_buffer[i].message.data = malloc(cfg.max_message_len); - channel->outgoing_messages_buffer[i].message.data = malloc(cfg.max_message_len); channel->incoming_messages_buffer[i].free = true; + + channel->outgoing_messages_buffer[i].message.data = malloc(cfg.max_message_len); channel->outgoing_messages_buffer[i].free = true; } - for (int i = 0; i < cfg.buffer_size; i++) { + for (unsigned int i = 0; i < cfg.buffer_size; i++) { channel->ack_buffer[i] = false; } } @@ -1048,7 +1044,6 @@ static NBN_IncomingMessage *Channel_AddReceivedMessage(NBN_Channel *channel, NBN NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[header->id % channel->buffer_size]; inc_msg->free = false; - inc_msg->message.type = NBN_INCOMING_MESSAGE; inc_msg->message.header = *header; channel->last_received_message_id = header->id; @@ -1082,7 +1077,6 @@ static NBN_IncomingMessage *Channel_AddReceivedMessage(NBN_Channel *channel, NBN NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[header->id % channel->buffer_size]; inc_msg->free = false; - inc_msg->message.type = NBN_INCOMING_MESSAGE; inc_msg->message.header = *header; return inc_msg; @@ -1091,9 +1085,8 @@ static NBN_IncomingMessage *Channel_AddReceivedMessage(NBN_Channel *channel, NBN NBN_Abort(); } -static bool Channel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *message) { +static NBN_Writer *Channel_AddOutgoingMessage(NBN_Channel *channel, uint8_t type) { NBN_Assert(channel->mode == NBN_CHANNEL_UNRELIABLE || channel->mode == NBN_CHANNEL_RELIABLE); - NBN_Assert(message->header.length <= channel->max_message_len); uint16_t msg_id = channel->next_outgoing_message_id; int index = msg_id % channel->buffer_size; @@ -1101,26 +1094,32 @@ static bool Channel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *messag // make sure the outgoing message is not already in use if (!out_msg->free) { - LogError("No outgoing message available in channel %d (type: %d, outgoing message count: %d)", channel->mode, - channel->id, channel->outgoing_message_count); + LogError("No outgoing message available in channel %d (mode: %d, outgoing message count: %d, msg_id: %d, " + "index: %d, oldest unacked msg: %d)", + channel->id, channel->mode, channel->outgoing_message_count, msg_id, index, + channel->oldest_unacked_message_id); #ifdef NBN_DEBUG NBN_Abort(); #endif - return false; + return NULL; } out_msg->free = false; out_msg->last_send_time = -1; - out_msg->message.type = NBN_OUTGOING_MESSAGE; - out_msg->message.header = message->header; out_msg->message.header.id = msg_id; - memcpy(out_msg->message.data, message->data, message->header.length); + out_msg->message.header.channel_id = channel->id; + out_msg->message.header.type = type; + out_msg->message.header.length = 0; channel->next_outgoing_message_id++; channel->outgoing_message_count++; - return true; + NBN_Writer_Init(&out_msg->writer, out_msg->message.data, channel->max_message_len); + + LogDebug("Outgoing message %d (type: %d) added to channel %d", msg_id, type, channel->id); + + return &out_msg->writer; } static NBN_Message *Channel_GetNextRecvedMessage(NBN_Channel *channel) { @@ -1157,14 +1156,15 @@ static NBN_Message *Channel_GetNextRecvedMessage(NBN_Channel *channel) { } } -static bool Channel_GetNextOutgoingMessage(NBN_Channel *channel, NBN_Message **res_msg, double time) { +static bool Channel_GetNextOutgoingMessage(NBN_Channel *channel, NBN_Message *res_msg, double time) { if (channel->mode == NBN_CHANNEL_UNRELIABLE) { NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[channel->next_outgoing_message_slot]; if (out_msg->free) return false; - *res_msg = &out_msg->message; + *res_msg = out_msg->message; + res_msg->header.length = out_msg->writer.position; out_msg->free = true; channel->next_outgoing_message_slot++; @@ -1184,7 +1184,8 @@ static bool Channel_GetNextOutgoingMessage(NBN_Channel *channel, NBN_Message **r if (!out_msg->free && (out_msg->last_send_time < 0 || time - out_msg->last_send_time >= NBN_MESSAGE_RESEND_DELAY)) { - *res_msg = &out_msg->message; + *res_msg = out_msg->message; + res_msg->header.length = out_msg->writer.position; return true; } @@ -1352,8 +1353,9 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn double time) { LogDebug("Flushing all channels"); - NBN_Packet packet = {0}; - NBN_PacketEntry *packet_entry; + static NBN_Packet packet = {0}; + static NBN_PacketEntry *packet_entry; + unsigned int sent_packet_count = 0; unsigned int sent_bytes = 0; @@ -1364,17 +1366,17 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn LogDebug("Flushing channel %d (message count: %d)", channel->id, channel->outgoing_message_count); - NBN_Message *out_msg; + static NBN_Message out_msg; unsigned int j = 0; // TODO: use bandwidth to determine how many packets to send at most while (j < channel->outgoing_message_count && sent_packet_count < 16 && Channel_GetNextOutgoingMessage(channel, &out_msg, time)) { - uint8_t msg_type = out_msg->header.type; - uint16_t msg_id = out_msg->header.id; - uint16_t msg_len = out_msg->header.length; + uint8_t msg_type = out_msg.header.type; + uint16_t msg_id = out_msg.header.id; + uint16_t msg_len = out_msg.header.length; bool message_sent = false; - NBN_PacketResult ret = Packet_WriteMessage(&packet, out_msg); + NBN_PacketResult ret = Packet_WriteMessage(&packet, &out_msg); if (ret == NBN_PACKET_WRITE_OK) { message_sent = true; @@ -1390,7 +1392,7 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); - NBN_PacketResult ret = Packet_WriteMessage(&packet, out_msg); + NBN_PacketResult ret = Packet_WriteMessage(&packet, &out_msg); if (ret != NBN_PACKET_WRITE_OK) { LogError("Failed to send packet %d", packet.header.seq_number); @@ -1414,7 +1416,7 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn packet_entry->messages[packet_entry->messages_count++] = (NBN_MessageEntry){msg_id, channel->id}; - Channel_OnMessageSent(endpoint, channel, out_msg); + Channel_OnMessageSent(endpoint, channel, &out_msg); } j++; @@ -1716,9 +1718,8 @@ static void Endpoint_Deinit(NBN_Endpoint *); static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, NBN_Connection_ID, NBN_Driver_ID); static uint32_t Endpoint_BuildProtocolId(const char *); static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *, NBN_Packet *, NBN_Connection *); -static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN_Message *); static void Endpoint_UpdateTime(NBN_Endpoint *); -static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *, uint8_t, uint8_t); +static NBN_Writer *Endpoint_CreateOutgoingMessage(NBN_Endpoint *, NBN_Connection *, uint8_t, uint8_t); static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_server, NBN_Channel_Config *channels, unsigned int channel_count) { @@ -1729,12 +1730,6 @@ static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_ endpoint->channel_count = channel_count; endpoint->channels = malloc(sizeof(NBN_Channel_Config) * channel_count); - endpoint->write_messages = malloc(sizeof(NBN_Message) * channel_count); - - for (int i = 0; i < channel_count; i++) { - endpoint->write_messages[i].data = malloc(channels[i].max_message_len); - } - memcpy(endpoint->channels, channels, sizeof(NBN_Channel_Config) * channel_count); EventQueue_Init(&endpoint->event_queue); @@ -1748,11 +1743,6 @@ static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_ } static void Endpoint_Deinit(NBN_Endpoint *endpoint) { - for (int i = 0; i < endpoint->channel_count; i++) { - free(endpoint->write_messages[i].data); - } - - free(endpoint->write_messages); #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) PacketSimulator_Stop(&endpoint->packet_simulator); #endif @@ -1843,36 +1833,24 @@ static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *pa return 0; } -static void Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, uint8_t type, uint8_t channel_id) { +static NBN_Writer *Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connection *connection, uint8_t type, + uint8_t channel_id) { NBN_Assert(channel_id < endpoint->channel_count); - - NBN_Message *message = &endpoint->write_messages[channel_id]; - unsigned int max_message_len = endpoint->channels[channel_id].max_message_len; - - message->header = (NBN_MessageHeader){0, 0, type, channel_id}; - message->sender = NULL; - - NBN_Writer_Init(&endpoint->message_writer, message->data, max_message_len); - endpoint->current_write_message = message; -} - -static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Message *message) { - NBN_Assert(!connection->is_closed || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); + NBN_Assert(!connection->is_closed || type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); NBN_Assert(!connection->is_stale); - uint8_t channel_id = message->header.channel_id; - NBN_Channel *channel = &connection->channels[channel_id]; + LogDebug("Create outgoing message of type %d on channel %d", type, channel_id); - LogDebug("Enqueue message of type %d on channel %d", message->header.type, channel->id); + NBN_Channel *channel = &connection->channels[channel_id]; + NBN_Writer *writer = Channel_AddOutgoingMessage(channel, type); - if (!Channel_AddOutgoingMessage(channel, message)) { - LogError("Failed to enqueue outgoing message of type %d on channel %d", message->header.type, - message->header.channel_id); + if (!writer) { + LogError("Failed to enqueue outgoing message of type %d on channel %d", type, channel_id); - return NBN_ERROR; + return NULL; } - return 0; + return writer; } static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) { @@ -1928,6 +1906,20 @@ uint8_t NBN_GameClient_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_ return channel_id; } +unsigned int NBN_GameClient_GetChannelCurrentCapacity(uint8_t channel_id) { + NBN_Assert(channel_id < nbn_game_client.endpoint.channel_count); + + NBN_Channel *channel = &nbn_game_client.server_connection->channels[channel_id]; + + if (channel->mode == NBN_CHANNEL_UNRELIABLE) { + return 0; // TODO: + } else if (channel->mode == NBN_CHANNEL_RELIABLE) { + return 0; // TODO: + } + + NBN_Abort(); +} + NBN_Writer *NBN_GameClient_WriteConnectionRequestData(void) { NBN_Writer_Init(&nbn_game_client.client_data_writer, nbn_game_client.endpoint.connection_request_data_buffer, sizeof(nbn_game_client.endpoint.connection_request_data_buffer)); @@ -2003,6 +1995,10 @@ int NBN_GameClient_Start(void) { NBN_Writer *writer = NBN_GameClient_CreateReliableMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE); + if (!writer) { + return NBN_ERROR; + } + if (connection_data_len > 0) { NBN_Assert(connection_data_len <= sizeof(nbn_game_client.endpoint.connection_request_data_buffer)); @@ -2012,9 +2008,6 @@ int NBN_GameClient_Start(void) { NBN_Writer_WriteUInt32(writer, 0); } - if (NBN_GameClient_EnqueueMessage() < 0) - return NBN_ERROR; - LogInfo("Started"); return 0; @@ -2029,9 +2022,7 @@ void NBN_GameClient_Stop(void) { if (!nbn_game_client.server_connection->is_closed && !nbn_game_client.server_connection->is_stale) { LogInfo("Disconnecting..."); - NBN_GameClient_CreateReliableMessage(NBN_DISCONNECTION_MESSAGE_TYPE); - - if (NBN_GameClient_EnqueueMessage() < 0) { + if (!NBN_GameClient_CreateReliableMessage(NBN_DISCONNECTION_MESSAGE_TYPE)) { LogError("Failed to send disconnection message"); } @@ -2167,13 +2158,8 @@ int NBN_GameClient_Flush(void) { } NBN_Writer *NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id) { - NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - - NBN_Assert(channel_id < endpoint->channel_count); - - Endpoint_CreateOutgoingMessage(endpoint, type, channel_id); - - return &endpoint->message_writer; + return Endpoint_CreateOutgoingMessage(&nbn_game_client.endpoint, nbn_game_client.server_connection, type, + channel_id); } NBN_Writer *NBN_GameClient_CreateReliableMessage(uint8_t type) { @@ -2184,21 +2170,6 @@ NBN_Writer *NBN_GameClient_CreateUnreliableMessage(uint8_t type) { return NBN_GameClient_CreateMessage(type, nbn_game_client.endpoint.default_unreliable_channel); } -int NBN_GameClient_EnqueueMessage(void) { - NBN_Endpoint *endpoint = &nbn_game_client.endpoint; - NBN_Message *message = endpoint->current_write_message; - - message->header.length = endpoint->message_writer.position; - - if (Endpoint_EnqueueOutgoingMessage(endpoint, nbn_game_client.server_connection, message) < 0) { - LogError("Failed to create outgoing message"); - - return NBN_ERROR; - } - - return 0; -} - NBN_Reader *NBN_GameClient_ReadMessage(void) { NBN_Assert(nbn_game_client.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); @@ -2337,7 +2308,6 @@ static void ClientDriver_OnPacketReceived(NBN_Packet *packet) { * ====== SERVER ====== */ -static int GameServer_EnqueueMessageFor(NBN_Connection *client, NBN_Message *message); static void GameServer_AddClient(NBN_Connection *); static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection); static void GameServer_AddClientToClosedList(NBN_Connection *client); @@ -2612,56 +2582,33 @@ int NBN_GameServer_CloseClient(NBN_ConnectionHandle *conn) { return GameServer_CloseClientWithCode(HANDLE_TO_CONN(conn), -1, false); } -NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id) { - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; +NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id, NBN_ConnectionHandle *receiver) { + NBN_Connection *conn = HANDLE_TO_CONN(receiver); - NBN_Assert(channel_id < endpoint->channel_count); + NBN_Assert(conn->is_accepted || type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE || type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); - NBN_Writer *writer = &endpoint->message_writer; + NBN_Writer *writer = Endpoint_CreateOutgoingMessage(&nbn_game_server.endpoint, conn, type, channel_id); - Endpoint_CreateOutgoingMessage(endpoint, type, channel_id); + if (!writer) { + LogError("Failed to create outgoing message for client %lld", receiver->id); - return writer; -} + /* Do not close the client if we failed to send the close client message to avoid infinite loops */ + if (type != NBN_CLIENT_CLOSED_MESSAGE_TYPE) { + GameServer_CloseClientWithCode(conn, -1, false); -NBN_Writer *NBN_GameServer_CreateReliableMessage(uint8_t type) { - return NBN_GameServer_CreateMessage(type, nbn_game_server.endpoint.default_reliable_channel); -} + return NULL; + } + } -NBN_Writer *NBN_GameServer_CreateUnreliableMessage(uint8_t type) { - return NBN_GameServer_CreateMessage(type, nbn_game_server.endpoint.default_unreliable_channel); + return writer; } -int NBN_GameServer_EnqueueMessageFor(NBN_ConnectionHandle *conn) { - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - NBN_Message *message = endpoint->current_write_message; - - message->header.length = endpoint->message_writer.position; - - return GameServer_EnqueueMessageFor(HANDLE_TO_CONN(conn), message); +NBN_Writer *NBN_GameServer_CreateReliableMessage(uint8_t type, NBN_ConnectionHandle *receiver) { + return NBN_GameServer_CreateMessage(type, nbn_game_server.endpoint.default_reliable_channel, receiver); } -int NBN_GameServer_EnqueueBroadcastMessage(void) { - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - NBN_Message *message = endpoint->current_write_message; - message->header.length = endpoint->message_writer.position; - - int ret = 0; - - for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { - NBN_Connection *conn = nbn_game_server.clients[i].value; - - if (!conn->is_accepted || conn->is_closed) - continue; - - if (GameServer_EnqueueMessageFor(conn, message) < 0) { - LogError("Failed to send message to client %lld when broadcasting", conn->handle.id); - ret = NBN_ERROR; - break; - } - } - - return ret; +NBN_Writer *NBN_GameServer_CreateUnreliableMessage(uint8_t type, NBN_ConnectionHandle *receiver) { + return NBN_GameServer_CreateMessage(type, nbn_game_server.endpoint.default_unreliable_channel, receiver); } NBN_Reader *NBN_GameServer_ReadMessage(void) { @@ -2695,7 +2642,12 @@ int NBN_GameServer_AcceptIncomingConnection(void) { unsigned data_length = nbn_game_server.server_data_writer.position; NBN_Connection *client = nbn_game_server.last_event.data.connection; - NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); + NBN_Writer *writer = + NBN_GameServer_CreateReliableMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE, (NBN_ConnectionHandle *)client); + + if (!writer) { + return NBN_ERROR; + } if (data_length > 0) { NBN_Assert(data_length <= sizeof(nbn_game_server.endpoint.server_initial_data_buffer)); @@ -2706,9 +2658,6 @@ int NBN_GameServer_AcceptIncomingConnection(void) { NBN_Writer_WriteUInt32(writer, 0); } - if (NBN_GameServer_EnqueueMessageFor((NBN_ConnectionHandle *)client) < 0) - return NBN_ERROR; - client->is_accepted = true; LogInfo("Client %lld has been accepted into the server", client->handle.id); @@ -2760,28 +2709,6 @@ NBN_MessageInfo NBN_GameServer_GetMessageInfo(void) { NBN_GameServerStats NBN_GameServer_GetStats(void) { return nbn_game_server.stats; } -static int GameServer_EnqueueMessageFor(NBN_Connection *client, NBN_Message *message) { - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; - - /* Only NBN_CLIENT_ACCEPTED_MESSAGE_TYPE and NBN_CLIENT_CLOSED_MESSAGE_TYPE messages can be sent to an - * unaccapted client */ - NBN_Assert(client->is_accepted || message->header.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE || - message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); - - if (Endpoint_EnqueueOutgoingMessage(endpoint, client, message) < 0) { - LogError("Failed to create outgoing message for client %lld", client->handle.id); - - /* Do not close the client if we failed to send the close client message to avoid infinite loops */ - if (message->header.type != NBN_CLIENT_CLOSED_MESSAGE_TYPE) { - GameServer_CloseClientWithCode(client, -1, false); - - return NBN_ERROR; - } - } - - return 0; -} - static void GameServer_AddClient(NBN_Connection *client) { NBN_Assert(hmgeti(nbn_game_server.clients, client->handle.id) == -1); @@ -2819,9 +2746,14 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool if (!disconnection) { LogDebug("Send close message for client %lld (code: %d)", client->handle.id, code); - NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE); + NBN_Writer *writer = + NBN_GameServer_CreateReliableMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE, (NBN_ConnectionHandle *)client); + + if (!writer) { + return NBN_ERROR; + } + NBN_Writer_WriteInt32(writer, code); - NBN_GameServer_EnqueueMessageFor((NBN_ConnectionHandle *)client); } return 0; diff --git a/nbnet.h b/nbnet.h index def6881..964f6a3 100644 --- a/nbnet.h +++ b/nbnet.h @@ -150,14 +150,14 @@ typedef unsigned int NBN_Client_Iterator; typedef struct NBN_Writer { uint8_t *buffer; - unsigned int length; - unsigned int position; + uint16_t length; + uint16_t position; } NBN_Writer; typedef struct NBN_Reader { uint8_t *buffer; - unsigned int length; - unsigned int position; + uint16_t length; + uint16_t position; } NBN_Reader; typedef enum NBN_LogLevel { NBN_LOG_ERROR, NBN_LOG_INFO, NBN_LOG_WARNING, NBN_LOG_DEBUG } NBN_LogLevel; @@ -205,6 +205,9 @@ void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t p // TODO: doc uint8_t NBN_GameClient_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len); +// TODO: doc +unsigned int NBN_GameClient_GetChannelCurrentCapacity(uint8_t channel_id); + // TODO: doc NBN_Writer *NBN_GameClient_WriteConnectionRequestData(void); @@ -252,9 +255,6 @@ NBN_Writer *NBN_GameClient_CreateReliableMessage(uint8_t type); // TODO: doc NBN_Writer *NBN_GameClient_CreateUnreliableMessage(uint8_t type); -// TODO: doc -int NBN_GameClient_EnqueueMessage(void); - // TODO: doc NBN_Reader *NBN_GameClient_ReadMessage(void); @@ -362,18 +362,13 @@ int NBN_GameServer_CloseClient(NBN_ConnectionHandle *conn); int NBN_GameServer_CloseClientWithCode(NBN_ConnectionHandle *conn, int code); // TODO: doc -NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id); - -// TODO: doc -NBN_Writer *NBN_GameServer_CreateReliableMessage(uint8_t type); +NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id, NBN_ConnectionHandle *receiver); // TODO: doc -NBN_Writer *NBN_GameServer_CreateUnreliableMessage(uint8_t type); +NBN_Writer *NBN_GameServer_CreateReliableMessage(uint8_t type, NBN_ConnectionHandle *receiver); // TODO: doc -int NBN_GameServer_EnqueueMessageFor(NBN_ConnectionHandle *conn); -// TODO: doc -int NBN_GameServer_EnqueueBroadcastMessage(void); +NBN_Writer *NBN_GameServer_CreateUnreliableMessage(uint8_t type, NBN_ConnectionHandle *receiver); // TODO: doc NBN_Reader *NBN_GameServer_ReadMessage(void); diff --git a/soak/client.c b/soak/client.c index 5e2c652..2983106 100644 --- a/soak/client.c +++ b/soak/client.c @@ -116,10 +116,11 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { // TODO: support big messages NBN_Writer *writer = NBN_GameClient_CreateMessage(SOAK_MESSAGE_SMALL, channel->id); - SoakMessage_Write(writer, msg_id, entry->data, entry->length); - - if (NBN_GameClient_EnqueueMessage() < 0) + if (!writer) { return -1; + } + + SoakMessage_Write(writer, msg_id, entry->data, entry->length); channel->sent_message_count++; channel->last_sent_message_id = msg_id; diff --git a/soak/server.c b/soak/server.c index 64898af..09e1b2c 100644 --- a/soak/server.c +++ b/soak/server.c @@ -121,14 +121,10 @@ static void EchoReceivedSoakMessages(void) { while (channel->echo_queue.count > 0) { Soak_MessageEntry *msg_entry = &channel->echo_queue.messages[channel->echo_queue.head]; - NBN_Writer *writer = NBN_GameServer_CreateMessage(SOAK_MESSAGE_SMALL, channel->id); // TODO: support big + NBN_Writer *writer = + NBN_GameServer_CreateMessage(SOAK_MESSAGE_SMALL, channel->id, conn); // TODO: support big - SoakMessage_Write(writer, msg_entry->msg_id, msg_entry->data, msg_entry->length); - - log_info("Send soak message %d's echo (length: %d) to client %llu", msg_entry->msg_id, - msg_entry->length, conn->id); - - if (NBN_GameServer_EnqueueMessageFor(conn) < 0) { + if (!writer) { log_error("Failed to send soak message to client %llu, closing client", conn->id); if (NBN_GameServer_CloseClient(conn) < 0) { @@ -140,6 +136,11 @@ static void EchoReceivedSoakMessages(void) { return; } + SoakMessage_Write(writer, msg_entry->msg_id, msg_entry->data, msg_entry->length); + + log_info("Send soak message %d's echo (length: %d) to client %llu", msg_entry->msg_id, + msg_entry->length, conn->id); + msg_entry->length = 0; channel->echo_queue.head = (channel->echo_queue.head + 1) % SOAK_CLIENT_MAX_PENDING_MESSAGES; From 5e065fc1ed9656856e88e7a8f48f0cddccb2bce9 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Fri, 13 Mar 2026 14:07:45 +0100 Subject: [PATCH 77/85] improve channel capacity check --- nbnet.c | 34 +++++++++++++++++++++------------- nbnet.h | 3 +++ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/nbnet.c b/nbnet.c index f8e2e16..bc89dbc 100644 --- a/nbnet.c +++ b/nbnet.c @@ -209,6 +209,7 @@ struct NBN_Channel { unsigned int outgoing_message_count; unsigned int buffer_size; unsigned int max_message_len; + unsigned int current_capacity; NBN_OutgoingMessage *outgoing_messages_buffer; NBN_IncomingMessage *incoming_messages_buffer; bool *ack_buffer; // TODO: needed? @@ -1009,6 +1010,7 @@ static void Channel_Init(NBN_Channel *channel, uint8_t id, NBN_Channel_Config cf channel->most_recent_message_id = 0; channel->buffer_size = cfg.buffer_size; channel->max_message_len = cfg.max_message_len; + channel->current_capacity = cfg.buffer_size; channel->outgoing_messages_buffer = malloc(sizeof(NBN_OutgoingMessage) * cfg.buffer_size); channel->incoming_messages_buffer = malloc(sizeof(NBN_IncomingMessage) * cfg.buffer_size); channel->ack_buffer = malloc(sizeof(bool) * cfg.buffer_size); @@ -1093,8 +1095,8 @@ static NBN_Writer *Channel_AddOutgoingMessage(NBN_Channel *channel, uint8_t type NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[index]; // make sure the outgoing message is not already in use - if (!out_msg->free) { - LogError("No outgoing message available in channel %d (mode: %d, outgoing message count: %d, msg_id: %d, " + if (channel->current_capacity == 0) { + LogError("Channel %d outgoing buffer reached it's capacity (mode: %d, outgoing message count: %d, msg_id: %d, " "index: %d, oldest unacked msg: %d)", channel->id, channel->mode, channel->outgoing_message_count, msg_id, index, channel->oldest_unacked_message_id); @@ -1105,6 +1107,8 @@ static NBN_Writer *Channel_AddOutgoingMessage(NBN_Channel *channel, uint8_t type return NULL; } + NBN_Assert(out_msg->free); + out_msg->free = false; out_msg->last_send_time = -1; out_msg->message.header.id = msg_id; @@ -1114,6 +1118,7 @@ static NBN_Writer *Channel_AddOutgoingMessage(NBN_Channel *channel, uint8_t type channel->next_outgoing_message_id++; channel->outgoing_message_count++; + channel->current_capacity--; NBN_Writer_Init(&out_msg->writer, out_msg->message.data, channel->max_message_len); @@ -1199,12 +1204,11 @@ static bool Channel_GetNextOutgoingMessage(NBN_Channel *channel, NBN_Message *re } static int Channel_OnMessageSent(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) { - if (channel->mode != NBN_CHANNEL_UNRELIABLE) { - return 0; + if (channel->mode == NBN_CHANNEL_UNRELIABLE) { + channel->outgoing_message_count--; + channel->current_capacity++; } - channel->outgoing_message_count--; - return 0; } @@ -1235,6 +1239,8 @@ static int Channel_OnOutgoingMessageAcked(NBN_Endpoint *endpoint, NBN_Channel *c if (channel->ack_buffer[index]) { channel->ack_buffer[index] = false; channel->oldest_unacked_message_id++; + channel->current_capacity++; + NBN_Assert(channel->current_capacity <= channel->buffer_size); } else { break; } @@ -1911,13 +1917,7 @@ unsigned int NBN_GameClient_GetChannelCurrentCapacity(uint8_t channel_id) { NBN_Channel *channel = &nbn_game_client.server_connection->channels[channel_id]; - if (channel->mode == NBN_CHANNEL_UNRELIABLE) { - return 0; // TODO: - } else if (channel->mode == NBN_CHANNEL_RELIABLE) { - return 0; // TODO: - } - - NBN_Abort(); + return channel->current_capacity; } NBN_Writer *NBN_GameClient_WriteConnectionRequestData(void) { @@ -2343,6 +2343,14 @@ uint8_t NBN_GameServer_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_ return channel_id; } +unsigned int NBN_GameServer_GetChannelCurrentCapacity(uint8_t channel_id, NBN_ConnectionHandle *conn) { + NBN_Assert(channel_id < nbn_game_server.endpoint.channel_count); + + NBN_Channel *channel = &HANDLE_TO_CONN(conn)->channels[channel_id]; + + return channel->current_capacity; +} + static int StartServerDrivers(uint16_t port) { int driver_count = 0; diff --git a/nbnet.h b/nbnet.h index 964f6a3..e9b77bb 100644 --- a/nbnet.h +++ b/nbnet.h @@ -301,6 +301,9 @@ void NBN_GameServer_Init(const char *protocol_name, uint16_t port); // TODO: doc uint8_t NBN_GameServer_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len); +// TODO: doc +unsigned int NBN_GameServer_GetChannelCurrentCapacity(uint8_t channel_id, NBN_ConnectionHandle *conn); + /** * Start the game server with the provided configuration. * From f9ae635638f607abb78f009b6829a701db83a1e5 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Fri, 13 Mar 2026 14:26:28 +0100 Subject: [PATCH 78/85] take channel capacity into account in soak test --- soak/client.c | 12 ++++++++---- soak/server.c | 9 ++++++--- soak/soak.h | 2 ++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/soak/client.c b/soak/client.c index 2983106..529c009 100644 --- a/soak/client.c +++ b/soak/client.c @@ -72,16 +72,19 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { log_info("Compute number of soak messages to send (sent: %d, pending: %d, remaining: %d)", channel->sent_message_count, pending_message_count, remaining_message_count); + unsigned int capacity = NBN_GameClient_GetChannelCurrentCapacity(channel->id); + // make sure that we don't exceed channel capacity + unsigned int max_pending_messages = (unsigned int)fmin(SOAK_CLIENT_MAX_PENDING_MESSAGES, capacity); + // don't send anything on this tick if we have reached the max number of unacked messages - if (pending_message_count >= SOAK_CLIENT_MAX_PENDING_MESSAGES) { + if (pending_message_count >= max_pending_messages) { log_info("Max number of pending messages has been reached, not sending anything this tick"); return 0; } // number of messages to send on this tick - unsigned int send_message_count = - fmin(SOAK_CLIENT_MAX_PENDING_MESSAGES - pending_message_count, remaining_message_count); + unsigned int send_message_count = fmin(max_pending_messages - pending_message_count, remaining_message_count); log_info("Will send %d soak messages this tick", send_message_count); @@ -276,7 +279,8 @@ int main(int argc, char *argv[]) { NBN_GameClient_Init(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT); for (uint8_t c = 0; c < SOAK_CHANNEL_COUNT; c++) { - uint8_t channel_id = NBN_GameClient_CreateChannel(NBN_CHANNEL_RELIABLE, 128, 256); + uint8_t channel_id = + NBN_GameClient_CreateChannel(NBN_CHANNEL_RELIABLE, SOAK_CHANNEL_BUFFER_SIZE, SOAK_MAX_MESSAGE_SIZE); // channels 0 and 1 are the default nbnet channels assert(channel_id == 2 + c); diff --git a/soak/server.c b/soak/server.c index 09e1b2c..bb26afd 100644 --- a/soak/server.c +++ b/soak/server.c @@ -97,7 +97,7 @@ static void HandleClientDisconnection(NBN_DisconnectionInfo info) { assert(soak_client != NULL); - log_info("Client has disconnected (ID: %d)", info.conn_id); + log_info("Client has disconnected (ID: %lld)", info.conn_id); free(soak_client->channels); free(soak_client); @@ -118,8 +118,10 @@ static void EchoReceivedSoakMessages(void) { for (unsigned int c = 0; c < SOAK_CHANNEL_COUNT; c++) { SoakChannel *channel = &soak_client->channels[c]; + int send_count = NBN_GameServer_GetChannelCurrentCapacity(channel->id, conn); - while (channel->echo_queue.count > 0) { + // make sure that we don't exceed channel capacity + while (channel->echo_queue.count > 0 && --send_count >= 0) { Soak_MessageEntry *msg_entry = &channel->echo_queue.messages[channel->echo_queue.head]; NBN_Writer *writer = NBN_GameServer_CreateMessage(SOAK_MESSAGE_SMALL, channel->id, conn); // TODO: support big @@ -284,7 +286,8 @@ int main(int argc, char *argv[]) { NBN_GameServer_Init(SOAK_PROTOCOL_NAME, SOAK_PORT); for (uint8_t c = 0; c < SOAK_CHANNEL_COUNT; c++) { - uint8_t channel_id = NBN_GameServer_CreateChannel(NBN_CHANNEL_RELIABLE, 128, 256); + uint8_t channel_id = + NBN_GameServer_CreateChannel(NBN_CHANNEL_RELIABLE, SOAK_CHANNEL_BUFFER_SIZE, SOAK_MAX_MESSAGE_SIZE); // channels 0 and 1 are the default nbnet channels assert(channel_id == 2 + c); diff --git a/soak/soak.h b/soak/soak.h index a8b5261..e1651f0 100644 --- a/soak/soak.h +++ b/soak/soak.h @@ -54,7 +54,9 @@ #define SOAK_DONE 1 #define SOAK_CLIENT_MAX_PENDING_MESSAGES 50 // max number of unacked messages at a time #define SOAK_SERVER_FULL_CODE 1234 +#define SOAK_MAX_MESSAGE_SIZE 256 #define SOAK_CHANNEL_COUNT 4 +#define SOAK_CHANNEL_BUFFER_SIZE 128 typedef struct { unsigned int message_count; From 539952e20d82e0dddce54dd383c1c8d9a98833eb Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Fri, 13 Mar 2026 14:43:42 +0100 Subject: [PATCH 79/85] fix examples --- examples/echo/client.c | 7 ++++++- examples/echo/server.c | 8 ++++++-- examples/raylib/client.c | 18 ++++++------------ examples/raylib/server.c | 13 +++++++++---- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/examples/echo/client.c b/examples/echo/client.c index 79ef70d..f3401a8 100644 --- a/examples/echo/client.c +++ b/examples/echo/client.c @@ -77,12 +77,17 @@ void OnMessageReceived(void) { int SendEcho(const char *msg) { NBN_Writer *writer = NBN_GameClient_CreateReliableMessage(ECHO_MESSAGE_TYPE); + + if (!writer) { + return -1; + } + unsigned int length = strlen(msg); NBN_Writer_WriteUInt32(writer, length); NBN_Writer_WriteBytes(writer, (uint8_t *)msg, length); - return NBN_GameClient_EnqueueMessage(); + return 0; } int main(int argc, char *argv[]) { diff --git a/examples/echo/server.c b/examples/echo/server.c index b546858..9ddab01 100644 --- a/examples/echo/server.c +++ b/examples/echo/server.c @@ -57,12 +57,16 @@ static int EchoReceivedMessage(void) { msg_info.channel_id); // create and send an echo of the received message - NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(ECHO_MESSAGE_TYPE); + NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(ECHO_MESSAGE_TYPE, connection); + + if (!writer) { + return -1; + } NBN_Writer_WriteUInt32(writer, length); NBN_Writer_WriteBytes(writer, (uint8_t *)msg_str, length); - return NBN_GameServer_EnqueueMessageFor(connection); + return 0; } static bool error = false; diff --git a/examples/raylib/client.c b/examples/raylib/client.c index b3fa6e1..96f96f6 100644 --- a/examples/raylib/client.c +++ b/examples/raylib/client.c @@ -285,32 +285,26 @@ static void HandleGameClientEvent(int ev) { } static int SendStateUpdate(void) { - // Create a new UPDATE_STATE_MESSAGE unreliable message NBN_Writer *writer = NBN_GameClient_CreateUnreliableMessage(UPDATE_STATE_MESSAGE); - // Write the local client state to the message - UpdateClientStateMessage_Write(writer, local_client_state); - - // Send the message to the server - if (NBN_GameClient_EnqueueMessage() < 0) { + if (!writer) { return -1; } + UpdateClientStateMessage_Write(writer, local_client_state); + return 0; } static int SendColorUpdate(void) { - // Create a new CHANGE_COLOR_MESSAGE reliable message NBN_Writer *writer = NBN_GameClient_CreateReliableMessage(CHANGE_COLOR_MESSAGE); - // Write the new client color to the message - ChangeColorMessage_Write(writer, local_client_state.color); - - // Send the message to the server - if (NBN_GameClient_EnqueueMessage() < 0) { + if (!writer) { return -1; } + ChangeColorMessage_Write(writer, local_client_state.color); + return 0; } diff --git a/examples/raylib/server.c b/examples/raylib/server.c index 668eb52..d35c318 100644 --- a/examples/raylib/server.c +++ b/examples/raylib/server.c @@ -242,11 +242,16 @@ static int BroadcastGameState(void) { game_state.client_count = client_count; - // Create a unreliable message GAME_STATE_MESSAGE and write to it - NBN_Writer *writer = NBN_GameServer_CreateUnreliableMessage(GAME_STATE_MESSAGE); - GameStateMessage_Write(writer, &game_state); + NBN_Client_Iterator it = 0; + NBN_ConnectionHandle *cli; - return NBN_GameServer_EnqueueBroadcastMessage(); + // Broadcast GAME_STATE_MESSAGE to all clients + while ((cli = NBN_GameServer_GetNextClient(&it)) != NULL) { + NBN_Writer *writer = NBN_GameServer_CreateUnreliableMessage(GAME_STATE_MESSAGE, cli); + GameStateMessage_Write(writer, &game_state); + } + + return 0; } static bool running = true; From 104449422c3865b8bc36e26b493c3d98efd9f1a9 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Fri, 27 Mar 2026 12:55:06 +0100 Subject: [PATCH 80/85] remove incorrect asserts --- nbnet.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/nbnet.c b/nbnet.c index bc89dbc..f682661 100644 --- a/nbnet.c +++ b/nbnet.c @@ -2174,8 +2174,6 @@ NBN_Reader *NBN_GameClient_ReadMessage(void) { NBN_Assert(nbn_game_client.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); NBN_MessageInfo msg_info = nbn_game_client.last_event.data.message_info; - NBN_Assert(msg_info.length > 0 && msg_info.data != NULL); - NBN_Reader *reader = &nbn_game_client.endpoint.message_reader; NBN_Reader_Init(reader, msg_info.data, msg_info.length); @@ -2623,8 +2621,6 @@ NBN_Reader *NBN_GameServer_ReadMessage(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); NBN_MessageInfo msg_info = nbn_game_server.last_event.data.message_info; - NBN_Assert(msg_info.length > 0 && msg_info.data != NULL); - NBN_Reader *reader = &nbn_game_server.endpoint.message_reader; NBN_Reader_Init(reader, msg_info.data, msg_info.length); From 4208d3ca3539e26f089824af81d3c91b70848d02 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Mon, 6 Apr 2026 14:16:10 +0200 Subject: [PATCH 81/85] rename stuff and fix warnings --- nbnet.c | 473 +++++++++++++++++++++++++++----------------------- nbnet.h | 124 ++++++------- soak/client.c | 22 +-- soak/server.c | 34 ++-- soak/soak.c | 16 +- 5 files changed, 351 insertions(+), 318 deletions(-) diff --git a/nbnet.c b/nbnet.c index f682661..d70cd24 100644 --- a/nbnet.c +++ b/nbnet.c @@ -417,59 +417,59 @@ struct NBN_Endpoint { #endif }; -typedef struct NBN_GameServer_Config { +typedef struct NBN_Server_Config { const char *protocol_name; uint16_t port; NBN_Channel_Config channels[NBN_MAX_CHANNEL_COUNT]; unsigned int channel_count; -} NBN_GameServer_Config; +} NBN_Server_Config; -typedef struct NBN_GameServer { +typedef struct NBN_Server { NBN_Endpoint endpoint; - NBN_GameServer_Config config; + NBN_Server_Config config; struct { NBN_Connection_ID key; NBN_Connection *value; } *clients; NBN_ConnectionListNode *closed_clients_head; - NBN_GameServerStats stats; + NBN_ServerStats stats; NBN_Event last_event; NBN_Writer server_data_writer; NBN_Reader client_data_reader; -} NBN_GameServer; +} NBN_Server; -typedef struct NBN_GameClient_Config { +typedef struct NBN_Client_Config { const char *protocol_name; const char *host; uint16_t port; NBN_Channel_Config channels[NBN_MAX_CHANNEL_COUNT]; unsigned int channel_count; -} NBN_GameClient_Config; +} NBN_Client_Config; -typedef struct NBN_GameClient { +typedef struct NBN_Client { NBN_Endpoint endpoint; - NBN_GameClient_Config config; + NBN_Client_Config config; NBN_Connection *server_connection; bool is_connected; NBN_Event last_event; int closed_code; NBN_Writer client_data_writer; NBN_Reader server_data_reader; -} NBN_GameClient; +} NBN_Client; -static NBN_GameServer nbn_game_server; -static NBN_GameClient nbn_game_client; +static NBN_Server nbn_game_server; +static NBN_Client nbn_game_client; -typedef int (*NBN_Driver_Func_ClientStart)(NBN_GameClient *, const char *, uint16_t); -typedef void (*NBN_Driver_Func_ClientStop)(NBN_GameClient *); -typedef int (*NBN_Driver_Func_ClientSendPacket)(NBN_GameClient *, NBN_Packet *); -typedef int (*NBN_Driver_Func_ClientRecvPackets)(NBN_GameClient *); +typedef int (*NBN_Driver_Func_ClientStart)(NBN_Client *, const char *, uint16_t); +typedef void (*NBN_Driver_Func_ClientStop)(NBN_Client *); +typedef int (*NBN_Driver_Func_ClientSendPacket)(NBN_Client *, NBN_Packet *); +typedef int (*NBN_Driver_Func_ClientRecvPackets)(NBN_Client *); -typedef int (*NBN_Driver_Func_ServerStart)(NBN_GameServer *, uint16_t); -typedef void (*NBN_Driver_Func_ServerStop)(NBN_GameServer *); -typedef int (*NBN_Driver_Func_ServerSendPacketTo)(NBN_GameServer *, NBN_Packet *, NBN_Connection *); -typedef void (*NBN_Driver_Func_ServerCleanupConnection)(NBN_GameServer *, NBN_Connection *); -typedef int (*NBN_Driver_Func_ServerRecvPackets)(NBN_GameServer *); +typedef int (*NBN_Driver_Func_ServerStart)(NBN_Server *, uint16_t); +typedef void (*NBN_Driver_Func_ServerStop)(NBN_Server *); +typedef int (*NBN_Driver_Func_ServerSendPacketTo)(NBN_Server *, NBN_Packet *, NBN_Connection *); +typedef void (*NBN_Driver_Func_ServerCleanupConnection)(NBN_Server *, NBN_Connection *); +typedef int (*NBN_Driver_Func_ServerRecvPackets)(NBN_Server *); typedef struct NBN_Driver_Implementation { /* Client functions */ @@ -522,16 +522,16 @@ typedef struct in_addr IN_ADDR; #endif // NBN_PLATFORM_WINDOWS -static int UDP_Client_Start(NBN_GameClient *client, const char *host, uint16_t port); -static void UDP_Client_Stop(NBN_GameClient *client); -static int UDP_Client_RecvPackets(NBN_GameClient *client); -static int UDP_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet); +static int UDP_Client_Start(NBN_Client *client, const char *host, uint16_t port); +static void UDP_Client_Stop(NBN_Client *client); +static int UDP_Client_RecvPackets(NBN_Client *client); +static int UDP_Client_SendPacket(NBN_Client *client, NBN_Packet *packet); -static int UDP_Server_Start(NBN_GameServer *server, uint16_t port); -static void UDP_Server_Stop(NBN_GameServer *server); -static int UDP_Server_RecvPackets(NBN_GameServer *server); -static int UDP_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *connection); -static void UDP_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *connection); +static int UDP_Server_Start(NBN_Server *server, uint16_t port); +static void UDP_Server_Stop(NBN_Server *server); +static int UDP_Server_RecvPackets(NBN_Server *server); +static int UDP_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_Connection *connection); +static void UDP_Server_CleanupConnection(NBN_Server *server, NBN_Connection *connection); static NBN_Driver nbn_udp_driver = {.id = NBN_DRIVER_UDP, .name = "UDP", @@ -566,16 +566,16 @@ static SOCKET nbn_udp_sock; #include -static int WebRTC_Client_Start(NBN_GameClient *client, const char *host, uint16_t port); -static void WebRTC_Client_Stop(NBN_GameClient *client); -static int WebRTC_Client_RecvPackets(NBN_GameClient *client); -static int WebRTC_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet); +static int WebRTC_Client_Start(NBN_Client *client, const char *host, uint16_t port); +static void WebRTC_Client_Stop(NBN_Client *client); +static int WebRTC_Client_RecvPackets(NBN_Client *client); +static int WebRTC_Client_SendPacket(NBN_Client *client, NBN_Packet *packet); -static int WebRTC_Server_Start(NBN_GameServer *server, uint16_t port); -static void WebRTC_Server_Stop(NBN_GameServer *server); -static int WebRTC_Server_RecvPackets(NBN_GameServer *server); -static int WebRTC_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *connection); -static void WebRTC_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *connection); +static int WebRTC_Server_Start(NBN_Server *server, uint16_t port); +static void WebRTC_Server_Stop(NBN_Server *server); +static int WebRTC_Server_RecvPackets(NBN_Server *server); +static int WebRTC_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_Connection *connection); +static void WebRTC_Server_CleanupConnection(NBN_Server *server, NBN_Connection *connection); static NBN_Driver nbn_webrtc_em_driver = {.id = NBN_DRIVER_WEBRTC_EMSCRIPTEN, .name = "WebRTC_EMSCRIPTEN", @@ -598,16 +598,16 @@ static NBN_WebRTC_Config nbn_wrtc_cfg = NBN_WEBRTC_DEFAULT_CONFIG; #ifdef NBN_WEBRTC_NATIVE -static int WebRTC_Native_Client_Start(NBN_GameClient *client, const char *host, uint16_t port); -static void WebRTC_Native_Client_Stop(NBN_GameClient *client); -static int WebRTC_Native_Client_RecvPackets(NBN_GameClient *client); -static int WebRTC_Native_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet); +static int WebRTC_Native_Client_Start(NBN_Client *client, const char *host, uint16_t port); +static void WebRTC_Native_Client_Stop(NBN_Client *client); +static int WebRTC_Native_Client_RecvPackets(NBN_Client *client); +static int WebRTC_Native_Client_SendPacket(NBN_Client *client, NBN_Packet *packet); -static int WebRTC_Native_Server_Start(NBN_GameServer *server, uint16_t port); -static void WebRTC_Native_Server_Stop(NBN_GameServer *server); -static int WebRTC_Native_Server_RecvPackets(NBN_GameServer *server); -static int WebRTC_Native_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *connection); -static void WebRTC_Native_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *connection); +static int WebRTC_Native_Server_Start(NBN_Server *server, uint16_t port); +static void WebRTC_Native_Server_Stop(NBN_Server *server); +static int WebRTC_Native_Server_RecvPackets(NBN_Server *server); +static int WebRTC_Native_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_Connection *connection); +static void WebRTC_Native_Server_CleanupConnection(NBN_Server *server, NBN_Connection *connection); static NBN_Driver nbn_webrtc_native_driver = { .id = NBN_DRIVER_WEBRTC_NATIVE, @@ -1203,7 +1203,7 @@ static bool Channel_GetNextOutgoingMessage(NBN_Channel *channel, NBN_Message *re } } -static int Channel_OnMessageSent(NBN_Endpoint *endpoint, NBN_Channel *channel, NBN_Message *message) { +static int Channel_OnMessageSent(NBN_Channel *channel) { if (channel->mode == NBN_CHANNEL_UNRELIABLE) { channel->outgoing_message_count--; channel->current_capacity++; @@ -1212,7 +1212,7 @@ static int Channel_OnMessageSent(NBN_Endpoint *endpoint, NBN_Channel *channel, N return 0; } -static int Channel_OnOutgoingMessageAcked(NBN_Endpoint *endpoint, NBN_Channel *channel, uint16_t msg_id) { +static int Channel_OnOutgoingMessageAcked(NBN_Channel *channel, uint16_t msg_id) { if (channel->mode != NBN_CHANNEL_RELIABLE) { return 0; } @@ -1232,7 +1232,7 @@ static int Channel_OnOutgoingMessageAcked(NBN_Endpoint *endpoint, NBN_Channel *c channel->outgoing_message_count--; if (msg_id == channel->oldest_unacked_message_id) { - for (int i = 0; i < channel->buffer_size; i++) { + for (unsigned int i = 0; i < channel->buffer_size; i++) { uint16_t ack_msg_id = msg_id + i; int index = ack_msg_id % channel->buffer_size; @@ -1262,15 +1262,15 @@ static int Channel_OnOutgoingMessageAcked(NBN_Endpoint *endpoint, NBN_Channel *c static void Connection_Destroy(NBN_Connection *); static uint32_t Connection_BuildPacketAckBits(NBN_Connection *); -static int Connection_DecodePacketHeader(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); -static int Connection_AckPacket(NBN_Endpoint *, NBN_Connection *, uint16_t, double time); +static int Connection_DecodePacketHeader(NBN_Connection *, NBN_Packet *, double); +static int Connection_AckPacket(NBN_Connection *, uint16_t, double time); static void Connection_InitOutgoingPacket(NBN_Connection *, uint32_t, NBN_Packet *, NBN_PacketEntry **); static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *, uint16_t); static bool Connection_InsertReceivedPacketEntry(NBN_Connection *, uint16_t); static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *, uint16_t); static bool Connection_IsPacketReceived(NBN_Connection *, uint16_t); static int Connection_SendPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry *, double, bool); -static int Connection_ReadNextMessageHeader(NBN_Endpoint *, NBN_Reader *, NBN_MessageHeader *); +static int Connection_ReadNextMessageHeader(NBN_Reader *, NBN_MessageHeader *); static void Connection_UpdateAveragePing(NBN_Connection *, double); static void Connection_UpdateAveragePacketLoss(NBN_Connection *, uint16_t); static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *, float); @@ -1281,7 +1281,7 @@ static bool Connection_CheckIfStale(NBN_Connection *, double); static int Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, double time) { - if (Connection_DecodePacketHeader(endpoint, connection, packet, time) < 0) { + if (Connection_DecodePacketHeader(connection, packet, time) < 0) { LogError("Failed to decode packet %d header", packet->header.seq_number); return NBN_ERROR; @@ -1306,7 +1306,7 @@ static int Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connecti LogDebug("Reading message number %d from packet %d", i, packet->header.seq_number); static NBN_MessageHeader header = {0}; - int msg_len = Connection_ReadNextMessageHeader(endpoint, &msg_reader, &header); + int msg_len = Connection_ReadNextMessageHeader(&msg_reader, &header); if (msg_len < 0) { LogError("Failed to read packet: invalid message header"); @@ -1324,7 +1324,7 @@ static int Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connecti NBN_Channel *channel = &connection->channels[channel_id]; - if (msg_len > channel->max_message_len) { + if ((unsigned int)msg_len > channel->max_message_len) { LogError("Failed to read packet: message %d too large for channel %d (%d > %d)", header.id, channel_id, header.length, channel->max_message_len); @@ -1422,7 +1422,7 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn packet_entry->messages[packet_entry->messages_count++] = (NBN_MessageEntry){msg_id, channel->id}; - Channel_OnMessageSent(endpoint, channel, &out_msg); + Channel_OnMessageSent(channel); } j++; @@ -1458,9 +1458,8 @@ static bool Connection_CheckIfStale(NBN_Connection *connection, double time) { #endif } -static int Connection_DecodePacketHeader(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, - double time) { - if (Connection_AckPacket(endpoint, connection, packet->header.ack, time) < 0) { +static int Connection_DecodePacketHeader(NBN_Connection *connection, NBN_Packet *packet, double time) { + if (Connection_AckPacket(connection, packet->header.ack, time) < 0) { LogError("Failed to ack packet %d", packet->header.seq_number); return NBN_ERROR; @@ -1470,7 +1469,7 @@ static int Connection_DecodePacketHeader(NBN_Endpoint *endpoint, NBN_Connection if (B_IS_UNSET(packet->header.ack_bits, i)) continue; - if (Connection_AckPacket(endpoint, connection, packet->header.ack - (i + 1), time) < 0) { + if (Connection_AckPacket(connection, packet->header.ack - (i + 1), time) < 0) { LogError("Failed to ack packet %d", packet->header.seq_number); return NBN_ERROR; @@ -1481,10 +1480,10 @@ static int Connection_DecodePacketHeader(NBN_Endpoint *endpoint, NBN_Connection } static void Connection_Destroy(NBN_Connection *connection) { - for (int i = 0; i < connection->channel_count; i++) { + for (unsigned int i = 0; i < connection->channel_count; i++) { NBN_Channel *channel = &connection->channels[i]; - for (int j = 0; j < channel->buffer_size; j++) { + for (unsigned int j = 0; j < channel->buffer_size; j++) { free(channel->incoming_messages_buffer[j].message.data); free(channel->outgoing_messages_buffer[j].message.data); } @@ -1516,8 +1515,7 @@ static uint32_t Connection_BuildPacketAckBits(NBN_Connection *connection) { return ack_bits; } -static int Connection_AckPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, uint16_t ack_packet_seq_number, - double time) { +static int Connection_AckPacket(NBN_Connection *connection, uint16_t ack_packet_seq_number, double time) { NBN_PacketEntry *packet_entry = Connection_FindSendPacketEntry(connection, ack_packet_seq_number); if (packet_entry && !packet_entry->acked) { @@ -1533,7 +1531,7 @@ static int Connection_AckPacket(NBN_Endpoint *endpoint, NBN_Connection *connecti NBN_Assert(channel != NULL); - if (Channel_OnOutgoingMessageAcked(endpoint, channel, msg_entry->id) < 0) { + if (Channel_OnOutgoingMessageAcked(channel, msg_entry->id) < 0) { return NBN_ERROR; } } @@ -1634,7 +1632,7 @@ static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, } } -static int Connection_ReadNextMessageHeader(NBN_Endpoint *endpoint, NBN_Reader *reader, NBN_MessageHeader *header) { +static int Connection_ReadNextMessageHeader(NBN_Reader *reader, NBN_MessageHeader *header) { if (NBN_Reader_ReadUInt16(reader, &header->id) < 0) { LogError("Failed to read message id"); @@ -1781,7 +1779,7 @@ static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, NBN_Con connection->channels = malloc(sizeof(NBN_Channel) * endpoint->channel_count); connection->channel_count = endpoint->channel_count; - for (int i = 0; i < endpoint->channel_count; i++) { + for (unsigned int i = 0; i < endpoint->channel_count; i++) { Channel_Init(&connection->channels[i], i, endpoint->channels[i]); } @@ -1883,23 +1881,23 @@ static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) { * ====== CLIENT ====== */ -static int GameClient_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); -static NBN_Client_Event GameClient_HandleEvent(void); -static NBN_Client_Event GameClient_HandleMessageReceivedEvent(void); +static int Client_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); +static NBN_Client_Event Client_HandleEvent(void); +static NBN_Client_Event Client_HandleMessageReceivedEvent(void); static NBN_Connection *CreateServerConnection(NBN_Driver_ID driver_id); -void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port) { - nbn_game_client.config = (NBN_GameClient_Config){.protocol_name = protocol_name, .host = host, .port = port}; +void NBN_Client_Init(const char *protocol_name, const char *host, uint16_t port) { + nbn_game_client.config = (NBN_Client_Config){.protocol_name = protocol_name, .host = host, .port = port}; nbn_game_client.client_data_writer.position = 0; - nbn_game_client.endpoint.default_reliable_channel = NBN_GameClient_CreateChannel( + nbn_game_client.endpoint.default_reliable_channel = NBN_Client_CreateChannel( NBN_CHANNEL_RELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); - nbn_game_client.endpoint.default_unreliable_channel = NBN_GameClient_CreateChannel( + nbn_game_client.endpoint.default_unreliable_channel = NBN_Client_CreateChannel( NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); } -uint8_t NBN_GameClient_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len) { - NBN_GameClient_Config *cfg = &nbn_game_client.config; +uint8_t NBN_Client_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len) { + NBN_Client_Config *cfg = &nbn_game_client.config; NBN_Assert(cfg->channel_count < NBN_MAX_CHANNEL_COUNT); @@ -1912,7 +1910,7 @@ uint8_t NBN_GameClient_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_ return channel_id; } -unsigned int NBN_GameClient_GetChannelCurrentCapacity(uint8_t channel_id) { +unsigned int NBN_Client_GetChannelCurrentCapacity(uint8_t channel_id) { NBN_Assert(channel_id < nbn_game_client.endpoint.channel_count); NBN_Channel *channel = &nbn_game_client.server_connection->channels[channel_id]; @@ -1920,7 +1918,7 @@ unsigned int NBN_GameClient_GetChannelCurrentCapacity(uint8_t channel_id) { return channel->current_capacity; } -NBN_Writer *NBN_GameClient_WriteConnectionRequestData(void) { +NBN_Writer *NBN_Client_WriteConnectionRequestData(void) { NBN_Writer_Init(&nbn_game_client.client_data_writer, nbn_game_client.endpoint.connection_request_data_buffer, sizeof(nbn_game_client.endpoint.connection_request_data_buffer)); @@ -1969,8 +1967,8 @@ static int StartClientDrivers(const char *host, uint16_t port) { return driver_count; } -int NBN_GameClient_Start(void) { - NBN_GameClient_Config config = nbn_game_client.config; +int NBN_Client_Start(void) { + NBN_Client_Config config = nbn_game_client.config; const char *protocol_name = config.protocol_name; const char *host = config.host; uint16_t port = config.port; @@ -1993,7 +1991,7 @@ int NBN_GameClient_Start(void) { unsigned int connection_data_len = nbn_game_client.client_data_writer.position; - NBN_Writer *writer = NBN_GameClient_CreateReliableMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE); + NBN_Writer *writer = NBN_Client_CreateReliableMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE); if (!writer) { return NBN_ERROR; @@ -2013,20 +2011,20 @@ int NBN_GameClient_Start(void) { return 0; } -void NBN_GameClient_Stop(void) { +void NBN_Client_Stop(void) { // Poll remaining events to clear the event queue - while (NBN_GameClient_Poll() != NBN_CLIENT_NO_EVENT) { + while (NBN_Client_Poll() != NBN_CLIENT_NO_EVENT) { } if (nbn_game_client.server_connection) { if (!nbn_game_client.server_connection->is_closed && !nbn_game_client.server_connection->is_stale) { LogInfo("Disconnecting..."); - if (!NBN_GameClient_CreateReliableMessage(NBN_DISCONNECTION_MESSAGE_TYPE)) { + if (!NBN_Client_CreateReliableMessage(NBN_DISCONNECTION_MESSAGE_TYPE)) { LogError("Failed to send disconnection message"); } - if (NBN_GameClient_Flush() < 0) { + if (NBN_Client_Flush() < 0) { LogError("Failed to send packets"); } @@ -2062,7 +2060,7 @@ void NBN_GameClient_Stop(void) { LogInfo("Stopped"); } -NBN_Reader *NBN_GameClient_ReadServerData(void) { +NBN_Reader *NBN_Client_ReadServerData(void) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; NBN_Reader_Init(&nbn_game_client.server_data_reader, endpoint->server_initial_data_buffer, @@ -2096,7 +2094,7 @@ static int ReadPacketsFromClientDrivers(void) { return 0; } -NBN_Client_Event NBN_GameClient_Poll(void) { +NBN_Client_Event NBN_Client_Poll(void) { NBN_Endpoint *endpoint = &nbn_game_client.endpoint; Endpoint_UpdateTime(endpoint); @@ -2133,7 +2131,7 @@ NBN_Client_Event NBN_GameClient_Poll(void) { LogDebug("Got message %d of type %d from channel %d", msg->header.id, msg->header.type, channel->id); - if (GameClient_ProcessReceivedMessage(msg, server_conn) < 0) { + if (Client_ProcessReceivedMessage(msg, server_conn) < 0) { LogError("Failed to process received message"); return NBN_ERROR; @@ -2149,28 +2147,28 @@ NBN_Client_Event NBN_GameClient_Poll(void) { bool ret = EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_client.last_event); - return ret ? GameClient_HandleEvent() : NBN_CLIENT_NO_EVENT; + return ret ? Client_HandleEvent() : NBN_CLIENT_NO_EVENT; } -int NBN_GameClient_Flush(void) { +int NBN_Client_Flush(void) { return Connection_FlushChannels(&nbn_game_client.endpoint, nbn_game_client.server_connection, nbn_game_client.endpoint.protocol_id, nbn_game_client.endpoint.time); } -NBN_Writer *NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id) { +NBN_Writer *NBN_Client_CreateMessage(uint8_t type, uint8_t channel_id) { return Endpoint_CreateOutgoingMessage(&nbn_game_client.endpoint, nbn_game_client.server_connection, type, channel_id); } -NBN_Writer *NBN_GameClient_CreateReliableMessage(uint8_t type) { - return NBN_GameClient_CreateMessage(type, nbn_game_client.endpoint.default_reliable_channel); +NBN_Writer *NBN_Client_CreateReliableMessage(uint8_t type) { + return NBN_Client_CreateMessage(type, nbn_game_client.endpoint.default_reliable_channel); } -NBN_Writer *NBN_GameClient_CreateUnreliableMessage(uint8_t type) { - return NBN_GameClient_CreateMessage(type, nbn_game_client.endpoint.default_unreliable_channel); +NBN_Writer *NBN_Client_CreateUnreliableMessage(uint8_t type) { + return NBN_Client_CreateMessage(type, nbn_game_client.endpoint.default_unreliable_channel); } -NBN_Reader *NBN_GameClient_ReadMessage(void) { +NBN_Reader *NBN_Client_ReadMessage(void) { NBN_Assert(nbn_game_client.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); NBN_MessageInfo msg_info = nbn_game_client.last_event.data.message_info; @@ -2189,19 +2187,19 @@ static NBN_Connection *CreateServerConnection(NBN_Driver_ID driver_id) { return server_connection; } -NBN_MessageInfo NBN_GameClient_GetMessageInfo(void) { +NBN_MessageInfo NBN_Client_GetMessageInfo(void) { NBN_Assert(nbn_game_client.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); return nbn_game_client.last_event.data.message_info; } -NBN_ConnectionStats NBN_GameClient_GetStats(void) { return nbn_game_client.server_connection->stats; } +NBN_ConnectionStats NBN_Client_GetStats(void) { return nbn_game_client.server_connection->stats; } -int NBN_GameClient_GetServerCloseCode(void) { return nbn_game_client.closed_code; } +int NBN_Client_GetServerCloseCode(void) { return nbn_game_client.closed_code; } -bool NBN_GameClient_IsConnected(void) { return nbn_game_client.is_connected; } +bool NBN_Client_IsConnected(void) { return nbn_game_client.is_connected; } -static int GameClient_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *server_connection) { +static int Client_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *server_connection) { NBN_Assert(nbn_game_client.server_connection == server_connection); NBN_Event ev; @@ -2224,17 +2222,17 @@ static int GameClient_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio return 0; } -static NBN_Client_Event GameClient_HandleEvent(void) { +static NBN_Client_Event Client_HandleEvent(void) { switch (nbn_game_client.last_event.type) { case NBN_CLIENT_MESSAGE_RECEIVED: - return GameClient_HandleMessageReceivedEvent(); + return Client_HandleMessageReceivedEvent(); default: return nbn_game_client.last_event.type; } } -static NBN_Client_Event GameClient_HandleMessageReceivedEvent(void) { +static NBN_Client_Event Client_HandleMessageReceivedEvent(void) { NBN_MessageInfo message_info = nbn_game_client.last_event.data.message_info; NBN_Endpoint *endpoint = &nbn_game_client.endpoint; @@ -2242,7 +2240,7 @@ static NBN_Client_Event GameClient_HandleMessageReceivedEvent(void) { if (message_info.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE) { nbn_game_client.is_connected = false; - NBN_Reader *reader = NBN_GameClient_ReadMessage(); + NBN_Reader *reader = NBN_Client_ReadMessage(); if (NBN_Reader_ReadInt32(reader, &nbn_game_client.closed_code) < 0) { LogError("Failed to read code from client closed message"); @@ -2258,7 +2256,7 @@ static NBN_Client_Event GameClient_HandleMessageReceivedEvent(void) { return NBN_ERROR; } - NBN_Reader *reader = NBN_GameClient_ReadMessage(); + NBN_Reader *reader = NBN_Client_ReadMessage(); unsigned int data_length; if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) { @@ -2306,29 +2304,29 @@ static void ClientDriver_OnPacketReceived(NBN_Packet *packet) { * ====== SERVER ====== */ -static void GameServer_AddClient(NBN_Connection *); -static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection); -static void GameServer_AddClientToClosedList(NBN_Connection *client); -static int GameServer_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); -static int GameServer_CloseStaleClientConnections(void); -static void GameServer_RemoveClosedClientConnections(void); -static bool GameServer_HandleEvent(NBN_Server_Event *ev); -static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev); +static void Server_AddClient(NBN_Connection *); +static int Server_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection); +static void Server_AddClientToClosedList(NBN_Connection *client); +static int Server_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); +static int Server_CloseStaleClientConnections(void); +static void Server_RemoveClosedClientConnections(void); +static bool Server_HandleEvent(NBN_Server_Event *ev); +static bool Server_HandleMessageReceivedEvent(NBN_Server_Event *ev); -void NBN_GameServer_Init(const char *protocol_name, uint16_t port) { - nbn_game_server.config = (NBN_GameServer_Config){.protocol_name = protocol_name, .port = port}; +void NBN_Server_Init(const char *protocol_name, uint16_t port) { + nbn_game_server.config = (NBN_Server_Config){.protocol_name = protocol_name, .port = port}; nbn_game_server.server_data_writer.position = 0; hmdefault(nbn_game_server.clients, NULL); - nbn_game_server.endpoint.default_reliable_channel = NBN_GameServer_CreateChannel( + nbn_game_server.endpoint.default_reliable_channel = NBN_Server_CreateChannel( NBN_CHANNEL_RELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); - nbn_game_server.endpoint.default_unreliable_channel = NBN_GameServer_CreateChannel( + nbn_game_server.endpoint.default_unreliable_channel = NBN_Server_CreateChannel( NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); } -uint8_t NBN_GameServer_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len) { - NBN_GameServer_Config *cfg = &nbn_game_server.config; +uint8_t NBN_Server_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len) { + NBN_Server_Config *cfg = &nbn_game_server.config; NBN_Assert(cfg->channel_count < NBN_MAX_CHANNEL_COUNT); @@ -2341,7 +2339,7 @@ uint8_t NBN_GameServer_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_ return channel_id; } -unsigned int NBN_GameServer_GetChannelCurrentCapacity(uint8_t channel_id, NBN_ConnectionHandle *conn) { +unsigned int NBN_Server_GetChannelCurrentCapacity(uint8_t channel_id, NBN_ConnectionHandle *conn) { NBN_Assert(channel_id < nbn_game_server.endpoint.channel_count); NBN_Channel *channel = &HANDLE_TO_CONN(conn)->channels[channel_id]; @@ -2385,8 +2383,8 @@ static int StartServerDrivers(uint16_t port) { return driver_count; } -int NBN_GameServer_Start(void) { - NBN_GameServer_Config config = nbn_game_server.config; +int NBN_Server_Start(void) { + NBN_Server_Config config = nbn_game_server.config; const char *protocol_name = config.protocol_name; uint16_t port = config.port; uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); @@ -2407,9 +2405,9 @@ int NBN_GameServer_Start(void) { return 0; } -void NBN_GameServer_Stop(void) { +void NBN_Server_Stop(void) { // Poll remaning events to clear the event queue - while (NBN_GameServer_Poll() != NBN_SERVER_NO_EVENT) { + while (NBN_Server_Poll() != NBN_SERVER_NO_EVENT) { } for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { @@ -2457,13 +2455,13 @@ static NBN_Connection_ID NBN_BuildConnectionHash(NBN_Connection_ID id, NBN_Drive return ((NBN_Connection_ID)driver_byte << 56) | id; } -NBN_ConnectionHandle *NBN_GameServer_GetConnection(NBN_Connection_ID id) { +NBN_ConnectionHandle *NBN_Server_GetConnection(NBN_Connection_ID id) { return (NBN_ConnectionHandle *)hmget(nbn_game_server.clients, id); } -unsigned int NBN_GameServer_GetClientCount(void) { return hmlen(nbn_game_server.clients); } +unsigned int NBN_Server_GetClientCount(void) { return hmlen(nbn_game_server.clients); } -NBN_ConnectionHandle *NBN_GameServer_GetNextClient(NBN_Client_Iterator *it) { +NBN_ConnectionHandle *NBN_Server_GetNextClient(NBN_Client_Iterator *it) { for (; *it < hmlen(nbn_game_server.clients);) { NBN_Connection *conn = (NBN_Connection *)nbn_game_server.clients[*it].value; @@ -2497,13 +2495,13 @@ static void ReadPacketsFromServerDrivers(void) { #endif // __EMSCRIPTEN__ } -NBN_Server_Event NBN_GameServer_Poll(void) { +NBN_Server_Event NBN_Server_Poll(void) { Endpoint_UpdateTime(&nbn_game_server.endpoint); NBN_Endpoint *endpoint = &nbn_game_server.endpoint; if (EventQueue_IsEmpty(&endpoint->event_queue)) { - if (GameServer_CloseStaleClientConnections() < 0) + if (Server_CloseStaleClientConnections() < 0) return NBN_ERROR; ReadPacketsFromServerDrivers(); @@ -2520,7 +2518,7 @@ NBN_Server_Event NBN_GameServer_Poll(void) { NBN_Message *msg; while ((msg = Channel_GetNextRecvedMessage(channel)) != NULL) { - if (GameServer_ProcessReceivedMessage(msg, client) < 0) { + if (Server_ProcessReceivedMessage(msg, client) < 0) { LogError("Failed to process received message"); return NBN_ERROR; @@ -2536,13 +2534,13 @@ NBN_Server_Event NBN_GameServer_Poll(void) { client->last_read_packets_time = endpoint->time; } - GameServer_RemoveClosedClientConnections(); + Server_RemoveClosedClientConnections(); } NBN_Server_Event ev; while (EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_server.last_event)) { - if (GameServer_HandleEvent(&ev)) { + if (Server_HandleEvent(&ev)) { return ev; } } @@ -2550,10 +2548,10 @@ NBN_Server_Event NBN_GameServer_Poll(void) { return NBN_SERVER_NO_EVENT; } -int NBN_GameServer_Flush(void) { +int NBN_Server_Flush(void) { nbn_game_server.stats.upload_bandwidth = 0; - GameServer_RemoveClosedClientConnections(); + Server_RemoveClosedClientConnections(); for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { NBN_Connection *client = nbn_game_server.clients[i].value; @@ -2580,15 +2578,15 @@ static NBN_Connection *CreateClientConnection(NBN_Driver_ID driver_id, NBN_Conne return client; } -int NBN_GameServer_CloseClientWithCode(NBN_ConnectionHandle *conn, int code) { - return GameServer_CloseClientWithCode(HANDLE_TO_CONN(conn), code, false); +int NBN_Server_CloseClientWithCode(NBN_ConnectionHandle *conn, int code) { + return Server_CloseClientWithCode(HANDLE_TO_CONN(conn), code, false); } -int NBN_GameServer_CloseClient(NBN_ConnectionHandle *conn) { - return GameServer_CloseClientWithCode(HANDLE_TO_CONN(conn), -1, false); +int NBN_Server_CloseClient(NBN_ConnectionHandle *conn) { + return Server_CloseClientWithCode(HANDLE_TO_CONN(conn), -1, false); } -NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id, NBN_ConnectionHandle *receiver) { +NBN_Writer *NBN_Server_CreateMessage(uint8_t type, uint8_t channel_id, NBN_ConnectionHandle *receiver) { NBN_Connection *conn = HANDLE_TO_CONN(receiver); NBN_Assert(conn->is_accepted || type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE || type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); @@ -2600,7 +2598,7 @@ NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id, NBN_C /* Do not close the client if we failed to send the close client message to avoid infinite loops */ if (type != NBN_CLIENT_CLOSED_MESSAGE_TYPE) { - GameServer_CloseClientWithCode(conn, -1, false); + Server_CloseClientWithCode(conn, -1, false); return NULL; } @@ -2609,15 +2607,15 @@ NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id, NBN_C return writer; } -NBN_Writer *NBN_GameServer_CreateReliableMessage(uint8_t type, NBN_ConnectionHandle *receiver) { - return NBN_GameServer_CreateMessage(type, nbn_game_server.endpoint.default_reliable_channel, receiver); +NBN_Writer *NBN_Server_CreateReliableMessage(uint8_t type, NBN_ConnectionHandle *receiver) { + return NBN_Server_CreateMessage(type, nbn_game_server.endpoint.default_reliable_channel, receiver); } -NBN_Writer *NBN_GameServer_CreateUnreliableMessage(uint8_t type, NBN_ConnectionHandle *receiver) { - return NBN_GameServer_CreateMessage(type, nbn_game_server.endpoint.default_unreliable_channel, receiver); +NBN_Writer *NBN_Server_CreateUnreliableMessage(uint8_t type, NBN_ConnectionHandle *receiver) { + return NBN_Server_CreateMessage(type, nbn_game_server.endpoint.default_unreliable_channel, receiver); } -NBN_Reader *NBN_GameServer_ReadMessage(void) { +NBN_Reader *NBN_Server_ReadMessage(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); NBN_MessageInfo msg_info = nbn_game_server.last_event.data.message_info; @@ -2628,7 +2626,7 @@ NBN_Reader *NBN_GameServer_ReadMessage(void) { return reader; } -NBN_Writer *NBN_GameServer_WriteConnectionData(void) { +NBN_Writer *NBN_Server_WriteConnectionData(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); @@ -2640,14 +2638,14 @@ NBN_Writer *NBN_GameServer_WriteConnectionData(void) { return &nbn_game_server.server_data_writer; } -int NBN_GameServer_AcceptIncomingConnection(void) { +int NBN_Server_AcceptIncomingConnection(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); unsigned data_length = nbn_game_server.server_data_writer.position; NBN_Connection *client = nbn_game_server.last_event.data.connection; NBN_Writer *writer = - NBN_GameServer_CreateReliableMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE, (NBN_ConnectionHandle *)client); + NBN_Server_CreateReliableMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE, (NBN_ConnectionHandle *)client); if (!writer) { return NBN_ERROR; @@ -2669,26 +2667,26 @@ int NBN_GameServer_AcceptIncomingConnection(void) { return 0; } -int NBN_GameServer_RejectIncomingConnectionWithCode(int code) { +int NBN_Server_RejectIncomingConnectionWithCode(int code) { NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); NBN_Connection *conn = nbn_game_server.last_event.data.connection; LogDebug("Rejecting incoming connection %lld (code: %d)", conn->handle.id, code); - return GameServer_CloseClientWithCode(conn, code, false); + return Server_CloseClientWithCode(conn, code, false); } -int NBN_GameServer_RejectIncomingConnection(void) { return NBN_GameServer_RejectIncomingConnectionWithCode(-1); } +int NBN_Server_RejectIncomingConnection(void) { return NBN_Server_RejectIncomingConnectionWithCode(-1); } -NBN_ConnectionHandle *NBN_GameServer_GetIncomingConnection(void) { +NBN_ConnectionHandle *NBN_Server_GetIncomingConnection(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); NBN_Assert(nbn_game_server.last_event.data.connection != NULL); return (NBN_ConnectionHandle *)nbn_game_server.last_event.data.connection; } -NBN_Reader *NBN_GameServer_ReadConnectionRequestData(void) { +NBN_Reader *NBN_Server_ReadConnectionRequestData(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); NBN_Endpoint *endpoint = &nbn_game_server.endpoint; @@ -2699,28 +2697,28 @@ NBN_Reader *NBN_GameServer_ReadConnectionRequestData(void) { return &nbn_game_server.client_data_reader; } -NBN_DisconnectionInfo NBN_GameServer_GetDisconnectionInfo(void) { +NBN_DisconnectionInfo NBN_Server_GetDisconnectionInfo(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_DISCONNECTION); return nbn_game_server.last_event.data.disconnection; } -NBN_MessageInfo NBN_GameServer_GetMessageInfo(void) { +NBN_MessageInfo NBN_Server_GetMessageInfo(void) { NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_MESSAGE_RECEIVED); return nbn_game_server.last_event.data.message_info; } -NBN_GameServerStats NBN_GameServer_GetStats(void) { return nbn_game_server.stats; } +NBN_ServerStats NBN_Server_GetStats(void) { return nbn_game_server.stats; } -static void GameServer_AddClient(NBN_Connection *client) { +static void Server_AddClient(NBN_Connection *client) { NBN_Assert(hmgeti(nbn_game_server.clients, client->handle.id) == -1); hmput(nbn_game_server.clients, client->handle.id, client); LogDebug("New client %lld", client->handle.id); } -static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection) { +static int Server_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection) { if (!client->is_closed && client->is_accepted) { if (!disconnection) { NBN_Event e; @@ -2736,7 +2734,7 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool if (client->is_stale) { LogDebug("Closing stale connection %lld", client->handle.id); - GameServer_AddClientToClosedList(client); + Server_AddClientToClosedList(client); client->is_closed = true; return 0; @@ -2744,14 +2742,14 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool LogDebug("Closing active connection %lld (will send a disconnection message)", client->handle.id); - GameServer_AddClientToClosedList(client); + Server_AddClientToClosedList(client); client->is_closed = true; if (!disconnection) { LogDebug("Send close message for client %lld (code: %d)", client->handle.id, code); NBN_Writer *writer = - NBN_GameServer_CreateReliableMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE, (NBN_ConnectionHandle *)client); + NBN_Server_CreateReliableMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE, (NBN_ConnectionHandle *)client); if (!writer) { return NBN_ERROR; @@ -2763,7 +2761,7 @@ static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool return 0; } -static void GameServer_AddClientToClosedList(NBN_Connection *client) { +static void Server_AddClientToClosedList(NBN_Connection *client) { if (client->is_closed) return; @@ -2789,7 +2787,7 @@ static void GameServer_AddClientToClosedList(NBN_Connection *client) { } } -static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *client) { +static int Server_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *client) { NBN_Event ev; ev.type = NBN_CLIENT_MESSAGE_RECEIVED; @@ -2812,7 +2810,7 @@ static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connectio return 0; } -static int GameServer_CloseStaleClientConnections(void) { +static int Server_CloseStaleClientConnections(void) { for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { NBN_Connection *client = nbn_game_server.clients[i].value; @@ -2821,7 +2819,7 @@ static int GameServer_CloseStaleClientConnections(void) { client->is_stale = true; - if (GameServer_CloseClientWithCode(client, -1, false) < 0) + if (Server_CloseClientWithCode(client, -1, false) < 0) return NBN_ERROR; } } @@ -2829,7 +2827,7 @@ static int GameServer_CloseStaleClientConnections(void) { return 0; } -static void GameServer_RemoveClosedClientConnections(void) { +static void Server_RemoveClosedClientConnections(void) { NBN_ConnectionListNode *current = nbn_game_server.closed_clients_head; while (current) { @@ -2878,9 +2876,9 @@ static void GameServer_RemoveClosedClientConnections(void) { } } -static bool GameServer_HandleEvent(NBN_Server_Event *ev) { +static bool Server_HandleEvent(NBN_Server_Event *ev) { if (nbn_game_server.last_event.type == NBN_SERVER_MESSAGE_RECEIVED) { - return GameServer_HandleMessageReceivedEvent(ev); + return Server_HandleMessageReceivedEvent(ev); } *ev = nbn_game_server.last_event.type; @@ -2888,7 +2886,7 @@ static bool GameServer_HandleEvent(NBN_Server_Event *ev) { } // TODO: big ass function -static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev) { +static bool Server_HandleMessageReceivedEvent(NBN_Server_Event *ev) { NBN_Event *last_event = &nbn_game_server.last_event; NBN_MessageInfo message_info = last_event->data.message_info; NBN_Connection *sender = HANDLE_TO_CONN(message_info.sender); @@ -2902,7 +2900,7 @@ static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev) { LogInfo("Received a disconnection request from client %lld (user_data: %p)", sender->handle.id, sender->handle.user_data); - if (GameServer_CloseClientWithCode(sender, -1, true) < 0) { + if (Server_CloseClientWithCode(sender, -1, true) < 0) { *ev = NBN_ERROR; return true; } @@ -2912,7 +2910,7 @@ static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev) { last_event->type = NBN_SERVER_DISCONNECTION; last_event->data.disconnection = (NBN_DisconnectionInfo){sender->handle.id, sender->handle.user_data}; - GameServer_RemoveClosedClientConnections(); + Server_RemoveClosedClientConnections(); *ev = NBN_SERVER_DISCONNECTION; return true; @@ -2937,7 +2935,7 @@ static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev) { return true; } - NBN_Reader *reader = NBN_GameServer_ReadMessage(); + NBN_Reader *reader = NBN_Server_ReadMessage(); unsigned int data_length; if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) { @@ -2979,14 +2977,14 @@ static bool GameServer_HandleMessageReceivedEvent(NBN_Server_Event *ev) { return true; } -static void ServerDriver_OnClientConnected(NBN_Connection *client) { GameServer_AddClient(client); } +static void ServerDriver_OnClientConnected(NBN_Connection *client) { Server_AddClient(client); } static int ServerDriver_OnClientPacketReceived(NBN_Packet *packet) { if (Endpoint_ProcessReceivedPacket(&nbn_game_server.endpoint, packet, packet->sender) < 0) { LogError("An error occured while processing packet from client %d, closing the client", packet->sender->handle.id); - return GameServer_CloseClientWithCode(packet->sender, -1, false); + return Server_CloseClientWithCode(packet->sender, -1, false); } return 0; @@ -3078,7 +3076,7 @@ static NBN_Connection_ID UDP_BuildConnectionID(NBN_IPAddress address) { static NBN_Connection *UDP_FindOrCreateClientConnectionByAddress(NBN_IPAddress address) { NBN_Connection_ID conn_id = UDP_BuildConnectionID(address); conn_id = NBN_BuildConnectionHash(conn_id, NBN_DRIVER_UDP); - NBN_ConnectionHandle *handle = NBN_GameServer_GetConnection(conn_id); + NBN_ConnectionHandle *handle = NBN_Server_GetConnection(conn_id); if (handle) { return HANDLE_TO_CONN(handle); @@ -3131,7 +3129,9 @@ static char *UDP_GetLastErrorMessage(void) { #endif } -static int UDP_Server_Start(NBN_GameServer *server, uint16_t port) { +static int UDP_Server_Start(NBN_Server *server, uint16_t port) { + (void)server; + if (UDP_InitSocket() < 0) return NBN_ERROR; @@ -3141,9 +3141,12 @@ static int UDP_Server_Start(NBN_GameServer *server, uint16_t port) { return 0; } -static void UDP_Server_Stop(NBN_GameServer *server) { UDP_DeinitSocket(); } +static void UDP_Server_Stop(NBN_Server *server) { + (void)server; + UDP_DeinitSocket(); +} -static int UDP_Server_RecvPackets(NBN_GameServer *server) { +static int UDP_Server_RecvPackets(NBN_Server *server) { static NBN_Packet packet = {0}; SOCKADDR_IN src_addr; socklen_t src_addr_len = sizeof(src_addr); @@ -3177,9 +3180,14 @@ static int UDP_Server_RecvPackets(NBN_GameServer *server) { return 0; } -static void UDP_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *connection) {} +static void UDP_Server_CleanupConnection(NBN_Server *server, NBN_Connection *connection) { + (void)server; + (void)connection; +} + +static int UDP_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_Connection *connection) { + (void)server; -static int UDP_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *connection) { NBN_IPAddress dest_address = connection->driver_data.udp.ip_address; SOCKADDR_IN dest_addr; @@ -3197,7 +3205,7 @@ static int UDP_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, N return 0; } -static int UDP_Client_Start(NBN_GameClient *client, const char *host, uint16_t port) { +static int UDP_Client_Start(NBN_Client *client, const char *host, uint16_t port) { NBN_IPAddress *ip_address = &client->server_connection->driver_data.udp.ip_address; UDP_ParseIpAddress(host, port, ip_address); @@ -3211,9 +3219,12 @@ static int UDP_Client_Start(NBN_GameClient *client, const char *host, uint16_t p return 0; } -static void UDP_Client_Stop(NBN_GameClient *client) { UDP_DeinitSocket(); } +static void UDP_Client_Stop(NBN_Client *client) { + (void)client; + UDP_DeinitSocket(); +} -static int UDP_Client_RecvPackets(NBN_GameClient *client) { +static int UDP_Client_RecvPackets(NBN_Client *client) { NBN_IPAddress server_address = client->server_connection->driver_data.udp.ip_address; static NBN_Packet packet = {0}; SOCKADDR_IN src_addr; @@ -3248,7 +3259,7 @@ static int UDP_Client_RecvPackets(NBN_GameClient *client) { return 0; } -static int UDP_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet) { +static int UDP_Client_SendPacket(NBN_Client *client, NBN_Packet *packet) { NBN_IPAddress server_address = client->server_connection->driver_data.udp.ip_address; SOCKADDR_IN dest_addr; @@ -3298,7 +3309,7 @@ extern void __js_game_client_close(void); void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config) { nbn_wrtc_cfg = config; } -static int WebRTC_Server_Start(NBN_GameServer *server, uint16_t port) { +static int WebRTC_Server_Start(NBN_Server *server, uint16_t port) { __js_game_server_init(server->endpoint.protocol_id, nbn_wrtc_cfg.enable_tls, nbn_wrtc_cfg.key_path, nbn_wrtc_cfg.cert_path); @@ -3308,16 +3319,16 @@ static int WebRTC_Server_Start(NBN_GameServer *server, uint16_t port) { return 0; } -static void WebRTC_Server_Stop(NBN_GameServer *server) { __js_game_server_stop(); } +static void WebRTC_Server_Stop(NBN_Server *server) { __js_game_server_stop(); } -static int WebRTC_Server_RecvPackets(NBN_GameServer *server) { +static int WebRTC_Server_RecvPackets(NBN_Server *server) { static NBN_Packet packet = {0}; uint32_t peer_id; unsigned int len; while ((len = __js_game_server_dequeue_packet(&peer_id, packet.buffer)) > 0) { NBN_Connection_ID conn_id = NBN_BuildConnectionHash(peer_id, NBN_DRIVER_WEBRTC_EMSCRIPTEN); - NBN_ConnectionHandle *handle = NBN_GameServer_GetConnection(conn_id); + NBN_ConnectionHandle *handle = NBN_Server_GetConnection(conn_id); NBN_Connection *conn = NULL; if (handle == NULL) { @@ -3342,17 +3353,17 @@ static int WebRTC_Server_RecvPackets(NBN_GameServer *server) { return 0; } -static void WebRTC_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *conn) { +static void WebRTC_Server_CleanupConnection(NBN_Server *server, NBN_Connection *conn) { NBN_Assert(conn != NULL); __js_game_server_close_client_peer(conn->driver_data.webrtc.peer_id); } -static int WebRTC_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *conn) { +static int WebRTC_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_Connection *conn) { return __js_game_server_send_packet_to(packet->buffer, packet->size, conn->driver_data.webrtc.peer_id); } -static int WebRTC_Client_Start(NBN_GameClient *client, const char *host, uint16_t port) { +static int WebRTC_Client_Start(NBN_Client *client, const char *host, uint16_t port) { __js_game_client_init(client->endpoint.protocol_id, nbn_wrtc_cfg.enable_tls); int res; @@ -3363,9 +3374,9 @@ static int WebRTC_Client_Start(NBN_GameClient *client, const char *host, uint16_ return 0; } -static void WebRTC_Client_Stop(NBN_GameClient *client) { __js_game_client_close(); } +static void WebRTC_Client_Stop(NBN_Client *client) { __js_game_client_close(); } -static int WebRTC_Client_RecvPackets(NBN_GameClient *client) { +static int WebRTC_Client_RecvPackets(NBN_Client *client) { static NBN_Packet packet = {0}; unsigned int len; @@ -3381,7 +3392,7 @@ static int WebRTC_Client_RecvPackets(NBN_GameClient *client) { return 0; } -static int WebRTC_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet) { +static int WebRTC_Client_SendPacket(NBN_Client *client, NBN_Packet *packet) { return __js_game_client_send_packet(packet->buffer, packet->size); } @@ -3585,6 +3596,8 @@ static void ClosePeer(NBN_Connection *conn) { } static void Server_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { + (void)pc; + LogDebug("Processing local description of type '%s'", type); if (strncmp(type, "answer", strlen("answer")) != 0) { @@ -3613,8 +3626,8 @@ static void Server_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { } } -static int CreatePeer(int ws, NBN_WebRTC_Peer_ID *peer_id, int *channel_id, - rtcDescriptionCallbackFunc on_rtc_description_cb, rtcStateChangeCallbackFunc state_changed_cb) { +static int CreatePeer(NBN_WebRTC_Peer_ID *peer_id, int *channel_id, rtcDescriptionCallbackFunc on_rtc_description_cb, + rtcStateChangeCallbackFunc state_changed_cb) { rtcConfiguration rtcCfg = {.iceServers = nbn_wrtc_cfg.ice_servers, .iceServersCount = (int)nbn_wrtc_cfg.ice_servers_count, .disableAutoNegotiation = false}; @@ -3656,12 +3669,14 @@ static int CreatePeer(int ws, NBN_WebRTC_Peer_ID *peer_id, int *channel_id, } static void WS_Server_OnOpen(int ws, void *user_ptr) { + (void)user_ptr; + LogDebug("WS %d is open", ws); NBN_WebRTC_Peer_ID peer_id; int channel_id; - if (CreatePeer(ws, &peer_id, &channel_id, Server_OnLocalDescription, Server_OnPeerStateChanged) < 0) { + if (CreatePeer(&peer_id, &channel_id, Server_OnLocalDescription, Server_OnPeerStateChanged) < 0) { LogError("Failed to create peer"); return; } @@ -3699,6 +3714,8 @@ static void WS_Server_OnMessage(int ws, const char *msg, int size, void *user_pt } static void OnWs_Connection(int wsserver, int ws, void *user_ptr) { + (void)wsserver; + LogDebug("New WS connection %d (user_ptr: %p)", ws, user_ptr); rtcSetOpenCallback(ws, WS_Server_OnOpen); @@ -3707,7 +3724,9 @@ static void OnWs_Connection(int wsserver, int ws, void *user_ptr) { rtcSetMessageCallback(ws, WS_Server_OnMessage); } -static int WebRTC_Native_Server_Start(NBN_GameServer *server, uint16_t port) { +static int WebRTC_Native_Server_Start(NBN_Server *server, uint16_t port) { + (void)server; + rtcInitLogger(nbn_wrtc_cfg.log_level, WebRTC_Native_Log); rtcPreload(); @@ -3727,7 +3746,9 @@ static int WebRTC_Native_Server_Start(NBN_GameServer *server, uint16_t port) { return 0; } -static void WebRTC_Native_Server_Stop(NBN_GameServer *server) { +static void WebRTC_Native_Server_Stop(NBN_Server *server) { + (void)server; + if (wsserver >= 0) { rtcDeleteWebSocketServer(wsserver); } @@ -3735,7 +3756,7 @@ static void WebRTC_Native_Server_Stop(NBN_GameServer *server) { rtcCleanup(); } -static int WebRTC_Native_Server_RecvPackets(NBN_GameServer *server) { +static int WebRTC_Native_Server_RecvPackets(NBN_Server *server) { static NBN_Packet packet = {0}; const int buffer_size = sizeof(packet.buffer); int size = buffer_size; @@ -3762,12 +3783,16 @@ static int WebRTC_Native_Server_RecvPackets(NBN_GameServer *server) { return 0; } -static void WebRTC_Native_Server_CleanupConnection(NBN_GameServer *server, NBN_Connection *conn) { +static void WebRTC_Native_Server_CleanupConnection(NBN_Server *server, NBN_Connection *conn) { + (void)server; + NBN_Assert(conn != NULL); ClosePeer(conn); } -static int WebRTC_Native_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet *packet, NBN_Connection *conn) { +static int WebRTC_Native_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_Connection *conn) { + (void)server; + NBN_WebRTC_Peer_ID peer_id = conn->driver_data.webrtc.peer_id; int channel_id = conn->driver_data.webrtc.channel_id; @@ -3783,6 +3808,8 @@ static int WebRTC_Native_Server_SendPacketTo(NBN_GameServer *server, NBN_Packet static bool wrtc_client_connected; static void Client_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { + (void)pc; + LogDebug("Processing local description of type '%s'", type); if (strncmp(type, "offer", strlen("offer")) != 0) { @@ -3797,6 +3824,8 @@ static void Client_OnLocalDescription(int pc, const char *sdp, const char *type, } static void Client_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { + (void)user_ptr; + LogDebug("Server peer state changed to %d", pc, state); if (state == RTC_CONNECTED) { @@ -3806,12 +3835,14 @@ static void Client_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { } static void WS_Client_OnOpen(int ws, void *user_ptr) { + (void)user_ptr; + LogDebug("WS %d is open, creating peer...", ws); NBN_WebRTC_Peer_ID peer_id; int channel_id; - if (CreatePeer(ws, &peer_id, &channel_id, Client_OnLocalDescription, Client_OnPeerStateChanged) < 0) { + if (CreatePeer(&peer_id, &channel_id, Client_OnLocalDescription, Client_OnPeerStateChanged) < 0) { LogError("Failed to create peer"); return; } @@ -3842,7 +3873,9 @@ static void WS_Client_OnMessage(int ws, const char *msg, int size, void *user_pt ProcessSignalingMessage(peer_id, ws, msg, size, "answer"); } -static int WebRTC_Native_Client_Start(NBN_GameClient *client, const char *host, uint16_t port) { +static int WebRTC_Native_Client_Start(NBN_Client *client, const char *host, uint16_t port) { + (void)client; + wrtc_client_connected = false; rtcInitLogger(nbn_wrtc_cfg.log_level, WebRTC_Native_Log); @@ -3900,7 +3933,7 @@ static int WebRTC_Native_Client_Start(NBN_GameClient *client, const char *host, return wrtc_client_connected ? 0 : NBN_ERROR; } -static void WebRTC_Native_Client_Stop(NBN_GameClient *client) { +static void WebRTC_Native_Client_Stop(NBN_Client *client) { if (wrtc_client_connected) { ClosePeer(client->server_connection); } @@ -3909,7 +3942,7 @@ static void WebRTC_Native_Client_Stop(NBN_GameClient *client) { rtcCleanup(); } -static int WebRTC_Native_Client_RecvPackets(NBN_GameClient *client) { +static int WebRTC_Native_Client_RecvPackets(NBN_Client *client) { static NBN_Packet packet = {0}; const int buffer_size = sizeof(packet.buffer); int size = buffer_size; @@ -3928,7 +3961,7 @@ static int WebRTC_Native_Client_RecvPackets(NBN_GameClient *client) { return 0; } -static int WebRTC_Native_Client_SendPacket(NBN_GameClient *client, NBN_Packet *packet) { +static int WebRTC_Native_Client_SendPacket(NBN_Client *client, NBN_Packet *packet) { int channel_id = client->server_connection->driver_data.webrtc.channel_id; if (rtcSendMessage(channel_id, (char *)packet->buffer, packet->size) < 0) { @@ -3959,17 +3992,17 @@ DWORD WINAPI PacketSimulator_Routine(LPVOID); static void *PacketSimulator_Routine(void *); #endif -void NBN_GameClient_SetPing(float v) { nbn_game_client.endpoint.packet_simulator.ping = v; } -void NBN_GameClient_SetJitter(float v) { nbn_game_client.endpoint.packet_simulator.jitter = v; } -void NBN_GameClient_SetPacketLoss(float v) { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } -void NBN_GameClient_SetPacketDuplication(float v) { +void NBN_Client_SetPing(float v) { nbn_game_client.endpoint.packet_simulator.ping = v; } +void NBN_Client_SetJitter(float v) { nbn_game_client.endpoint.packet_simulator.jitter = v; } +void NBN_Client_SetPacketLoss(float v) { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } +void NBN_Client_SetPacketDuplication(float v) { nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; } -void NBN_GameServer_SetPing(float v) { nbn_game_server.endpoint.packet_simulator.ping = v; } -void NBN_GameServer_SetJitter(float v) { nbn_game_server.endpoint.packet_simulator.jitter = v; } -void NBN_GameServer_SetPacketLoss(float v) { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } -void NBN_GameServer_SetPacketDuplication(float v) { +void NBN_Server_SetPing(float v) { nbn_game_server.endpoint.packet_simulator.ping = v; } +void NBN_Server_SetJitter(float v) { nbn_game_server.endpoint.packet_simulator.jitter = v; } +void NBN_Server_SetPacketLoss(float v) { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } +void NBN_Server_SetPacketDuplication(float v) { nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; } @@ -4194,7 +4227,7 @@ static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimu #endif // NBN_DEBUG -static int log_level = NBN_DEFAULT_LOG_LEVEL; +static NBN_LogLevel log_level = NBN_DEFAULT_LOG_LEVEL; void NBN_SetLogLevel(NBN_LogLevel level) { log_level = level; } diff --git a/nbnet.h b/nbnet.h index e9b77bb..69deebc 100644 --- a/nbnet.h +++ b/nbnet.h @@ -136,10 +136,10 @@ typedef enum NBN_Server_Event { NBN_SERVER_MESSAGE_RECEIVED } NBN_Server_Event; -typedef struct NBN_GameServerStats { +typedef struct NBN_ServerStats { float upload_bandwidth; /* Total upload bandwith of the game server */ float download_bandwidth; /* Total download bandwith of the game server */ -} NBN_GameServerStats; +} NBN_ServerStats; typedef struct NBN_DisconnectionInfo { NBN_Connection_ID conn_id; /* ID if the disconnected connection */ @@ -200,32 +200,32 @@ int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len); * @param host Host to connect to * @param port Port to connect to */ -void NBN_GameClient_Init(const char *protocol_name, const char *host, uint16_t port); +void NBN_Client_Init(const char *protocol_name, const char *host, uint16_t port); // TODO: doc -uint8_t NBN_GameClient_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len); +uint8_t NBN_Client_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len); // TODO: doc -unsigned int NBN_GameClient_GetChannelCurrentCapacity(uint8_t channel_id); +unsigned int NBN_Client_GetChannelCurrentCapacity(uint8_t channel_id); // TODO: doc -NBN_Writer *NBN_GameClient_WriteConnectionRequestData(void); +NBN_Writer *NBN_Client_WriteConnectionRequestData(void); /** * Start the game client. * * @return 0 when successully started, -1 otherwise */ -int NBN_GameClient_Start(void); +int NBN_Client_Start(void); /** - * Disconnect from the server. The client can be restarted by calling NBN_GameClient_Start or - * NBN_GameClient_StartWithData again. + * Disconnect from the server. The client can be restarted by calling NBN_Client_Start or + * NBN_Client_StartWithData again. */ -void NBN_GameClient_Stop(void); +void NBN_Client_Stop(void); // TODO: doc -NBN_Reader *NBN_GameClient_ReadServerData(void); +NBN_Reader *NBN_Client_ReadServerData(void); /** * Poll game client events. @@ -234,7 +234,7 @@ NBN_Reader *NBN_GameClient_ReadServerData(void); * * @return The code of the polled event or NBN_NO_EVENT when there is no more events. */ -NBN_Client_Event NBN_GameClient_Poll(void); +NBN_Client_Event NBN_Client_Poll(void); /** * Pack all enqueued messages into packets and send them. @@ -244,19 +244,19 @@ NBN_Client_Event NBN_GameClient_Poll(void); * * @return 0 when successful, -1 otherwise */ -int NBN_GameClient_Flush(void); +int NBN_Client_Flush(void); // TODO: doc -NBN_Writer *NBN_GameClient_CreateMessage(uint8_t type, uint8_t channel_id); +NBN_Writer *NBN_Client_CreateMessage(uint8_t type, uint8_t channel_id); // TODO: doc -NBN_Writer *NBN_GameClient_CreateReliableMessage(uint8_t type); +NBN_Writer *NBN_Client_CreateReliableMessage(uint8_t type); // TODO: doc -NBN_Writer *NBN_GameClient_CreateUnreliableMessage(uint8_t type); +NBN_Writer *NBN_Client_CreateUnreliableMessage(uint8_t type); // TODO: doc -NBN_Reader *NBN_GameClient_ReadMessage(void); +NBN_Reader *NBN_Client_ReadMessage(void); /** * Retrieve the info about the last received message. @@ -266,14 +266,14 @@ NBN_Reader *NBN_GameClient_ReadMessage(void); * * @return A structure containing information about the received message */ -NBN_MessageInfo NBN_GameClient_GetMessageInfo(void); +NBN_MessageInfo NBN_Client_GetMessageInfo(void); /** * Retrieve network stats about the game client. * * @return A structure containing network related stats about the game client */ -NBN_ConnectionStats NBN_GameClient_GetStats(void); +NBN_ConnectionStats NBN_Client_GetStats(void); /** * Retrieve the code sent by the server when closing the connection. @@ -282,12 +282,12 @@ NBN_ConnectionStats NBN_GameClient_GetStats(void); * * @return The code used by the server when closing the connection or -1 (the default code) */ -int NBN_GameClient_GetServerCloseCode(void); +int NBN_Client_GetServerCloseCode(void); /** * @return true if connected, false otherwise */ -bool NBN_GameClient_IsConnected(void); +bool NBN_Client_IsConnected(void); /** * Initialize the game server with minimal configuration. @@ -296,33 +296,33 @@ bool NBN_GameClient_IsConnected(void); * able to communicate * @param port The port clients will connect to */ -void NBN_GameServer_Init(const char *protocol_name, uint16_t port); +void NBN_Server_Init(const char *protocol_name, uint16_t port); // TODO: doc -uint8_t NBN_GameServer_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len); +uint8_t NBN_Server_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len); // TODO: doc -unsigned int NBN_GameServer_GetChannelCurrentCapacity(uint8_t channel_id, NBN_ConnectionHandle *conn); +unsigned int NBN_Server_GetChannelCurrentCapacity(uint8_t channel_id, NBN_ConnectionHandle *conn); /** * Start the game server with the provided configuration. * * @return 0 when successfully started, -1 otherwise */ -int NBN_GameServer_Start(void); +int NBN_Server_Start(void); /** * Stop the game server and clean everything up. */ -void NBN_GameServer_Stop(void); +void NBN_Server_Stop(void); // TODO: doc -NBN_ConnectionHandle *NBN_GameServer_GetConnection(NBN_Connection_ID); +NBN_ConnectionHandle *NBN_Server_GetConnection(NBN_Connection_ID); // TODO: doc -unsigned int NBN_GameServer_GetClientCount(void); +unsigned int NBN_Server_GetClientCount(void); -NBN_ConnectionHandle *NBN_GameServer_GetNextClient(NBN_Client_Iterator *it); +NBN_ConnectionHandle *NBN_Server_GetNextClient(NBN_Client_Iterator *it); /** * Poll game server events. @@ -331,7 +331,7 @@ NBN_ConnectionHandle *NBN_GameServer_GetNextClient(NBN_Client_Iterator *it); * * @return The code of the polled event or NBN_NO_EVENT when there is no more events. */ -NBN_Server_Event NBN_GameServer_Poll(void); +NBN_Server_Event NBN_Server_Poll(void); /** * Pack all enqueued messages into packets and send them. @@ -341,7 +341,7 @@ NBN_Server_Event NBN_GameServer_Poll(void); * * @return 0 when successful, -1 otherwise */ -int NBN_GameServer_Flush(void); +int NBN_Server_Flush(void); /** * Close a client's connection without a specific code (default code is -1) @@ -350,7 +350,7 @@ int NBN_GameServer_Flush(void); * * @return 0 when successful, -1 otherwise */ -int NBN_GameServer_CloseClient(NBN_ConnectionHandle *conn); +int NBN_Server_CloseClient(NBN_ConnectionHandle *conn); /** * Close a client's connection with a specific code. @@ -362,25 +362,25 @@ int NBN_GameServer_CloseClient(NBN_ConnectionHandle *conn); * * @return 0 when successful, -1 otherwise */ -int NBN_GameServer_CloseClientWithCode(NBN_ConnectionHandle *conn, int code); +int NBN_Server_CloseClientWithCode(NBN_ConnectionHandle *conn, int code); // TODO: doc -NBN_Writer *NBN_GameServer_CreateMessage(uint8_t type, uint8_t channel_id, NBN_ConnectionHandle *receiver); +NBN_Writer *NBN_Server_CreateMessage(uint8_t type, uint8_t channel_id, NBN_ConnectionHandle *receiver); // TODO: doc -NBN_Writer *NBN_GameServer_CreateReliableMessage(uint8_t type, NBN_ConnectionHandle *receiver); +NBN_Writer *NBN_Server_CreateReliableMessage(uint8_t type, NBN_ConnectionHandle *receiver); // TODO: doc -NBN_Writer *NBN_GameServer_CreateUnreliableMessage(uint8_t type, NBN_ConnectionHandle *receiver); +NBN_Writer *NBN_Server_CreateUnreliableMessage(uint8_t type, NBN_ConnectionHandle *receiver); // TODO: doc -NBN_Reader *NBN_GameServer_ReadMessage(void); +NBN_Reader *NBN_Server_ReadMessage(void); // TODO: doc -NBN_Writer *NBN_GameServer_WriteConnectionData(void); +NBN_Writer *NBN_Server_WriteConnectionData(void); // TODO: doc -int NBN_GameServer_AcceptIncomingConnection(void); +int NBN_Server_AcceptIncomingConnection(void); /** * Reject the last client connection request with a specific code. @@ -392,7 +392,7 @@ int NBN_GameServer_AcceptIncomingConnection(void); * * @return 0 when successful, -1 otherwise */ -int NBN_GameServer_RejectIncomingConnectionWithCode(int code); +int NBN_Server_RejectIncomingConnectionWithCode(int code); /** * Reject the last client connection request without any specific code (default code is -1) @@ -401,7 +401,7 @@ int NBN_GameServer_RejectIncomingConnectionWithCode(int code); * * @return 0 when successful, -1 otherwise */ -int NBN_GameServer_RejectIncomingConnection(void); +int NBN_Server_RejectIncomingConnection(void); /** * Retrieve the last connection to the game server. @@ -410,10 +410,10 @@ int NBN_GameServer_RejectIncomingConnection(void); * * @return A pointer to a NBN_Connection representing the new connection */ -NBN_ConnectionHandle *NBN_GameServer_GetIncomingConnection(void); +NBN_ConnectionHandle *NBN_Server_GetIncomingConnection(void); // TODO: doc -NBN_Reader *NBN_GameServer_ReadConnectionRequestData(void); +NBN_Reader *NBN_Server_ReadConnectionRequestData(void); /** * Return the information about the last disconnected client. @@ -423,7 +423,7 @@ NBN_Reader *NBN_GameServer_ReadConnectionRequestData(void); * * @return information about the last disconnected client */ -NBN_DisconnectionInfo NBN_GameServer_GetDisconnectionInfo(void); +NBN_DisconnectionInfo NBN_Server_GetDisconnectionInfo(void); /** * Retrieve the info about the last received message. @@ -433,14 +433,14 @@ NBN_DisconnectionInfo NBN_GameServer_GetDisconnectionInfo(void); * * @return A structure containing information about the received message */ -NBN_MessageInfo NBN_GameServer_GetMessageInfo(void); +NBN_MessageInfo NBN_Server_GetMessageInfo(void); /** * Retrieve network stats about the game server. * * @return A structure containing network related stats about the game server */ -NBN_GameServerStats NBN_GameServer_GetStats(void); +NBN_ServerStats NBN_Server_GetStats(void); #ifdef __EMSCRIPTEN__ @@ -501,15 +501,15 @@ void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config); #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) -void NBN_GameClient_SetPing(float v); -void NBN_GameClient_SetJitter(float v); -void NBN_GameClient_SetPacketLoss(float v); -void NBN_GameClient_SetPacketDuplication(float v); +void NBN_Client_SetPing(float v); +void NBN_Client_SetJitter(float v); +void NBN_Client_SetPacketLoss(float v); +void NBN_Client_SetPacketDuplication(float v); -void NBN_GameServer_SetPing(float v); -void NBN_GameServer_SetJitter(float v); -void NBN_GameServer_SetPacketLoss(float v); -void NBN_GameServer_SetPacketDuplication(float v); +void NBN_Server_SetPing(float v); +void NBN_Server_SetJitter(float v); +void NBN_Server_SetPacketLoss(float v); +void NBN_Server_SetPacketDuplication(float v); #else @@ -517,15 +517,15 @@ void NBN_GameServer_SetPacketDuplication(float v); do { \ } while (0); -#define NBN_GameClient_SetPing(v) NBN_PacketSimulator_Disabled -#define NBN_GameClient_SetJitter(v) NBN_PacketSimulator_Disabled -#define NBN_GameClient_SetPacketLoss(v) NBN_PacketSimulator_Disabled -#define NBN_GameClient_SetPacketDuplication(v) NBN_PacketSimulator_Disabled +#define NBN_Client_SetPing(v) NBN_PacketSimulator_Disabled +#define NBN_Client_SetJitter(v) NBN_PacketSimulator_Disabled +#define NBN_Client_SetPacketLoss(v) NBN_PacketSimulator_Disabled +#define NBN_Client_SetPacketDuplication(v) NBN_PacketSimulator_Disabled -#define NBN_GameServer_SetPing(v) NBN_PacketSimulator_Disabled -#define NBN_GameServer_SetJitter(v) NBN_PacketSimulator_Disabled -#define NBN_GameServer_SetPacketLoss(v) NBN_PacketSimulator_Disabled -#define NBN_GameServer_SetPacketDuplication(v) NBN_PacketSimulator_Disabled +#define NBN_Server_SetPing(v) NBN_PacketSimulator_Disabled +#define NBN_Server_SetJitter(v) NBN_PacketSimulator_Disabled +#define NBN_Server_SetPacketLoss(v) NBN_PacketSimulator_Disabled +#define NBN_Server_SetPacketDuplication(v) NBN_PacketSimulator_Disabled #endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ diff --git a/soak/client.c b/soak/client.c index 529c009..be976af 100644 --- a/soak/client.c +++ b/soak/client.c @@ -72,7 +72,7 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { log_info("Compute number of soak messages to send (sent: %d, pending: %d, remaining: %d)", channel->sent_message_count, pending_message_count, remaining_message_count); - unsigned int capacity = NBN_GameClient_GetChannelCurrentCapacity(channel->id); + unsigned int capacity = NBN_Client_GetChannelCurrentCapacity(channel->id); // make sure that we don't exceed channel capacity unsigned int max_pending_messages = (unsigned int)fmin(SOAK_CLIENT_MAX_PENDING_MESSAGES, capacity); @@ -117,7 +117,7 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { log_info("Send soak message (id: %d, data length: %d)", msg_id, data_length); // TODO: support big messages - NBN_Writer *writer = NBN_GameClient_CreateMessage(SOAK_MESSAGE_SMALL, channel->id); + NBN_Writer *writer = NBN_Client_CreateMessage(SOAK_MESSAGE_SMALL, channel->id); if (!writer) { return -1; @@ -141,7 +141,7 @@ static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) unsigned int data_length; static uint8_t recv_buffer[SOAK_MESSAGE_BIG_MAX_DATA_LENGTH]; - NBN_Reader *reader = NBN_GameClient_ReadMessage(); + NBN_Reader *reader = NBN_Client_ReadMessage(); if (SoakMessage_Read(reader, &msg_id, recv_buffer, &data_length) < 0) { log_error("Failed to read soak message"); @@ -200,7 +200,7 @@ static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) } static int HandleReceivedMessage(SoakChannel *channels) { - NBN_MessageInfo msg = NBN_GameClient_GetMessageInfo(); + NBN_MessageInfo msg = NBN_Client_GetMessageInfo(); int ret; @@ -220,7 +220,7 @@ static int Tick(void *data) { int ev; - while ((ev = NBN_GameClient_Poll()) != NBN_CLIENT_NO_EVENT) { + while ((ev = NBN_Client_Poll()) != NBN_CLIENT_NO_EVENT) { if (ev < 0) { log_error("Error while poling client events"); return -1; @@ -230,7 +230,7 @@ static int Tick(void *data) { case NBN_CLIENT_DISCONNECTED: connected = false; - log_info("Disconnected from server (code: %d)", NBN_GameClient_GetServerCloseCode()); + log_info("Disconnected from server (code: %d)", NBN_Client_GetServerCloseCode()); Soak_Stop(); return 0; @@ -259,7 +259,7 @@ static int Tick(void *data) { } } - if (NBN_GameClient_Flush() < 0) { + if (NBN_Client_Flush() < 0) { log_error("Failed to flush game client send queue. Exit"); return -1; @@ -276,17 +276,17 @@ int main(int argc, char *argv[]) { SoakOptions options = Soak_GetOptions(); - NBN_GameClient_Init(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT); + NBN_Client_Init(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT); for (uint8_t c = 0; c < SOAK_CHANNEL_COUNT; c++) { uint8_t channel_id = - NBN_GameClient_CreateChannel(NBN_CHANNEL_RELIABLE, SOAK_CHANNEL_BUFFER_SIZE, SOAK_MAX_MESSAGE_SIZE); + NBN_Client_CreateChannel(NBN_CHANNEL_RELIABLE, SOAK_CHANNEL_BUFFER_SIZE, SOAK_MAX_MESSAGE_SIZE); // channels 0 and 1 are the default nbnet channels assert(channel_id == 2 + c); } - if (NBN_GameClient_Start() < 0) { + if (NBN_Client_Start() < 0) { log_error("Failed to start game client. Exit"); #ifdef __EMSCRIPTEN__ @@ -325,7 +325,7 @@ int main(int argc, char *argv[]) { int ret = Soak_MainLoop(Tick, channels); - NBN_GameClient_Stop(); + NBN_Client_Stop(); free(channels); #ifdef __EMSCRIPTEN__ diff --git a/soak/server.c b/soak/server.c index bb26afd..f17cdb0 100644 --- a/soak/server.c +++ b/soak/server.c @@ -63,9 +63,9 @@ typedef struct { } SoakClient; static void HandleNewConnection(void) { - NBN_GameServer_AcceptIncomingConnection(); + NBN_Server_AcceptIncomingConnection(); - NBN_ConnectionHandle *conn = NBN_GameServer_GetIncomingConnection(); + NBN_ConnectionHandle *conn = NBN_Server_GetIncomingConnection(); SoakClient *soak_client = (SoakClient *)malloc(sizeof(SoakClient)); soak_client->error = false; @@ -107,7 +107,7 @@ static void EchoReceivedSoakMessages(void) { NBN_Client_Iterator it = 0; NBN_ConnectionHandle *conn; - while ((conn = NBN_GameServer_GetNextClient(&it))) { + while ((conn = NBN_Server_GetNextClient(&it))) { SoakClient *soak_client = (SoakClient *)conn->user_data; @@ -118,18 +118,18 @@ static void EchoReceivedSoakMessages(void) { for (unsigned int c = 0; c < SOAK_CHANNEL_COUNT; c++) { SoakChannel *channel = &soak_client->channels[c]; - int send_count = NBN_GameServer_GetChannelCurrentCapacity(channel->id, conn); + int send_count = NBN_Server_GetChannelCurrentCapacity(channel->id, conn); // make sure that we don't exceed channel capacity while (channel->echo_queue.count > 0 && --send_count >= 0) { Soak_MessageEntry *msg_entry = &channel->echo_queue.messages[channel->echo_queue.head]; NBN_Writer *writer = - NBN_GameServer_CreateMessage(SOAK_MESSAGE_SMALL, channel->id, conn); // TODO: support big + NBN_Server_CreateMessage(SOAK_MESSAGE_SMALL, channel->id, conn); // TODO: support big if (!writer) { log_error("Failed to send soak message to client %llu, closing client", conn->id); - if (NBN_GameServer_CloseClient(conn) < 0) { + if (NBN_Server_CloseClient(conn) < 0) { log_error("Failed to close client %llu", conn->id); abort(); } @@ -205,14 +205,14 @@ static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_ConnectionHandle *s } static void HandleReceivedMessage(void) { - NBN_MessageInfo msg_info = NBN_GameServer_GetMessageInfo(); - NBN_Reader *reader = NBN_GameServer_ReadMessage(); + NBN_MessageInfo msg_info = NBN_Server_GetMessageInfo(); + NBN_Reader *reader = NBN_Server_ReadMessage(); SoakClient *soak_client = (SoakClient *)msg_info.sender->user_data; switch (msg_info.type) { case SOAK_MESSAGE_SMALL: if (HandleReceivedSoakMessage(reader, msg_info.sender, msg_info.channel_id) < 0) { - if (NBN_GameServer_CloseClient(msg_info.sender) < 0) { + if (NBN_Server_CloseClient(msg_info.sender) < 0) { log_error("Failed to close client %llu", msg_info.sender->id); abort(); } @@ -226,7 +226,7 @@ static void HandleReceivedMessage(void) { default: log_error("Received unexpected message (type: %d, channel_id: %d)", msg_info.type, msg_info.channel_id); - if (NBN_GameServer_CloseClient(msg_info.sender) < 0) { + if (NBN_Server_CloseClient(msg_info.sender) < 0) { log_error("Failed to close client %llu", msg_info.sender->id); abort(); } @@ -241,7 +241,7 @@ static int Tick(void *data) { int ev; - while ((ev = NBN_GameServer_Poll()) != NBN_SERVER_NO_EVENT) { + while ((ev = NBN_Server_Poll()) != NBN_SERVER_NO_EVENT) { if (ev < 0) return -1; @@ -251,7 +251,7 @@ static int Tick(void *data) { break; case NBN_SERVER_DISCONNECTION: - HandleClientDisconnection(NBN_GameServer_GetDisconnectionInfo()); + HandleClientDisconnection(NBN_Server_GetDisconnectionInfo()); break; case NBN_SERVER_MESSAGE_RECEIVED: @@ -262,7 +262,7 @@ static int Tick(void *data) { EchoReceivedSoakMessages(); - if (NBN_GameServer_Flush() < 0) { + if (NBN_Server_Flush() < 0) { log_error("Failed to flush game server send queue. Exit"); return -1; @@ -283,17 +283,17 @@ int main(int argc, char *argv[]) { SoakOptions options = Soak_GetOptions(); - NBN_GameServer_Init(SOAK_PROTOCOL_NAME, SOAK_PORT); + NBN_Server_Init(SOAK_PROTOCOL_NAME, SOAK_PORT); for (uint8_t c = 0; c < SOAK_CHANNEL_COUNT; c++) { uint8_t channel_id = - NBN_GameServer_CreateChannel(NBN_CHANNEL_RELIABLE, SOAK_CHANNEL_BUFFER_SIZE, SOAK_MAX_MESSAGE_SIZE); + NBN_Server_CreateChannel(NBN_CHANNEL_RELIABLE, SOAK_CHANNEL_BUFFER_SIZE, SOAK_MAX_MESSAGE_SIZE); // channels 0 and 1 are the default nbnet channels assert(channel_id == 2 + c); } - if (NBN_GameServer_Start()) { + if (NBN_Server_Start()) { log_error("Failed to start game server"); return 1; @@ -307,7 +307,7 @@ int main(int argc, char *argv[]) { int ret = Soak_MainLoop(Tick, NULL); - NBN_GameServer_Stop(); + NBN_Server_Stop(); Soak_Deinit(); #ifdef WEBRTC_NATIVE diff --git a/soak/soak.c b/soak/soak.c index 3c35404..4d86181 100644 --- a/soak/soak.c +++ b/soak/soak.c @@ -75,17 +75,17 @@ int Soak_Init(int argc, char *argv[]) { /* Packet simulator configuration */ #ifdef SOAK_CLIENT - NBN_GameClient_SetPing(soak_options.ping); - NBN_GameClient_SetJitter(soak_options.jitter); - NBN_GameClient_SetPacketLoss(soak_options.packet_loss); - NBN_GameClient_SetPacketDuplication(soak_options.packet_duplication); + NBN_Client_SetPing(soak_options.ping); + NBN_Client_SetJitter(soak_options.jitter); + NBN_Client_SetPacketLoss(soak_options.packet_loss); + NBN_Client_SetPacketDuplication(soak_options.packet_duplication); #endif #ifdef SOAK_SERVER - NBN_GameServer_SetPing(soak_options.ping); - NBN_GameServer_SetJitter(soak_options.jitter); - NBN_GameServer_SetPacketLoss(soak_options.packet_loss); - NBN_GameServer_SetPacketDuplication(soak_options.packet_duplication); + NBN_Server_SetPing(soak_options.ping); + NBN_Server_SetJitter(soak_options.jitter); + NBN_Server_SetPacketLoss(soak_options.packet_loss); + NBN_Server_SetPacketDuplication(soak_options.packet_duplication); #endif return 0; From 210a4c5fcc2a9e0297166b729e90aba682331fd5 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 8 Apr 2026 17:07:55 +0200 Subject: [PATCH 82/85] remove global state, update API --- examples/echo/client.c | 30 +- examples/echo/server.c | 28 +- examples/raylib/client.c | 84 ++-- examples/raylib/server.c | 74 +-- nbnet.c | 954 +++++++++++++++++++++------------------ nbnet.h | 125 ++--- soak/client.c | 88 ++-- soak/server.c | 65 +-- soak/soak.c | 32 -- soak/soak.h | 1 - 10 files changed, 760 insertions(+), 721 deletions(-) diff --git a/examples/echo/client.c b/examples/echo/client.c index f3401a8..4fb74ba 100644 --- a/examples/echo/client.c +++ b/examples/echo/client.c @@ -41,7 +41,7 @@ void OnConnected(void) { connected = true; // Start sending messages } -void OnDisconnected(void) { +void OnDisconnected(NBN_Client *client) { log_info("Disconnected"); // Stop the main loop @@ -49,18 +49,18 @@ void OnDisconnected(void) { running = false; // Retrieve the server code used when closing our client connection - if (NBN_GameClient_GetServerCloseCode() == ECHO_SERVER_BUSY_CODE) { + if (NBN_Client_GetServerCloseCode(client) == ECHO_SERVER_BUSY_CODE) { log_info("Another client is already connected"); } } -void OnMessageReceived(void) { +void OnMessageReceived(NBN_Client *client) { // Get info about the received message - NBN_MessageInfo msg_info = NBN_GameClient_GetMessageInfo(); + NBN_MessageInfo msg_info = NBN_Client_GetMessageInfo(client); assert(msg_info.type == ECHO_MESSAGE_TYPE); - NBN_Reader *reader = NBN_GameClient_ReadMessage(); + NBN_Reader *reader = NBN_Client_ReadMessage(client); unsigned int length; int res; @@ -75,8 +75,8 @@ void OnMessageReceived(void) { log_info("Received echo: %s (length: %d, channel: %d)", msg_str, msg_info.length, msg_info.channel_id); } -int SendEcho(const char *msg) { - NBN_Writer *writer = NBN_GameClient_CreateReliableMessage(ECHO_MESSAGE_TYPE); +int SendEcho(NBN_Client *client, const char *msg) { + NBN_Writer *writer = NBN_Client_CreateReliableMessage(client, ECHO_MESSAGE_TYPE); if (!writer) { return -1; @@ -120,9 +120,9 @@ int main(int argc, char *argv[]) { // Start the client with a protocol name (must be the same than the one used by the server) // the server host and port - NBN_GameClient_Init(ECHO_PROTOCOL_NAME, "127.0.0.1", ECHO_EXAMPLE_PORT); + NBN_Client *client = NBN_Client_Create(ECHO_PROTOCOL_NAME, "127.0.0.1", ECHO_EXAMPLE_PORT); - if (NBN_GameClient_Start() < 0) { + if (NBN_Client_Start(client) < 0) { log_error("Failed to start client"); // Error, quit the client application @@ -140,7 +140,7 @@ int main(int argc, char *argv[]) { int ev; // Poll for client events - while ((ev = NBN_GameClient_Poll()) != NBN_CLIENT_NO_EVENT) { + while ((ev = NBN_Client_Poll(client)) != NBN_CLIENT_NO_EVENT) { if (ev < 0) { log_error("An error occured while polling client events. Exit"); @@ -157,12 +157,12 @@ int main(int argc, char *argv[]) { // Client has disconnected from the server case NBN_CLIENT_DISCONNECTED: - OnDisconnected(); + OnDisconnected(client); break; // A message has been received from the server case NBN_CLIENT_MESSAGE_RECEIVED: - OnMessageReceived(); + OnMessageReceived(client); break; } } @@ -171,7 +171,7 @@ int main(int argc, char *argv[]) { break; if (connected) { - if (SendEcho(msg) < 0) { + if (SendEcho(client, msg) < 0) { log_error("Failed to send message. Exit"); // Stop main loop @@ -181,7 +181,7 @@ int main(int argc, char *argv[]) { } // Pack all enqueued messages as packets and send them - if (NBN_GameClient_Flush() < 0) { + if (NBN_Client_Flush(client) < 0) { log_error("Failed to send packets. Exit"); // Stop main loop @@ -194,7 +194,7 @@ int main(int argc, char *argv[]) { } // Stop and deinitialize the client - NBN_GameClient_Stop(); + NBN_Client_Stop(client); #ifdef NBN_WEBRTC_NATIVE NBN_WebRTC_C_Unregister(); diff --git a/examples/echo/server.c b/examples/echo/server.c index 9ddab01..1431479 100644 --- a/examples/echo/server.c +++ b/examples/echo/server.c @@ -30,9 +30,9 @@ static NBN_ConnectionHandle *connection = NULL; static NBN_Connection_ID conn_id; // Echo the received message -static int EchoReceivedMessage(void) { +static int EchoReceivedMessage(NBN_Server *server) { // Get info about the received message - NBN_MessageInfo msg_info = NBN_GameServer_GetMessageInfo(); + NBN_MessageInfo msg_info = NBN_Server_GetMessageInfo(server); assert(msg_info.type == ECHO_MESSAGE_TYPE); @@ -41,7 +41,7 @@ static int EchoReceivedMessage(void) { assert(msg_info.sender->id == conn_id); // read message data - NBN_Reader *reader = NBN_GameServer_ReadMessage(); + NBN_Reader *reader = NBN_Server_ReadMessage(server); unsigned int length; int res; @@ -57,7 +57,7 @@ static int EchoReceivedMessage(void) { msg_info.channel_id); // create and send an echo of the received message - NBN_Writer *writer = NBN_GameServer_CreateReliableMessage(ECHO_MESSAGE_TYPE, connection); + NBN_Writer *writer = NBN_Server_CreateReliableMessage(server, ECHO_MESSAGE_TYPE, connection); if (!writer) { return -1; @@ -116,9 +116,9 @@ int main(int argc, const char **argv) { // Start the server with a protocol name and a port - NBN_GameServer_Init(ECHO_PROTOCOL_NAME, ECHO_EXAMPLE_PORT); + NBN_Server *server = NBN_Server_Create(ECHO_PROTOCOL_NAME, ECHO_EXAMPLE_PORT); - if (NBN_GameServer_Start() < 0) { + if (NBN_Server_Start(server) < 0) { log_error("Failed to start the server"); // Error, quit the server application @@ -137,7 +137,7 @@ int main(int argc, const char **argv) { NBN_DisconnectionInfo disconnect_info; // Poll for server events - while ((ev = NBN_GameServer_Poll()) != NBN_SERVER_NO_EVENT) { + while ((ev = NBN_Server_Poll(server)) != NBN_SERVER_NO_EVENT) { if (ev < 0) { log_error("Something went wrong"); @@ -151,10 +151,10 @@ int main(int argc, const char **argv) { case NBN_SERVER_NEW_CONNECTION: // Echo server work with one single client at a time if (connection) { - NBN_GameServer_RejectIncomingConnectionWithCode(ECHO_SERVER_BUSY_CODE); + NBN_Server_RejectIncomingConnectionWithCode(server, ECHO_SERVER_BUSY_CODE); } else { - NBN_GameServer_AcceptIncomingConnection(); - connection = NBN_GameServer_GetIncomingConnection(); + NBN_Server_AcceptIncomingConnection(server); + connection = NBN_Server_GetIncomingConnection(server); conn_id = connection->id; } @@ -162,7 +162,7 @@ int main(int argc, const char **argv) { // The client has disconnected case NBN_SERVER_DISCONNECTION: - disconnect_info = NBN_GameServer_GetDisconnectionInfo(); + disconnect_info = NBN_Server_GetDisconnectionInfo(server); assert(disconnect_info.conn_id == conn_id); connection = NULL; @@ -170,7 +170,7 @@ int main(int argc, const char **argv) { // A message has been received from the client case NBN_SERVER_MESSAGE_RECEIVED: - if (EchoReceivedMessage() < 0) { + if (EchoReceivedMessage(server) < 0) { log_error("Failed to echo received message"); // Error, quit the server application @@ -185,7 +185,7 @@ int main(int argc, const char **argv) { } // Pack all enqueued messages as packets and send them - if (NBN_GameServer_Flush() < 0) { + if (NBN_Server_Flush(server) < 0) { log_error("Failed to send packets"); // Error, quit the server application @@ -198,7 +198,7 @@ int main(int argc, const char **argv) { } // Stop the server - NBN_GameServer_Stop(); + NBN_Server_Stop(server); #ifdef NBN_WEBRTC_NATIVE NBN_WebRTC_C_Unregister(); diff --git a/examples/raylib/client.c b/examples/raylib/client.c index 96f96f6..be24f34 100644 --- a/examples/raylib/client.c +++ b/examples/raylib/client.c @@ -62,8 +62,8 @@ Color client_colors_to_raylib_colors[] = { PINK // CLI_PINK }; -static void WriteConnectionRequestData(const char *name) { - NBN_Writer *writer = NBN_GameClient_WriteConnectionRequestData(); +static void WriteConnectionRequestData(NBN_Client *client, const char *name) { + NBN_Writer *writer = NBN_Client_WriteConnectionRequestData(client); // send the name of the client as the connection request data NBN_Writer_WriteString(writer, name, CLIENT_NAME_MAX_LEN); @@ -80,11 +80,11 @@ static void SpawnLocalClient(int x, int y, uint32_t client_id) { spawned = true; } -static int HandleConnection(void) { +static int HandleConnection(NBN_Client *client) { TraceLog(LOG_INFO, "Connected, reading connection data..."); uint32_t x, y, client_id; - NBN_Reader *reader = NBN_GameClient_ReadServerData(); + NBN_Reader *reader = NBN_Client_ReadServerData(client); if (NBN_Reader_ReadUInt32(reader, &x) < 0) { return -1; @@ -104,8 +104,8 @@ static int HandleConnection(void) { return 0; } -static void HandleDisconnection(void) { - int code = NBN_GameClient_GetServerCloseCode(); // Get the server code used when closing the client connection +static void HandleDisconnection(NBN_Client *client) { + int code = NBN_Client_GetServerCloseCode(client); // Get the server code used when closing the client connection TraceLog(LOG_INFO, "Disconnected from server (code: %d)", code); @@ -208,7 +208,7 @@ static void DestroyDisconnectedClients(void) { } } -static void HandleGameStateMessage(void) { +static void HandleGameStateMessage(NBN_Client *client) { if (!spawned) return; @@ -220,7 +220,7 @@ static void HandleGameStateMessage(void) { static GameState recv_game_state; // Read the game state from the received GAME_STATE_MESSAGE message - NBN_Reader *reader = NBN_GameClient_ReadMessage(); + NBN_Reader *reader = NBN_Client_ReadMessage(client); if (GameStateMessage_Read(reader, &recv_game_state) < 0) { TraceLog(LOG_ERROR, "Failed to read game state"); @@ -247,14 +247,14 @@ static void HandleGameStateMessage(void) { DestroyDisconnectedClients(); } -static void HandleReceivedMessage(void) { +static void HandleReceivedMessage(NBN_Client *client) { // Fetch info about the last received message - NBN_MessageInfo msg_info = NBN_GameClient_GetMessageInfo(); + NBN_MessageInfo msg_info = NBN_Client_GetMessageInfo(client); switch (msg_info.type) { // We received the latest game state from the server case GAME_STATE_MESSAGE: - HandleGameStateMessage(); + HandleGameStateMessage(client); return; } @@ -262,11 +262,11 @@ static void HandleReceivedMessage(void) { abort(); } -static void HandleGameClientEvent(int ev) { +static void HandleGameClientEvent(NBN_Client *client, int ev) { switch (ev) { case NBN_CLIENT_CONNECTED: // We are connected to the server - if (HandleConnection() < 0) { + if (HandleConnection(client) < 0) { TraceLog(LOG_ERROR, "Failed to handle connection"); abort(); } @@ -274,18 +274,18 @@ static void HandleGameClientEvent(int ev) { case NBN_CLIENT_DISCONNECTED: // The server has closed our connection - HandleDisconnection(); + HandleDisconnection(client); break; case NBN_CLIENT_MESSAGE_RECEIVED: // We received a message from the server - HandleReceivedMessage(); + HandleReceivedMessage(client); break; } } -static int SendStateUpdate(void) { - NBN_Writer *writer = NBN_GameClient_CreateUnreliableMessage(UPDATE_STATE_MESSAGE); +static int SendStateUpdate(NBN_Client *client) { + NBN_Writer *writer = NBN_Client_CreateUnreliableMessage(client, UPDATE_STATE_MESSAGE); if (!writer) { return -1; @@ -296,8 +296,8 @@ static int SendStateUpdate(void) { return 0; } -static int SendColorUpdate(void) { - NBN_Writer *writer = NBN_GameClient_CreateReliableMessage(CHANGE_COLOR_MESSAGE); +static int SendColorUpdate(NBN_Client *client) { + NBN_Writer *writer = NBN_Client_CreateReliableMessage(client, CHANGE_COLOR_MESSAGE); if (!writer) { return -1; @@ -310,7 +310,7 @@ static int SendColorUpdate(void) { bool color_key_pressed = false; -static int Update(void) { +static int Update(NBN_Client *client) { if (!spawned) return 0; @@ -332,7 +332,7 @@ static int Update(void) { TraceLog(LOG_INFO, "Switched color, new color: %d", local_client_state.color); - if (SendColorUpdate() < 0) { + if (SendColorUpdate(client) < 0) { TraceLog(LOG_WARNING, "Failed to send color update"); return -1; @@ -350,7 +350,7 @@ static int Update(void) { local_client_state.val = fmax(MIN_FLOAT_VAL, local_client_state.val - 0.005); // Send the latest local client state to the server - if (SendStateUpdate() < 0) { + if (SendStateUpdate(client) < 0) { TraceLog(LOG_WARNING, "Failed to send client state update"); return -1; @@ -374,8 +374,8 @@ void DrawClient(ClientState *state, bool is_local) { DrawRectangleLinesEx((Rectangle){state->x, state->y, 50, 50}, 3, DARKBROWN); } -void DrawHUD(void) { - NBN_ConnectionStats stats = NBN_GameClient_GetStats(); +void DrawHUD(NBN_Client *client) { + NBN_ConnectionStats stats = NBN_Client_GetStats(client); unsigned int ping = stats.ping * 1000; unsigned int packet_loss = stats.packet_loss * 100; @@ -386,7 +386,7 @@ void DrawHUD(void) { DrawText(TextFormat("Download: %.1f Bps", stats.download_bandwidth), 450, 550, 32, MAROON); } -void Draw(void) { +void Draw(NBN_Client *client) { BeginDrawing(); ClearBackground(LIGHTGRAY); @@ -410,7 +410,7 @@ void Draw(void) { DrawClient(&local_client_state, true); // Finally draw the HUD - DrawHUD(); + DrawHUD(client); } else { DrawText("Connecting to server...", 265, 280, 20, RED); } @@ -421,7 +421,7 @@ void Draw(void) { static double tick_dt = 1.0 / TICK_RATE; // Tick delta time (in seconds) static double acc = 0; -void UpdateAndDraw(void) { +void UpdateAndDraw(NBN_Client *client) { // Very basic fixed timestep implementation. // Target FPS is either 100 (in desktop) or whatever the browser frame rate is (in web) but the simulation runs at // TICK_RATE ticks per second. @@ -434,23 +434,23 @@ void UpdateAndDraw(void) { while (acc >= tick_dt) { int ev; - while ((ev = NBN_GameClient_Poll()) != NBN_CLIENT_NO_EVENT) { + while ((ev = NBN_Client_Poll(client)) != NBN_CLIENT_NO_EVENT) { if (ev < 0) { TraceLog(LOG_WARNING, "An occured while polling network events. Exit"); break; } - HandleGameClientEvent(ev); + HandleGameClientEvent(client, ev); } if (connected && !disconnected) { - if (Update() < 0) + if (Update(client) < 0) break; } if (!disconnected) { - if (NBN_GameClient_Flush() < 0) { + if (NBN_Client_Flush(client) < 0) { TraceLog(LOG_ERROR, "An occured while flushing the send queue. Exit"); break; @@ -460,7 +460,7 @@ void UpdateAndDraw(void) { acc -= tick_dt; // Consumes time } - Draw(); + Draw(client); } int main(int argc, char *argv[]) { @@ -494,27 +494,27 @@ int main(int argc, char *argv[]) { // Initialize the client with a protocol name, the server host and the server port // protocol name has to be the same as the one used by the server - NBN_GameClient_Init(RAYLIB_EXAMPLE_PROTOCOL_NAME, "127.0.0.1", RAYLIB_EXAMPLE_PORT); + NBN_Client *client = NBN_Client_Create(RAYLIB_EXAMPLE_PROTOCOL_NAME, "127.0.0.1", RAYLIB_EXAMPLE_PORT); - WriteConnectionRequestData(name); + WriteConnectionRequestData(client, name); // Start the client with the configuration - if (NBN_GameClient_Start() < 0) { + if (NBN_Client_Start(client) < 0) { TraceLog(LOG_WARNING, "Game client failed to start. Exit"); return 1; } // Network conditions simulated variables (read from the command line, default is always 0) - NBN_GameClient_SetPing(GetOptions().ping); - NBN_GameClient_SetJitter(GetOptions().jitter); - NBN_GameClient_SetPacketLoss(GetOptions().packet_loss); - NBN_GameClient_SetPacketDuplication(GetOptions().packet_duplication); + NBN_Client_SetPing(client, GetOptions().ping); + NBN_Client_SetJitter(client, GetOptions().jitter); + NBN_Client_SetPacketLoss(client, GetOptions().packet_loss); + NBN_Client_SetPacketDuplication(client, GetOptions().packet_duplication); // Main loop #ifdef __EMSCRIPTEN__ while (true) { - UpdateAndDraw(); + UpdateAndDraw(client); // Since we don't set any target FPS when running in a web browser we need to sleep for the correct amount // of time to achieve the targetted FPS @@ -522,12 +522,12 @@ int main(int argc, char *argv[]) { } #else while (!WindowShouldClose()) { - UpdateAndDraw(); + UpdateAndDraw(client); } #endif // Stop the client - NBN_GameClient_Stop(); + NBN_Client_Stop(client); CloseWindow(); diff --git a/examples/raylib/server.c b/examples/raylib/server.c index d35c318..b24978e 100644 --- a/examples/raylib/server.c +++ b/examples/raylib/server.c @@ -58,36 +58,36 @@ static unsigned int client_count = 0; static Vector2 spawns[] = {(Vector2){50, 50}, (Vector2){GAME_WIDTH - 100, 50}, (Vector2){50, GAME_HEIGHT - 100}, (Vector2){GAME_WIDTH - 100, GAME_HEIGHT - 100}}; -static void AcceptConnection(Vector2 spawn, NBN_ConnectionHandle *conn) { +static void AcceptConnection(NBN_Server *server, Vector2 spawn, NBN_ConnectionHandle *conn) { // Accept the connection with some data // this data can be read by the client upon processing the connection event - NBN_Writer *writer = NBN_GameServer_WriteConnectionData(); + NBN_Writer *writer = NBN_Server_WriteConnectionData(server); NBN_Writer_WriteUInt32(writer, (uint32_t)spawn.x); NBN_Writer_WriteUInt32(writer, (uint32_t)spawn.y); NBN_Writer_WriteUInt32(writer, conn->id); // TODO: id is 64bits - NBN_GameServer_AcceptIncomingConnection(); + NBN_Server_AcceptIncomingConnection(server); } -static int HandleNewConnection(void) { +static int HandleNewConnection(NBN_Server *server) { TraceLog(LOG_INFO, "New connection"); // If the server is full if (client_count == MAX_CLIENTS) { // Reject the connection (send a SERVER_FULL_CODE code to the client) TraceLog(LOG_INFO, "Connection rejected"); - NBN_GameServer_RejectIncomingConnectionWithCode(SERVER_FULL_CODE); + NBN_Server_RejectIncomingConnectionWithCode(server, SERVER_FULL_CODE); return 0; } // Otherwise... - NBN_ConnectionHandle *conn = NBN_GameServer_GetIncomingConnection(); + NBN_ConnectionHandle *conn = NBN_Server_GetIncomingConnection(server); // Read the connection request data transmitted by the client - NBN_Reader *reader = NBN_GameServer_ReadConnectionRequestData(); + NBN_Reader *reader = NBN_Server_ReadConnectionRequestData(server); char name[CLIENT_NAME_MAX_LEN]; if (NBN_Reader_ReadString(reader, name, sizeof(name)) < 0) { @@ -100,7 +100,7 @@ static int HandleNewConnection(void) { // Build some "initial" data that will be sent to the connected client - AcceptConnection(spawn, conn); + AcceptConnection(server, spawn, conn); TraceLog(LOG_INFO, "Connection accepted (ID: %d, name: %s)", conn->id, name); @@ -142,8 +142,8 @@ static void DestroyClient(Client *client) { free(client); } -static void HandleClientDisconnection(void) { - NBN_DisconnectionInfo info = NBN_GameServer_GetDisconnectionInfo(); +static void HandleClientDisconnection(NBN_Server *server) { + NBN_DisconnectionInfo info = NBN_Server_GetDisconnectionInfo(server); TraceLog(LOG_INFO, "Client has disconnected (id: %d, user data: %p)", info.conn_id, info.user_data); @@ -155,23 +155,23 @@ static void HandleClientDisconnection(void) { client_count--; } -static int HandleUpdateStateMessage(Client *sender) { +static int HandleUpdateStateMessage(NBN_Server *server, Client *sender) { // Update the state of the client with the data from the received UPDATE_STATE_MESSAGE message - NBN_Reader *reader = NBN_GameServer_ReadMessage(); + NBN_Reader *reader = NBN_Server_ReadMessage(server); return UpdateClientStateMessage_Read(reader, &sender->state); } -static int HandleChangeColorMessage(Client *sender) { +static int HandleChangeColorMessage(NBN_Server *server, Client *sender) { // Update the client color - NBN_Reader *reader = NBN_GameServer_ReadMessage(); + NBN_Reader *reader = NBN_Server_ReadMessage(server); return ChangeColorMessage_Read(reader, &sender->state.color); } -static int HandleReceivedMessage(void) { +static int HandleReceivedMessage(NBN_Server *server) { // Fetch info about the last received message - NBN_MessageInfo msg_info = NBN_GameServer_GetMessageInfo(); + NBN_MessageInfo msg_info = NBN_Server_GetMessageInfo(server); assert(msg_info.sender != NULL); Client *sender = msg_info.sender->user_data; assert(sender != NULL); @@ -179,33 +179,33 @@ static int HandleReceivedMessage(void) { switch (msg_info.type) { case UPDATE_STATE_MESSAGE: // The server received a client state update - return HandleUpdateStateMessage(sender); + return HandleUpdateStateMessage(server, sender); case CHANGE_COLOR_MESSAGE: // The server received a client switch color action - return HandleChangeColorMessage(sender); + return HandleChangeColorMessage(server, sender); } // Received an unexpected message return -1; } -static int HandleGameServerEvent(int ev) { +static int HandleGameServerEvent(NBN_Server *server, int ev) { switch (ev) { case NBN_SERVER_NEW_CONNECTION: // A new client has requested a connection - if (HandleNewConnection() < 0) + if (HandleNewConnection(server) < 0) return -1; break; case NBN_SERVER_DISCONNECTION: // A previously connected client has disconnected - HandleClientDisconnection(); + HandleClientDisconnection(server); break; case NBN_SERVER_MESSAGE_RECEIVED: // A message from a client has been received - if (HandleReceivedMessage() < 0) { + if (HandleReceivedMessage(server) < 0) { // TODO: kick client return -1; } @@ -216,7 +216,7 @@ static int HandleGameServerEvent(int ev) { } // Broadcasts the latest game state to all connected clients -static int BroadcastGameState(void) { +static int BroadcastGameState(NBN_Server *server) { static GameState game_state; unsigned int client_index = 0; @@ -246,8 +246,8 @@ static int BroadcastGameState(void) { NBN_ConnectionHandle *cli; // Broadcast GAME_STATE_MESSAGE to all clients - while ((cli = NBN_GameServer_GetNextClient(&it)) != NULL) { - NBN_Writer *writer = NBN_GameServer_CreateUnreliableMessage(GAME_STATE_MESSAGE, cli); + while ((cli = NBN_Server_GetNextClient(server, &it)) != NULL) { + NBN_Writer *writer = NBN_Server_CreateUnreliableMessage(server, GAME_STATE_MESSAGE, cli); GameStateMessage_Write(writer, &game_state); } @@ -281,20 +281,20 @@ int main(int argc, char *argv[]) { // Initialize the server with a protocol name and a port // protocol name has to match between the server and the clients - NBN_GameServer_Init(RAYLIB_EXAMPLE_PROTOCOL_NAME, RAYLIB_EXAMPLE_PORT); + NBN_Server *server = NBN_Server_Create(RAYLIB_EXAMPLE_PROTOCOL_NAME, RAYLIB_EXAMPLE_PORT); // Start the server with the configuration - if (NBN_GameServer_Start() < 0) { + if (NBN_Server_Start(server) < 0) { TraceLog(LOG_ERROR, "Game server failed to start. Exit"); return 1; } // Network conditions simulated variables (read from the command line, default is always 0) - NBN_GameServer_SetPing(GetOptions().ping); - NBN_GameServer_SetJitter(GetOptions().jitter); - NBN_GameServer_SetPacketLoss(GetOptions().packet_loss); - NBN_GameServer_SetPacketDuplication(GetOptions().packet_duplication); + NBN_Server_SetPing(server, GetOptions().ping); + NBN_Server_SetJitter(server, GetOptions().jitter); + NBN_Server_SetPacketLoss(server, GetOptions().packet_loss); + NBN_Server_SetPacketDuplication(server, GetOptions().packet_duplication); float tick_dt = 1.f / TICK_RATE; // Tick delta time @@ -302,31 +302,31 @@ int main(int argc, char *argv[]) { int ev; // Poll for server events - while ((ev = NBN_GameServer_Poll()) != NBN_SERVER_NO_EVENT) { + while ((ev = NBN_Server_Poll(server)) != NBN_SERVER_NO_EVENT) { if (ev < 0) { TraceLog(LOG_ERROR, "An occured while polling network events. Exit"); break; } - if (HandleGameServerEvent(ev) < 0) + if (HandleGameServerEvent(server, ev) < 0) break; } - if (BroadcastGameState() < 0) { + if (BroadcastGameState(server) < 0) { TraceLog(LOG_ERROR, "An occured while broadcasting game states. Exit"); break; } // Pack all enqueued messages as packets and send them - if (NBN_GameServer_Flush() < 0) { + if (NBN_Server_Flush(server) < 0) { TraceLog(LOG_ERROR, "An occured while flushing the send queue. Exit"); break; } - NBN_GameServerStats stats = NBN_GameServer_GetStats(); + NBN_ServerStats stats = NBN_Server_GetStats(server); TraceLog(LOG_TRACE, "Upload: %f Bps | Download: %f Bps", stats.upload_bandwidth, stats.download_bandwidth); @@ -344,7 +344,7 @@ int main(int argc, char *argv[]) { } // Stop the server - NBN_GameServer_Stop(); + NBN_Server_Stop(server); return 0; } diff --git a/nbnet.c b/nbnet.c index d70cd24..8e94f48 100644 --- a/nbnet.c +++ b/nbnet.c @@ -57,7 +57,6 @@ #ifndef NBN_PLATFORM_WINDOWS -#include #include #if _POSIX_C_SOURCE >= 199309L @@ -76,6 +75,36 @@ #endif // NBN_PLATFORM_WINDOWS +#ifdef NBN_UDP + +#if defined(NBN_PLATFORM_WINDOWS) + +#include + +typedef int socklen_t; + +#elif defined(NBN_PLATFORM_UNIX) || defined(NBN_PLATFORM_MAC) + +#include +#include +#include +#include +#include +#include + +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#define closesocket(s) close(s) + +typedef int SOCKET; +typedef struct sockaddr_in SOCKADDR_IN; +typedef struct sockaddr SOCKADDR; +typedef struct in_addr IN_ADDR; + +#endif // NBN_PLATFORM_WINDOWS + +#endif // NBN_UDP + typedef struct NBN_Connection NBN_Connection; typedef struct NBN_Endpoint NBN_Endpoint; typedef struct NBN_Channel NBN_Channel; @@ -257,7 +286,7 @@ struct NBN_Connection { uint32_t packet_recv_seq_buffer[NBN_MAX_PACKET_ENTRIES]; /* Driver-related data attached to the connection */ - union { + struct { #ifdef NBN_UDP struct { NBN_IPAddress ip_address; @@ -273,6 +302,8 @@ struct NBN_Connection { #endif // NBN_WEBRTC_NATIVE } webrtc; #endif // defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + + void *endpoint_ptr; } driver_data; }; @@ -424,7 +455,7 @@ typedef struct NBN_Server_Config { unsigned int channel_count; } NBN_Server_Config; -typedef struct NBN_Server { +struct NBN_Server { NBN_Endpoint endpoint; NBN_Server_Config config; struct { @@ -436,7 +467,24 @@ typedef struct NBN_Server { NBN_Event last_event; NBN_Writer server_data_writer; NBN_Reader client_data_reader; -} NBN_Server; + + struct { +#ifdef NBN_UDP + struct { + SOCKET sock; + } udp; +#endif + +#if defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + struct { + NBN_WebRTC_Config cfg; +#ifdef NBN_WEBRTC_NATIVE + int ws_server; +#endif // NBN_WEBRTC_NATIVE + } webrtc; +#endif // defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + } driver_data; +}; typedef struct NBN_Client_Config { const char *protocol_name; @@ -446,7 +494,7 @@ typedef struct NBN_Client_Config { unsigned int channel_count; } NBN_Client_Config; -typedef struct NBN_Client { +struct NBN_Client { NBN_Endpoint endpoint; NBN_Client_Config config; NBN_Connection *server_connection; @@ -455,14 +503,26 @@ typedef struct NBN_Client { int closed_code; NBN_Writer client_data_writer; NBN_Reader server_data_reader; -} NBN_Client; -static NBN_Server nbn_game_server; -static NBN_Client nbn_game_client; + struct { +#ifdef NBN_UDP + struct { + SOCKET sock; + } udp; +#endif + +#if defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + struct { + NBN_WebRTC_Config cfg; + bool is_connected; + } webrtc; +#endif // defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + } driver_data; +}; typedef int (*NBN_Driver_Func_ClientStart)(NBN_Client *, const char *, uint16_t); typedef void (*NBN_Driver_Func_ClientStop)(NBN_Client *); -typedef int (*NBN_Driver_Func_ClientSendPacket)(NBN_Client *, NBN_Packet *); +typedef int (*NBN_Driver_Func_ClientSendPacket)(NBN_Client *, NBN_Packet *, NBN_Connection *); typedef int (*NBN_Driver_Func_ClientRecvPackets)(NBN_Client *); typedef int (*NBN_Driver_Func_ServerStart)(NBN_Server *, uint16_t); @@ -496,36 +556,10 @@ struct NBN_Driver { #ifdef NBN_UDP -#if defined(NBN_PLATFORM_WINDOWS) - -#include - -typedef int socklen_t; - -#elif defined(NBN_PLATFORM_UNIX) || defined(NBN_PLATFORM_MAC) - -#include -#include -#include -#include -#include -#include - -#define INVALID_SOCKET -1 -#define SOCKET_ERROR -1 -#define closesocket(s) close(s) - -typedef int SOCKET; -typedef struct sockaddr_in SOCKADDR_IN; -typedef struct sockaddr SOCKADDR; -typedef struct in_addr IN_ADDR; - -#endif // NBN_PLATFORM_WINDOWS - static int UDP_Client_Start(NBN_Client *client, const char *host, uint16_t port); static void UDP_Client_Stop(NBN_Client *client); static int UDP_Client_RecvPackets(NBN_Client *client); -static int UDP_Client_SendPacket(NBN_Client *client, NBN_Packet *packet); +static int UDP_Client_SendPacket(NBN_Client *client, NBN_Packet *packet, NBN_Connection *connection); static int UDP_Server_Start(NBN_Server *server, uint16_t port); static void UDP_Server_Stop(NBN_Server *server); @@ -547,9 +581,6 @@ static NBN_Driver nbn_udp_driver = {.id = NBN_DRIVER_UDP, .serv_recv_packets = UDP_Server_RecvPackets, .serv_send_packet_to = UDP_Server_SendPacketTo, .serv_cleanup_connection = UDP_Server_CleanupConnection}}; -// TODO: remove global state -static SOCKET nbn_udp_sock; - #endif // NBN_UDP #ifdef __EMSCRIPTEN__ @@ -592,8 +623,6 @@ static NBN_Driver nbn_webrtc_em_driver = {.id = NBN_DRIVER_WEBRTC_EMSCRIPTEN, .serv_send_packet_to = WebRTC_Server_SendPacketTo, .serv_cleanup_connection = WebRTC_Server_CleanupConnection}}; -static NBN_WebRTC_Config nbn_wrtc_cfg = NBN_WEBRTC_DEFAULT_CONFIG; - #endif // __EMSCRIPTEN__ #ifdef NBN_WEBRTC_NATIVE @@ -601,7 +630,7 @@ static NBN_WebRTC_Config nbn_wrtc_cfg = NBN_WEBRTC_DEFAULT_CONFIG; static int WebRTC_Native_Client_Start(NBN_Client *client, const char *host, uint16_t port); static void WebRTC_Native_Client_Stop(NBN_Client *client); static int WebRTC_Native_Client_RecvPackets(NBN_Client *client); -static int WebRTC_Native_Client_SendPacket(NBN_Client *client, NBN_Packet *packet); +static int WebRTC_Native_Client_SendPacket(NBN_Client *client, NBN_Packet *packet, NBN_Connection *connection); static int WebRTC_Native_Server_Start(NBN_Server *server, uint16_t port); static void WebRTC_Native_Server_Stop(NBN_Server *server); @@ -625,8 +654,6 @@ static NBN_Driver nbn_webrtc_native_driver = { .serv_send_packet_to = WebRTC_Native_Server_SendPacketTo, .serv_cleanup_connection = WebRTC_Native_Server_CleanupConnection}}; -static NBN_WebRTC_Config nbn_wrtc_cfg = NBN_WEBRTC_DEFAULT_CONFIG; - #endif // NBN_WEBRTC_NATIVE /** @@ -1269,7 +1296,7 @@ static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *, u static bool Connection_InsertReceivedPacketEntry(NBN_Connection *, uint16_t); static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *, uint16_t); static bool Connection_IsPacketReceived(NBN_Connection *, uint16_t); -static int Connection_SendPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry *, double, bool); +static int Connection_SendPacket(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, NBN_PacketEntry *, double, bool); static int Connection_ReadNextMessageHeader(NBN_Reader *, NBN_MessageHeader *); static void Connection_UpdateAveragePing(NBN_Connection *, double); static void Connection_UpdateAveragePacketLoss(NBN_Connection *, uint16_t); @@ -1387,7 +1414,7 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn if (ret == NBN_PACKET_WRITE_OK) { message_sent = true; } else if (ret == NBN_PACKET_WRITE_NO_SPACE) { - if (Connection_SendPacket(connection, &packet, packet_entry, time, endpoint->is_server) < 0) { + if (Connection_SendPacket(endpoint, connection, &packet, packet_entry, time, endpoint->is_server) < 0) { LogError("Failed to send packet %d", packet.header.seq_number); return NBN_ERROR; @@ -1429,7 +1456,7 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn } } - if (Connection_SendPacket(connection, &packet, packet_entry, time, endpoint->is_server) < 0) { + if (Connection_SendPacket(endpoint, connection, &packet, packet_entry, time, endpoint->is_server) < 0) { LogError("Failed to send packet %d to connection %lld", packet.header.seq_number, connection->handle.id); return NBN_ERROR; @@ -1597,8 +1624,8 @@ static bool Connection_IsPacketReceived(NBN_Connection *connection, uint16_t pac return connection->packet_recv_seq_buffer[index] == packet_seq_number; } -static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, NBN_PacketEntry *packet_entry, - double time, bool is_server) { +static int Connection_SendPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, + NBN_PacketEntry *packet_entry, double time, bool is_server) { LogDebug("Send packet %d to connection %lld (messages count: %d)", packet->header.seq_number, connection->handle.id, packet->header.messages_count); @@ -1614,20 +1641,18 @@ static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, if (is_server) { #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - return PacketSimulator_EnqueuePacket(&nbn_game_server.endpoint.packet_simulator, packet, connection); + return PacketSimulator_EnqueuePacket(&endpoint->packet_simulator, packet, connection); #else if (connection->is_stale) return 0; - return connection->driver->impl.serv_send_packet_to(&nbn_game_server, packet, connection); + return connection->driver->impl.serv_send_packet_to(endpoint, packet, connection); #endif } else { #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - return PacketSimulator_EnqueuePacket(&nbn_game_client.endpoint.packet_simulator, packet, connection); + return PacketSimulator_EnqueuePacket(&endpoint->packet_simulator, packet, connection); #else - NBN_Driver *driver = nbn_game_client.server_connection->driver; - - return driver->impl.cli_send_packet(&nbn_game_client, packet); + return connection->driver->impl.cli_send_packet(endpoint, packet, connection); #endif } } @@ -1881,23 +1906,33 @@ static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) { * ====== CLIENT ====== */ -static int Client_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); -static NBN_Client_Event Client_HandleEvent(void); -static NBN_Client_Event Client_HandleMessageReceivedEvent(void); -static NBN_Connection *CreateServerConnection(NBN_Driver_ID driver_id); +static int Client_ProcessReceivedMessage(NBN_Client *, NBN_Message *, NBN_Connection *); +static NBN_Client_Event Client_HandleEvent(NBN_Client *); +static NBN_Client_Event Client_HandleMessageReceivedEvent(NBN_Client *); +static NBN_Connection *CreateServerConnection(NBN_Client *client, NBN_Driver_ID driver_id); + +NBN_Client *NBN_Client_Create(const char *protocol_name, const char *host, uint16_t port) { + NBN_Client *client = malloc(sizeof(NBN_Client)); -void NBN_Client_Init(const char *protocol_name, const char *host, uint16_t port) { - nbn_game_client.config = (NBN_Client_Config){.protocol_name = protocol_name, .host = host, .port = port}; - nbn_game_client.client_data_writer.position = 0; + client->config = (NBN_Client_Config){.protocol_name = protocol_name, .host = host, .port = port}; + client->client_data_writer.position = 0; - nbn_game_client.endpoint.default_reliable_channel = NBN_Client_CreateChannel( - NBN_CHANNEL_RELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); - nbn_game_client.endpoint.default_unreliable_channel = NBN_Client_CreateChannel( - NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); + client->endpoint.default_reliable_channel = NBN_Client_CreateChannel( + client, NBN_CHANNEL_RELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); + client->endpoint.default_unreliable_channel = NBN_Client_CreateChannel( + client, NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); + +#if defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + client->driver_data.webrtc.cfg = NBN_WEBRTC_DEFAULT_CONFIG; + client->driver_data.webrtc.is_connected = false; +#endif + + return client; } -uint8_t NBN_Client_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len) { - NBN_Client_Config *cfg = &nbn_game_client.config; +uint8_t NBN_Client_CreateChannel(NBN_Client *client, NBN_Channel_Mode mode, unsigned int buffer_size, + unsigned int max_message_len) { + NBN_Client_Config *cfg = &client->config; NBN_Assert(cfg->channel_count < NBN_MAX_CHANNEL_COUNT); @@ -1910,28 +1945,28 @@ uint8_t NBN_Client_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size return channel_id; } -unsigned int NBN_Client_GetChannelCurrentCapacity(uint8_t channel_id) { - NBN_Assert(channel_id < nbn_game_client.endpoint.channel_count); +unsigned int NBN_Client_GetChannelCurrentCapacity(NBN_Client *client, uint8_t channel_id) { + NBN_Assert(channel_id < client->endpoint.channel_count); - NBN_Channel *channel = &nbn_game_client.server_connection->channels[channel_id]; + NBN_Channel *channel = &client->server_connection->channels[channel_id]; return channel->current_capacity; } -NBN_Writer *NBN_Client_WriteConnectionRequestData(void) { - NBN_Writer_Init(&nbn_game_client.client_data_writer, nbn_game_client.endpoint.connection_request_data_buffer, - sizeof(nbn_game_client.endpoint.connection_request_data_buffer)); +NBN_Writer *NBN_Client_WriteConnectionRequestData(NBN_Client *client) { + NBN_Writer_Init(&client->client_data_writer, client->endpoint.connection_request_data_buffer, + sizeof(client->endpoint.connection_request_data_buffer)); - return &nbn_game_client.client_data_writer; + return &client->client_data_writer; } -static int StartClientDrivers(const char *host, uint16_t port) { +static int StartClientDrivers(NBN_Client *client, const char *host, uint16_t port) { int driver_count = 0; #ifdef NBN_UDP - nbn_game_client.server_connection = CreateServerConnection(NBN_DRIVER_UDP); + client->server_connection = CreateServerConnection(client, NBN_DRIVER_UDP); - if (nbn_udp_driver.impl.cli_start(&nbn_game_client, host, port) < 0) { + if (nbn_udp_driver.impl.cli_start(client, host, port) < 0) { LogError("Failed to start driver %s", nbn_udp_driver.name); return NBN_ERROR; } @@ -1941,9 +1976,9 @@ static int StartClientDrivers(const char *host, uint16_t port) { #endif // NBN_UDP #ifdef NBN_WEBRTC_NATIVE - nbn_game_client.server_connection = CreateServerConnection(NBN_DRIVER_WEBRTC_NATIVE); + client->server_connection = CreateServerConnection(client, NBN_DRIVER_WEBRTC_NATIVE); - if (nbn_webrtc_native_driver.impl.cli_start(&nbn_game_client, host, port) < 0) { + if (nbn_webrtc_native_driver.impl.cli_start(client, host, port) < 0) { LogError("Failed to start driver %s", nbn_webrtc_native_driver.name); return NBN_ERROR; } @@ -1953,9 +1988,9 @@ static int StartClientDrivers(const char *host, uint16_t port) { #endif // NBN_WEBRTC_NATIVE #ifdef __EMSCRIPTEN__ - nbn_game_client.server_connection = CreateServerConnection(NBN_DRIVER_WEBRTC_EMSCRIPTEN); + client->server_connection = CreateServerConnection(client, NBN_DRIVER_WEBRTC_EMSCRIPTEN); - if (nbn_webrtc_em_driver.impl.cli_start(&nbn_game_client, host, port) < 0) { + if (nbn_webrtc_em_driver.impl.cli_start(client, host, port) < 0) { LogError("Failed to start driver %s", nbn_webrtc_em_driver.name); return NBN_ERROR; } @@ -1964,19 +1999,21 @@ static int StartClientDrivers(const char *host, uint16_t port) { driver_count++; #endif // __EMSCRIPTEN__ + client->server_connection->driver_data.endpoint_ptr = client; + return driver_count; } -int NBN_Client_Start(void) { - NBN_Client_Config config = nbn_game_client.config; +int NBN_Client_Start(NBN_Client *client) { + NBN_Client_Config config = client->config; const char *protocol_name = config.protocol_name; const char *host = config.host; uint16_t port = config.port; uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - Endpoint_Init(&nbn_game_client.endpoint, protocol_id, false, config.channels, config.channel_count); + Endpoint_Init(&client->endpoint, protocol_id, false, config.channels, config.channel_count); - int driver_count = StartClientDrivers(host, port); + int driver_count = StartClientDrivers(client, host, port); if (driver_count < 1) { LogError("At least one network driver has to be activated"); @@ -1986,22 +2023,22 @@ int NBN_Client_Start(void) { NBN_Abort(); } - nbn_game_client.is_connected = false; - nbn_game_client.closed_code = -1; + client->is_connected = false; + client->closed_code = -1; - unsigned int connection_data_len = nbn_game_client.client_data_writer.position; + unsigned int connection_data_len = client->client_data_writer.position; - NBN_Writer *writer = NBN_Client_CreateReliableMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE); + NBN_Writer *writer = NBN_Client_CreateReliableMessage(client, NBN_CONNECTION_REQUEST_MESSAGE_TYPE); if (!writer) { return NBN_ERROR; } if (connection_data_len > 0) { - NBN_Assert(connection_data_len <= sizeof(nbn_game_client.endpoint.connection_request_data_buffer)); + NBN_Assert(connection_data_len <= sizeof(client->endpoint.connection_request_data_buffer)); NBN_Writer_WriteUInt32(writer, connection_data_len); - NBN_Writer_WriteBytes(writer, nbn_game_client.endpoint.connection_request_data_buffer, connection_data_len); + NBN_Writer_WriteBytes(writer, client->endpoint.connection_request_data_buffer, connection_data_len); } else { NBN_Writer_WriteUInt32(writer, 0); } @@ -2011,81 +2048,82 @@ int NBN_Client_Start(void) { return 0; } -void NBN_Client_Stop(void) { +void NBN_Client_Stop(NBN_Client *client) { // Poll remaining events to clear the event queue - while (NBN_Client_Poll() != NBN_CLIENT_NO_EVENT) { + while (NBN_Client_Poll(client) != NBN_CLIENT_NO_EVENT) { } - if (nbn_game_client.server_connection) { - if (!nbn_game_client.server_connection->is_closed && !nbn_game_client.server_connection->is_stale) { + if (client->server_connection) { + if (!client->server_connection->is_closed && !client->server_connection->is_stale) { LogInfo("Disconnecting..."); - if (!NBN_Client_CreateReliableMessage(NBN_DISCONNECTION_MESSAGE_TYPE)) { + if (!NBN_Client_CreateReliableMessage(client, NBN_DISCONNECTION_MESSAGE_TYPE)) { LogError("Failed to send disconnection message"); } - if (NBN_Client_Flush() < 0) { + if (NBN_Client_Flush(client) < 0) { LogError("Failed to send packets"); } - nbn_game_client.server_connection->is_closed = true; + client->server_connection->is_closed = true; LogInfo("Disconnected"); } - Connection_Destroy(nbn_game_client.server_connection); - nbn_game_client.server_connection = NULL; + Connection_Destroy(client->server_connection); + client->server_connection = NULL; } LogInfo("Stopping all drivers..."); #ifdef NBN_UDP - nbn_udp_driver.impl.cli_stop(&nbn_game_client); + nbn_udp_driver.impl.cli_stop(client); #endif // NBN_UDP #ifdef NBN_WEBRTC_NATIVE - nbn_webrtc_native_driver.impl.cli_stop(&nbn_game_client); + nbn_webrtc_native_driver.impl.cli_stop(client); #endif // NBN_WEBRTC_NATIVE #ifdef __EMSCRIPTEN__ - nbn_webrtc_em_driver.impl.cli_stop(&nbn_game_client); + nbn_webrtc_em_driver.impl.cli_stop(client); #endif // __EMSCRIPTEN__ - nbn_game_client.is_connected = false; - nbn_game_client.closed_code = -1; - nbn_game_client.endpoint.server_initial_data_len = 0; + client->is_connected = false; + client->closed_code = -1; + client->endpoint.server_initial_data_len = 0; - Endpoint_Deinit(&nbn_game_client.endpoint); + Endpoint_Deinit(&client->endpoint); + free(client); LogInfo("Stopped"); } -NBN_Reader *NBN_Client_ReadServerData(void) { - NBN_Endpoint *endpoint = &nbn_game_client.endpoint; +NBN_Reader *NBN_Client_ReadServerData(NBN_Client *client) { + NBN_Endpoint *endpoint = &client->endpoint; - NBN_Reader_Init(&nbn_game_client.server_data_reader, endpoint->server_initial_data_buffer, + NBN_Reader_Init(&client->server_data_reader, endpoint->server_initial_data_buffer, endpoint->server_initial_data_len); - return &nbn_game_client.server_data_reader; + return &client->server_data_reader; } -static int ReadPacketsFromClientDrivers(void) { +static int ReadPacketsFromClientDrivers(NBN_Client *client) { #ifdef NBN_UDP - if (nbn_udp_driver.impl.cli_recv_packets(&nbn_game_client) < 0) { + if (nbn_udp_driver.impl.cli_recv_packets(client) < 0) { LogError("Failed to read packets from driver %s", nbn_udp_driver.name); return NBN_ERROR; } #endif // NBN_UDP #ifdef NBN_WEBRTC_NATIVE - if (nbn_webrtc_native_driver.impl.cli_recv_packets(&nbn_game_client) < 0) { + if (nbn_webrtc_native_driver.impl.cli_recv_packets(client) < 0) { LogError("Failed to read packets from driver %s", nbn_webrtc_native_driver.name); return NBN_ERROR; } #endif // NBN_WEBRTC_NATIVE #ifdef __EMSCRIPTEN__ - if (nbn_webrtc_em_driver.impl.cli_recv_packets(&nbn_game_client) < 0) { + if (nbn_webrtc_em_driver.impl.cli_recv_packets(client) < 0) { LogError("Failed to read packets from driver %s", nbn_webrtc_em_driver.name); return NBN_ERROR; } @@ -2094,18 +2132,18 @@ static int ReadPacketsFromClientDrivers(void) { return 0; } -NBN_Client_Event NBN_Client_Poll(void) { - NBN_Endpoint *endpoint = &nbn_game_client.endpoint; +NBN_Client_Event NBN_Client_Poll(NBN_Client *client) { + NBN_Endpoint *endpoint = &client->endpoint; Endpoint_UpdateTime(endpoint); - if (nbn_game_client.server_connection->is_stale) + if (client->server_connection->is_stale) return NBN_CLIENT_NO_EVENT; if (EventQueue_IsEmpty(&endpoint->event_queue)) { - if (Connection_CheckIfStale(nbn_game_client.server_connection, nbn_game_client.endpoint.time)) { - nbn_game_client.server_connection->is_stale = true; - nbn_game_client.is_connected = false; + if (Connection_CheckIfStale(client->server_connection, client->endpoint.time)) { + client->server_connection->is_stale = true; + client->is_connected = false; LogInfo("Server connection is stale. Disconnected."); @@ -2117,11 +2155,11 @@ NBN_Client_Event NBN_Client_Poll(void) { if (!EventQueue_Enqueue(&endpoint->event_queue, e)) return NBN_ERROR; } else { - if (ReadPacketsFromClientDrivers() < 0) { + if (ReadPacketsFromClientDrivers(client) < 0) { return NBN_ERROR; } - NBN_Connection *server_conn = nbn_game_client.server_connection; + NBN_Connection *server_conn = client->server_connection; for (unsigned int i = 0; i < endpoint->channel_count; i++) { NBN_Channel *channel = &server_conn->channels[i]; @@ -2131,7 +2169,7 @@ NBN_Client_Event NBN_Client_Poll(void) { LogDebug("Got message %d of type %d from channel %d", msg->header.id, msg->header.type, channel->id); - if (Client_ProcessReceivedMessage(msg, server_conn) < 0) { + if (Client_ProcessReceivedMessage(client, msg, server_conn) < 0) { LogError("Failed to process received message"); return NBN_ERROR; @@ -2139,68 +2177,67 @@ NBN_Client_Event NBN_Client_Poll(void) { } } - Connection_UpdateAverageDownloadBandwidth(server_conn, nbn_game_client.endpoint.time); + Connection_UpdateAverageDownloadBandwidth(server_conn, client->endpoint.time); - server_conn->last_read_packets_time = nbn_game_client.endpoint.time; + server_conn->last_read_packets_time = client->endpoint.time; } } - bool ret = EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_client.last_event); + bool ret = EventQueue_Dequeue(&endpoint->event_queue, &client->last_event); - return ret ? Client_HandleEvent() : NBN_CLIENT_NO_EVENT; + return ret ? Client_HandleEvent(client) : NBN_CLIENT_NO_EVENT; } -int NBN_Client_Flush(void) { - return Connection_FlushChannels(&nbn_game_client.endpoint, nbn_game_client.server_connection, - nbn_game_client.endpoint.protocol_id, nbn_game_client.endpoint.time); +int NBN_Client_Flush(NBN_Client *client) { + return Connection_FlushChannels(&client->endpoint, client->server_connection, client->endpoint.protocol_id, + client->endpoint.time); } -NBN_Writer *NBN_Client_CreateMessage(uint8_t type, uint8_t channel_id) { - return Endpoint_CreateOutgoingMessage(&nbn_game_client.endpoint, nbn_game_client.server_connection, type, - channel_id); +NBN_Writer *NBN_Client_CreateMessage(NBN_Client *client, uint8_t type, uint8_t channel_id) { + return Endpoint_CreateOutgoingMessage(&client->endpoint, client->server_connection, type, channel_id); } -NBN_Writer *NBN_Client_CreateReliableMessage(uint8_t type) { - return NBN_Client_CreateMessage(type, nbn_game_client.endpoint.default_reliable_channel); +NBN_Writer *NBN_Client_CreateReliableMessage(NBN_Client *client, uint8_t type) { + return NBN_Client_CreateMessage(client, type, client->endpoint.default_reliable_channel); } -NBN_Writer *NBN_Client_CreateUnreliableMessage(uint8_t type) { - return NBN_Client_CreateMessage(type, nbn_game_client.endpoint.default_unreliable_channel); +NBN_Writer *NBN_Client_CreateUnreliableMessage(NBN_Client *client, uint8_t type) { + return NBN_Client_CreateMessage(client, type, client->endpoint.default_unreliable_channel); } -NBN_Reader *NBN_Client_ReadMessage(void) { - NBN_Assert(nbn_game_client.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); +NBN_Reader *NBN_Client_ReadMessage(NBN_Client *client) { + NBN_Assert(client->last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); - NBN_MessageInfo msg_info = nbn_game_client.last_event.data.message_info; - NBN_Reader *reader = &nbn_game_client.endpoint.message_reader; + NBN_MessageInfo msg_info = client->last_event.data.message_info; + NBN_Reader *reader = &client->endpoint.message_reader; NBN_Reader_Init(reader, msg_info.data, msg_info.length); return reader; } -static NBN_Connection *CreateServerConnection(NBN_Driver_ID driver_id) { - NBN_Connection *server_connection = Endpoint_CreateConnection(&nbn_game_client.endpoint, 0, driver_id); +static NBN_Connection *CreateServerConnection(NBN_Client *client, NBN_Driver_ID driver_id) { + NBN_Connection *server_connection = Endpoint_CreateConnection(&client->endpoint, 0, driver_id); - nbn_game_client.server_connection = server_connection; + client->server_connection = server_connection; return server_connection; } -NBN_MessageInfo NBN_Client_GetMessageInfo(void) { - NBN_Assert(nbn_game_client.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); +NBN_MessageInfo NBN_Client_GetMessageInfo(NBN_Client *client) { + NBN_Assert(client->last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); - return nbn_game_client.last_event.data.message_info; + return client->last_event.data.message_info; } -NBN_ConnectionStats NBN_Client_GetStats(void) { return nbn_game_client.server_connection->stats; } +NBN_ConnectionStats NBN_Client_GetStats(NBN_Client *client) { return client->server_connection->stats; } -int NBN_Client_GetServerCloseCode(void) { return nbn_game_client.closed_code; } +int NBN_Client_GetServerCloseCode(NBN_Client *client) { return client->closed_code; } -bool NBN_Client_IsConnected(void) { return nbn_game_client.is_connected; } +bool NBN_Client_IsConnected(NBN_Client *client) { return client->is_connected; } -static int Client_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *server_connection) { - NBN_Assert(nbn_game_client.server_connection == server_connection); +static int Client_ProcessReceivedMessage(NBN_Client *client, NBN_Message *message, NBN_Connection *server_connection) { + NBN_Assert(client->server_connection == server_connection); NBN_Event ev; @@ -2216,33 +2253,33 @@ static int Client_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *s ev.data.message_info = msg_info; - if (!EventQueue_Enqueue(&nbn_game_client.endpoint.event_queue, ev)) + if (!EventQueue_Enqueue(&client->endpoint.event_queue, ev)) return NBN_ERROR; return 0; } -static NBN_Client_Event Client_HandleEvent(void) { - switch (nbn_game_client.last_event.type) { +static NBN_Client_Event Client_HandleEvent(NBN_Client *client) { + switch (client->last_event.type) { case NBN_CLIENT_MESSAGE_RECEIVED: - return Client_HandleMessageReceivedEvent(); + return Client_HandleMessageReceivedEvent(client); default: - return nbn_game_client.last_event.type; + return client->last_event.type; } } -static NBN_Client_Event Client_HandleMessageReceivedEvent(void) { - NBN_MessageInfo message_info = nbn_game_client.last_event.data.message_info; - NBN_Endpoint *endpoint = &nbn_game_client.endpoint; +static NBN_Client_Event Client_HandleMessageReceivedEvent(NBN_Client *client) { + NBN_MessageInfo message_info = client->last_event.data.message_info; + NBN_Endpoint *endpoint = &client->endpoint; int ret = NBN_CLIENT_NO_EVENT; if (message_info.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE) { - nbn_game_client.is_connected = false; - NBN_Reader *reader = NBN_Client_ReadMessage(); + client->is_connected = false; + NBN_Reader *reader = NBN_Client_ReadMessage(client); - if (NBN_Reader_ReadInt32(reader, &nbn_game_client.closed_code) < 0) { + if (NBN_Reader_ReadInt32(reader, &client->closed_code) < 0) { LogError("Failed to read code from client closed message"); return NBN_ERROR; @@ -2256,7 +2293,7 @@ static NBN_Client_Event Client_HandleMessageReceivedEvent(void) { return NBN_ERROR; } - NBN_Reader *reader = NBN_Client_ReadMessage(); + NBN_Reader *reader = NBN_Client_ReadMessage(client); unsigned int data_length; if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) { @@ -2280,7 +2317,7 @@ static NBN_Client_Event Client_HandleMessageReceivedEvent(void) { } endpoint->server_initial_data_len = data_length; - nbn_game_client.is_connected = true; + client->is_connected = true; ret = NBN_CLIENT_CONNECTED; } else { ret = NBN_CLIENT_MESSAGE_RECEIVED; @@ -2289,8 +2326,8 @@ static NBN_Client_Event Client_HandleMessageReceivedEvent(void) { return ret; } -static void ClientDriver_OnPacketReceived(NBN_Packet *packet) { - if (Endpoint_ProcessReceivedPacket(&nbn_game_client.endpoint, packet, nbn_game_client.server_connection) < 0) { +static void ClientDriver_OnPacketReceived(NBN_Client *client, NBN_Packet *packet) { + if (Endpoint_ProcessReceivedPacket(&client->endpoint, packet, client->server_connection) < 0) { // packets from the server should always be valid LogError("Received invalid packet from server"); NBN_Abort(); @@ -2304,29 +2341,39 @@ static void ClientDriver_OnPacketReceived(NBN_Packet *packet) { * ====== SERVER ====== */ -static void Server_AddClient(NBN_Connection *); -static int Server_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection); -static void Server_AddClientToClosedList(NBN_Connection *client); -static int Server_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); -static int Server_CloseStaleClientConnections(void); -static void Server_RemoveClosedClientConnections(void); -static bool Server_HandleEvent(NBN_Server_Event *ev); -static bool Server_HandleMessageReceivedEvent(NBN_Server_Event *ev); +static void Server_AddClient(NBN_Server *, NBN_Connection *); +static int Server_CloseClientWithCode(NBN_Server *, NBN_Connection *, int, bool); +static void Server_AddClientToClosedList(NBN_Server *, NBN_Connection *); +static int Server_ProcessReceivedMessage(NBN_Server *, NBN_Message *, NBN_Connection *); +static int Server_CloseStaleClientConnections(NBN_Server *); +static void Server_RemoveClosedClientConnections(NBN_Server *); +static bool Server_HandleEvent(NBN_Server *, NBN_Server_Event *); +static bool Server_HandleMessageReceivedEvent(NBN_Server *, NBN_Server_Event *); -void NBN_Server_Init(const char *protocol_name, uint16_t port) { - nbn_game_server.config = (NBN_Server_Config){.protocol_name = protocol_name, .port = port}; +NBN_Server *NBN_Server_Create(const char *protocol_name, uint16_t port) { + NBN_Server *server = malloc(sizeof(NBN_Server)); - nbn_game_server.server_data_writer.position = 0; - hmdefault(nbn_game_server.clients, NULL); + server->config = (NBN_Server_Config){.protocol_name = protocol_name, .port = port}; - nbn_game_server.endpoint.default_reliable_channel = NBN_Server_CreateChannel( - NBN_CHANNEL_RELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); - nbn_game_server.endpoint.default_unreliable_channel = NBN_Server_CreateChannel( - NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); + server->server_data_writer.position = 0; + hmdefault(server->clients, NULL); + + server->endpoint.default_reliable_channel = NBN_Server_CreateChannel( + server, NBN_CHANNEL_RELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); + server->endpoint.default_unreliable_channel = NBN_Server_CreateChannel( + server, NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); + +#if defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + server->driver_data.webrtc.ws_server = -1; + server->driver_data.webrtc.cfg = NBN_WEBRTC_DEFAULT_CONFIG; +#endif // defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + + return server; } -uint8_t NBN_Server_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len) { - NBN_Server_Config *cfg = &nbn_game_server.config; +uint8_t NBN_Server_CreateChannel(NBN_Server *server, NBN_Channel_Mode mode, unsigned int buffer_size, + unsigned int max_message_len) { + NBN_Server_Config *cfg = &server->config; NBN_Assert(cfg->channel_count < NBN_MAX_CHANNEL_COUNT); @@ -2339,19 +2386,19 @@ uint8_t NBN_Server_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size return channel_id; } -unsigned int NBN_Server_GetChannelCurrentCapacity(uint8_t channel_id, NBN_ConnectionHandle *conn) { - NBN_Assert(channel_id < nbn_game_server.endpoint.channel_count); +unsigned int NBN_Server_GetChannelCurrentCapacity(NBN_Server *server, uint8_t channel_id, NBN_ConnectionHandle *conn) { + NBN_Assert(channel_id < server->endpoint.channel_count); NBN_Channel *channel = &HANDLE_TO_CONN(conn)->channels[channel_id]; return channel->current_capacity; } -static int StartServerDrivers(uint16_t port) { +static int StartServerDrivers(NBN_Server *server, uint16_t port) { int driver_count = 0; #ifdef NBN_UDP - if (nbn_udp_driver.impl.serv_start(&nbn_game_server, port) < 0) { + if (nbn_udp_driver.impl.serv_start(server, port) < 0) { LogError("Failed to start driver %s", nbn_udp_driver.name); return NBN_ERROR; } @@ -2361,7 +2408,7 @@ static int StartServerDrivers(uint16_t port) { #endif // NBN_UDP #ifdef NBN_WEBRTC_NATIVE - if (nbn_webrtc_native_driver.impl.serv_start(&nbn_game_server, port) < 0) { + if (nbn_webrtc_native_driver.impl.serv_start(server, port) < 0) { LogError("Failed to start driver %s", nbn_webrtc_native_driver.name); return NBN_ERROR; } @@ -2371,7 +2418,7 @@ static int StartServerDrivers(uint16_t port) { #endif // NBN_WEBRTC_NATIVE #ifdef __EMSCRIPTEN__ - if (nbn_webrtc_em_driver.impl.serv_start(&nbn_game_server, port) < 0) { + if (nbn_webrtc_em_driver.impl.serv_start(server, port) < 0) { LogError("Failed to start driver %s", nbn_webrtc_em_driver.name); return NBN_ERROR; } @@ -2383,56 +2430,56 @@ static int StartServerDrivers(uint16_t port) { return driver_count; } -int NBN_Server_Start(void) { - NBN_Server_Config config = nbn_game_server.config; +int NBN_Server_Start(NBN_Server *server) { + NBN_Server_Config config = server->config; const char *protocol_name = config.protocol_name; uint16_t port = config.port; uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); - Endpoint_Init(&nbn_game_server.endpoint, protocol_id, true, config.channels, config.channel_count); + Endpoint_Init(&server->endpoint, protocol_id, true, config.channels, config.channel_count); - nbn_game_server.closed_clients_head = NULL; + server->closed_clients_head = NULL; - int driver_count = StartServerDrivers(port); + int driver_count = StartServerDrivers(server, port); if (driver_count < 1) { LogError("At least one network driver has to be activated"); NBN_Abort(); } - LogInfo("Started (channel count: %d)", nbn_game_server.endpoint.channel_count); + LogInfo("Started (channel count: %d)", server->endpoint.channel_count); return 0; } -void NBN_Server_Stop(void) { +void NBN_Server_Stop(NBN_Server *server) { // Poll remaning events to clear the event queue - while (NBN_Server_Poll() != NBN_SERVER_NO_EVENT) { + while (NBN_Server_Poll(server) != NBN_SERVER_NO_EVENT) { } - for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { - NBN_Connection *conn = nbn_game_server.clients[i].value; + for (unsigned int i = 0; i < hmlen(server->clients); i++) { + NBN_Connection *conn = server->clients[i].value; - conn->driver->impl.serv_cleanup_connection(&nbn_game_server, conn); + conn->driver->impl.serv_cleanup_connection(server, conn); Connection_Destroy(conn); } - hmfree(nbn_game_server.clients); + hmfree(server->clients); #ifdef NBN_UDP - nbn_udp_driver.impl.serv_stop(&nbn_game_server); + nbn_udp_driver.impl.serv_stop(server); #endif // NBN_UDP #ifdef NBN_WEBRTC_NATIVE - nbn_webrtc_native_driver.impl.serv_stop(&nbn_game_server); + nbn_webrtc_native_driver.impl.serv_stop(server); #endif // NBN_WEBRTC_NATIVE #ifdef __EMSCRIPTEN__ - nbn_webrtc_em_driver.impl.serv_stop(&nbn_game_server); + nbn_webrtc_em_driver.impl.serv_stop(server); #endif // __EMSCRIPTEN__ // Free closed clients list - NBN_ConnectionListNode *current = nbn_game_server.closed_clients_head; + NBN_ConnectionListNode *current = server->closed_clients_head; while (current) { NBN_ConnectionListNode *next = current->next; @@ -2442,8 +2489,9 @@ void NBN_Server_Stop(void) { current = next; } - nbn_game_server.closed_clients_head = NULL; - Endpoint_Deinit(&nbn_game_server.endpoint); + server->closed_clients_head = NULL; + Endpoint_Deinit(&server->endpoint); + free(server); LogInfo("Stopped"); } @@ -2455,15 +2503,15 @@ static NBN_Connection_ID NBN_BuildConnectionHash(NBN_Connection_ID id, NBN_Drive return ((NBN_Connection_ID)driver_byte << 56) | id; } -NBN_ConnectionHandle *NBN_Server_GetConnection(NBN_Connection_ID id) { - return (NBN_ConnectionHandle *)hmget(nbn_game_server.clients, id); +NBN_ConnectionHandle *NBN_Server_GetConnection(NBN_Server *server, NBN_Connection_ID id) { + return (NBN_ConnectionHandle *)hmget(server->clients, id); } -unsigned int NBN_Server_GetClientCount(void) { return hmlen(nbn_game_server.clients); } +unsigned int NBN_Server_GetClientCount(NBN_Server *server) { return hmlen(server->clients); } -NBN_ConnectionHandle *NBN_Server_GetNextClient(NBN_Client_Iterator *it) { - for (; *it < hmlen(nbn_game_server.clients);) { - NBN_Connection *conn = (NBN_Connection *)nbn_game_server.clients[*it].value; +NBN_ConnectionHandle *NBN_Server_GetNextClient(NBN_Server *server, NBN_Client_Iterator *it) { + for (; *it < hmlen(server->clients);) { + NBN_Connection *conn = (NBN_Connection *)server->clients[*it].value; (*it)++; @@ -2475,41 +2523,41 @@ NBN_ConnectionHandle *NBN_Server_GetNextClient(NBN_Client_Iterator *it) { return NULL; } -static void ReadPacketsFromServerDrivers(void) { +static void ReadPacketsFromServerDrivers(NBN_Server *server) { #ifdef NBN_UDP - if (nbn_udp_driver.impl.serv_recv_packets(&nbn_game_server) < 0) { + if (nbn_udp_driver.impl.serv_recv_packets(server) < 0) { LogError("Failed to read packets from driver %s", nbn_udp_driver.name); } #endif // NBN_UDP #ifdef NBN_WEBRTC_NATIVE - if (nbn_webrtc_native_driver.impl.serv_recv_packets(&nbn_game_server) < 0) { + if (nbn_webrtc_native_driver.impl.serv_recv_packets(server) < 0) { LogError("Failed to read packets from driver %s", nbn_webrtc_native_driver.name); } #endif // NBN_WEBRTC_NATIVE #ifdef __EMSCRIPTEN__ - if (nbn_webrtc_em_driver.impl.serv_recv_packets(&nbn_game_server) < 0) { + if (nbn_webrtc_em_driver.impl.serv_recv_packets(server) < 0) { LogError("Failed to read packets from driver %s", nbn_webrtc_em_driver.name); } #endif // __EMSCRIPTEN__ } -NBN_Server_Event NBN_Server_Poll(void) { - Endpoint_UpdateTime(&nbn_game_server.endpoint); +NBN_Server_Event NBN_Server_Poll(NBN_Server *server) { + Endpoint_UpdateTime(&server->endpoint); - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + NBN_Endpoint *endpoint = &server->endpoint; if (EventQueue_IsEmpty(&endpoint->event_queue)) { - if (Server_CloseStaleClientConnections() < 0) + if (Server_CloseStaleClientConnections(server) < 0) return NBN_ERROR; - ReadPacketsFromServerDrivers(); + ReadPacketsFromServerDrivers(server); - nbn_game_server.stats.download_bandwidth = 0; + server->stats.download_bandwidth = 0; - for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { - NBN_Connection *client = nbn_game_server.clients[i].value; + for (unsigned int i = 0; i < hmlen(server->clients); i++) { + NBN_Connection *client = server->clients[i].value; for (unsigned int i = 0; i < endpoint->channel_count; i++) { NBN_Channel *channel = &client->channels[i]; @@ -2518,7 +2566,7 @@ NBN_Server_Event NBN_Server_Poll(void) { NBN_Message *msg; while ((msg = Channel_GetNextRecvedMessage(channel)) != NULL) { - if (Server_ProcessReceivedMessage(msg, client) < 0) { + if (Server_ProcessReceivedMessage(server, msg, client) < 0) { LogError("Failed to process received message"); return NBN_ERROR; @@ -2530,17 +2578,17 @@ NBN_Server_Event NBN_Server_Poll(void) { if (!client->is_closed) Connection_UpdateAverageDownloadBandwidth(client, endpoint->time); - nbn_game_server.stats.download_bandwidth += client->stats.download_bandwidth; + server->stats.download_bandwidth += client->stats.download_bandwidth; client->last_read_packets_time = endpoint->time; } - Server_RemoveClosedClientConnections(); + Server_RemoveClosedClientConnections(server); } NBN_Server_Event ev; - while (EventQueue_Dequeue(&endpoint->event_queue, &nbn_game_server.last_event)) { - if (Server_HandleEvent(&ev)) { + while (EventQueue_Dequeue(&endpoint->event_queue, &server->last_event)) { + if (Server_HandleEvent(server, &ev)) { return ev; } } @@ -2548,57 +2596,57 @@ NBN_Server_Event NBN_Server_Poll(void) { return NBN_SERVER_NO_EVENT; } -int NBN_Server_Flush(void) { - nbn_game_server.stats.upload_bandwidth = 0; +int NBN_Server_Flush(NBN_Server *server) { + server->stats.upload_bandwidth = 0; - Server_RemoveClosedClientConnections(); + Server_RemoveClosedClientConnections(server); - for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { - NBN_Connection *client = nbn_game_server.clients[i].value; + for (unsigned int i = 0; i < hmlen(server->clients); i++) { + NBN_Connection *client = server->clients[i].value; NBN_Assert(!(client->is_closed && client->is_stale)); - if (!client->is_stale && - Connection_FlushChannels(&nbn_game_server.endpoint, client, nbn_game_server.endpoint.protocol_id, - nbn_game_server.endpoint.time) < 0) { + if (!client->is_stale && Connection_FlushChannels(&server->endpoint, client, server->endpoint.protocol_id, + server->endpoint.time) < 0) { return NBN_ERROR; } - nbn_game_server.stats.upload_bandwidth += client->stats.upload_bandwidth; + server->stats.upload_bandwidth += client->stats.upload_bandwidth; } return 0; } -static NBN_Connection *CreateClientConnection(NBN_Driver_ID driver_id, NBN_Connection_ID conn_id) { +static NBN_Connection *CreateClientConnection(NBN_Server *server, NBN_Driver_ID driver_id, NBN_Connection_ID conn_id) { // write the driver ID to the first byte of the connection ID to avoid collisions between drivers conn_id = NBN_BuildConnectionHash(conn_id, driver_id); - NBN_Connection *client = Endpoint_CreateConnection(&nbn_game_server.endpoint, conn_id, driver_id); + NBN_Connection *client = Endpoint_CreateConnection(&server->endpoint, conn_id, driver_id); return client; } -int NBN_Server_CloseClientWithCode(NBN_ConnectionHandle *conn, int code) { - return Server_CloseClientWithCode(HANDLE_TO_CONN(conn), code, false); +int NBN_Server_CloseClientWithCode(NBN_Server *server, NBN_ConnectionHandle *conn, int code) { + return Server_CloseClientWithCode(server, HANDLE_TO_CONN(conn), code, false); } -int NBN_Server_CloseClient(NBN_ConnectionHandle *conn) { - return Server_CloseClientWithCode(HANDLE_TO_CONN(conn), -1, false); +int NBN_Server_CloseClient(NBN_Server *server, NBN_ConnectionHandle *conn) { + return Server_CloseClientWithCode(server, HANDLE_TO_CONN(conn), -1, false); } -NBN_Writer *NBN_Server_CreateMessage(uint8_t type, uint8_t channel_id, NBN_ConnectionHandle *receiver) { +NBN_Writer *NBN_Server_CreateMessage(NBN_Server *server, uint8_t type, uint8_t channel_id, + NBN_ConnectionHandle *receiver) { NBN_Connection *conn = HANDLE_TO_CONN(receiver); NBN_Assert(conn->is_accepted || type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE || type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); - NBN_Writer *writer = Endpoint_CreateOutgoingMessage(&nbn_game_server.endpoint, conn, type, channel_id); + NBN_Writer *writer = Endpoint_CreateOutgoingMessage(&server->endpoint, conn, type, channel_id); if (!writer) { LogError("Failed to create outgoing message for client %lld", receiver->id); /* Do not close the client if we failed to send the close client message to avoid infinite loops */ if (type != NBN_CLIENT_CLOSED_MESSAGE_TYPE) { - Server_CloseClientWithCode(conn, -1, false); + Server_CloseClientWithCode(server, conn, -1, false); return NULL; } @@ -2607,55 +2655,55 @@ NBN_Writer *NBN_Server_CreateMessage(uint8_t type, uint8_t channel_id, NBN_Conne return writer; } -NBN_Writer *NBN_Server_CreateReliableMessage(uint8_t type, NBN_ConnectionHandle *receiver) { - return NBN_Server_CreateMessage(type, nbn_game_server.endpoint.default_reliable_channel, receiver); +NBN_Writer *NBN_Server_CreateReliableMessage(NBN_Server *server, uint8_t type, NBN_ConnectionHandle *receiver) { + return NBN_Server_CreateMessage(server, type, server->endpoint.default_reliable_channel, receiver); } -NBN_Writer *NBN_Server_CreateUnreliableMessage(uint8_t type, NBN_ConnectionHandle *receiver) { - return NBN_Server_CreateMessage(type, nbn_game_server.endpoint.default_unreliable_channel, receiver); +NBN_Writer *NBN_Server_CreateUnreliableMessage(NBN_Server *server, uint8_t type, NBN_ConnectionHandle *receiver) { + return NBN_Server_CreateMessage(server, type, server->endpoint.default_unreliable_channel, receiver); } -NBN_Reader *NBN_Server_ReadMessage(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); +NBN_Reader *NBN_Server_ReadMessage(NBN_Server *server) { + NBN_Assert(server->last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); - NBN_MessageInfo msg_info = nbn_game_server.last_event.data.message_info; - NBN_Reader *reader = &nbn_game_server.endpoint.message_reader; + NBN_MessageInfo msg_info = server->last_event.data.message_info; + NBN_Reader *reader = &server->endpoint.message_reader; NBN_Reader_Init(reader, msg_info.data, msg_info.length); return reader; } -NBN_Writer *NBN_Server_WriteConnectionData(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); - NBN_Assert(nbn_game_server.last_event.data.connection != NULL); +NBN_Writer *NBN_Server_WriteConnectionData(NBN_Server *server) { + NBN_Assert(server->last_event.type == NBN_SERVER_NEW_CONNECTION); + NBN_Assert(server->last_event.data.connection != NULL); - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + NBN_Endpoint *endpoint = &server->endpoint; - NBN_Writer_Init(&nbn_game_server.server_data_writer, endpoint->server_initial_data_buffer, + NBN_Writer_Init(&server->server_data_writer, endpoint->server_initial_data_buffer, sizeof(endpoint->server_initial_data_buffer)); - return &nbn_game_server.server_data_writer; + return &server->server_data_writer; } -int NBN_Server_AcceptIncomingConnection(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); - NBN_Assert(nbn_game_server.last_event.data.connection != NULL); +int NBN_Server_AcceptIncomingConnection(NBN_Server *server) { + NBN_Assert(server->last_event.type == NBN_SERVER_NEW_CONNECTION); + NBN_Assert(server->last_event.data.connection != NULL); - unsigned data_length = nbn_game_server.server_data_writer.position; - NBN_Connection *client = nbn_game_server.last_event.data.connection; + unsigned data_length = server->server_data_writer.position; + NBN_Connection *client = server->last_event.data.connection; NBN_Writer *writer = - NBN_Server_CreateReliableMessage(NBN_CLIENT_ACCEPTED_MESSAGE_TYPE, (NBN_ConnectionHandle *)client); + NBN_Server_CreateReliableMessage(server, NBN_CLIENT_ACCEPTED_MESSAGE_TYPE, (NBN_ConnectionHandle *)client); if (!writer) { return NBN_ERROR; } if (data_length > 0) { - NBN_Assert(data_length <= sizeof(nbn_game_server.endpoint.server_initial_data_buffer)); + NBN_Assert(data_length <= sizeof(server->endpoint.server_initial_data_buffer)); NBN_Writer_WriteUInt32(writer, data_length); - NBN_Writer_WriteBytes(writer, nbn_game_server.endpoint.server_initial_data_buffer, data_length); + NBN_Writer_WriteBytes(writer, server->endpoint.server_initial_data_buffer, data_length); } else { NBN_Writer_WriteUInt32(writer, 0); } @@ -2667,58 +2715,60 @@ int NBN_Server_AcceptIncomingConnection(void) { return 0; } -int NBN_Server_RejectIncomingConnectionWithCode(int code) { - NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); - NBN_Assert(nbn_game_server.last_event.data.connection != NULL); +int NBN_Server_RejectIncomingConnectionWithCode(NBN_Server *server, int code) { + NBN_Assert(server->last_event.type == NBN_SERVER_NEW_CONNECTION); + NBN_Assert(server->last_event.data.connection != NULL); - NBN_Connection *conn = nbn_game_server.last_event.data.connection; + NBN_Connection *conn = server->last_event.data.connection; LogDebug("Rejecting incoming connection %lld (code: %d)", conn->handle.id, code); - return Server_CloseClientWithCode(conn, code, false); + return Server_CloseClientWithCode(server, conn, code, false); } -int NBN_Server_RejectIncomingConnection(void) { return NBN_Server_RejectIncomingConnectionWithCode(-1); } +int NBN_Server_RejectIncomingConnection(NBN_Server *server) { + return NBN_Server_RejectIncomingConnectionWithCode(server, -1); +} -NBN_ConnectionHandle *NBN_Server_GetIncomingConnection(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); - NBN_Assert(nbn_game_server.last_event.data.connection != NULL); +NBN_ConnectionHandle *NBN_Server_GetIncomingConnection(NBN_Server *server) { + NBN_Assert(server->last_event.type == NBN_SERVER_NEW_CONNECTION); + NBN_Assert(server->last_event.data.connection != NULL); - return (NBN_ConnectionHandle *)nbn_game_server.last_event.data.connection; + return (NBN_ConnectionHandle *)server->last_event.data.connection; } -NBN_Reader *NBN_Server_ReadConnectionRequestData(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_NEW_CONNECTION); +NBN_Reader *NBN_Server_ReadConnectionRequestData(NBN_Server *server) { + NBN_Assert(server->last_event.type == NBN_SERVER_NEW_CONNECTION); - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + NBN_Endpoint *endpoint = &server->endpoint; - NBN_Reader_Init(&nbn_game_server.client_data_reader, endpoint->connection_request_data_buffer, + NBN_Reader_Init(&server->client_data_reader, endpoint->connection_request_data_buffer, endpoint->client_connection_request_data_len); - return &nbn_game_server.client_data_reader; + return &server->client_data_reader; } -NBN_DisconnectionInfo NBN_Server_GetDisconnectionInfo(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_DISCONNECTION); +NBN_DisconnectionInfo NBN_Server_GetDisconnectionInfo(NBN_Server *server) { + NBN_Assert(server->last_event.type == NBN_SERVER_DISCONNECTION); - return nbn_game_server.last_event.data.disconnection; + return server->last_event.data.disconnection; } -NBN_MessageInfo NBN_Server_GetMessageInfo(void) { - NBN_Assert(nbn_game_server.last_event.type == NBN_SERVER_MESSAGE_RECEIVED); +NBN_MessageInfo NBN_Server_GetMessageInfo(NBN_Server *server) { + NBN_Assert(server->last_event.type == NBN_SERVER_MESSAGE_RECEIVED); - return nbn_game_server.last_event.data.message_info; + return server->last_event.data.message_info; } -NBN_ServerStats NBN_Server_GetStats(void) { return nbn_game_server.stats; } +NBN_ServerStats NBN_Server_GetStats(NBN_Server *server) { return server->stats; } -static void Server_AddClient(NBN_Connection *client) { - NBN_Assert(hmgeti(nbn_game_server.clients, client->handle.id) == -1); +static void Server_AddClient(NBN_Server *server, NBN_Connection *client) { + NBN_Assert(hmgeti(server->clients, client->handle.id) == -1); - hmput(nbn_game_server.clients, client->handle.id, client); + hmput(server->clients, client->handle.id, client); LogDebug("New client %lld", client->handle.id); } -static int Server_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection) { +static int Server_CloseClientWithCode(NBN_Server *server, NBN_Connection *client, int code, bool disconnection) { if (!client->is_closed && client->is_accepted) { if (!disconnection) { NBN_Event e; @@ -2726,7 +2776,7 @@ static int Server_CloseClientWithCode(NBN_Connection *client, int code, bool dis e.type = NBN_CLIENT_DISCONNECTED; e.data.disconnection = (NBN_DisconnectionInfo){client->handle.id, client->handle.user_data}; - if (!EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, e)) + if (!EventQueue_Enqueue(&server->endpoint.event_queue, e)) return NBN_ERROR; } } @@ -2734,7 +2784,7 @@ static int Server_CloseClientWithCode(NBN_Connection *client, int code, bool dis if (client->is_stale) { LogDebug("Closing stale connection %lld", client->handle.id); - Server_AddClientToClosedList(client); + Server_AddClientToClosedList(server, client); client->is_closed = true; return 0; @@ -2742,14 +2792,14 @@ static int Server_CloseClientWithCode(NBN_Connection *client, int code, bool dis LogDebug("Closing active connection %lld (will send a disconnection message)", client->handle.id); - Server_AddClientToClosedList(client); + Server_AddClientToClosedList(server, client); client->is_closed = true; if (!disconnection) { LogDebug("Send close message for client %lld (code: %d)", client->handle.id, code); NBN_Writer *writer = - NBN_Server_CreateReliableMessage(NBN_CLIENT_CLOSED_MESSAGE_TYPE, (NBN_ConnectionHandle *)client); + NBN_Server_CreateReliableMessage(server, NBN_CLIENT_CLOSED_MESSAGE_TYPE, (NBN_ConnectionHandle *)client); if (!writer) { return NBN_ERROR; @@ -2761,7 +2811,7 @@ static int Server_CloseClientWithCode(NBN_Connection *client, int code, bool dis return 0; } -static void Server_AddClientToClosedList(NBN_Connection *client) { +static void Server_AddClientToClosedList(NBN_Server *server, NBN_Connection *client) { if (client->is_closed) return; @@ -2771,13 +2821,13 @@ static void Server_AddClientToClosedList(NBN_Connection *client) { node->conn = client; node->next = NULL; - if (nbn_game_server.closed_clients_head == NULL) { + if (server->closed_clients_head == NULL) { // list is empty - nbn_game_server.closed_clients_head = node; + server->closed_clients_head = node; node->prev = NULL; } else { // list is not empty, add node at the end - NBN_ConnectionListNode *tail = nbn_game_server.closed_clients_head; + NBN_ConnectionListNode *tail = server->closed_clients_head; while (tail->next != NULL) tail = tail->next; @@ -2787,7 +2837,7 @@ static void Server_AddClientToClosedList(NBN_Connection *client) { } } -static int Server_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *client) { +static int Server_ProcessReceivedMessage(NBN_Server *server, NBN_Message *message, NBN_Connection *client) { NBN_Event ev; ev.type = NBN_CLIENT_MESSAGE_RECEIVED; @@ -2804,22 +2854,22 @@ static int Server_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *c client->handle.id); ev.data.message_info = msg_info; - if (!EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, ev)) + if (!EventQueue_Enqueue(&server->endpoint.event_queue, ev)) return NBN_ERROR; return 0; } -static int Server_CloseStaleClientConnections(void) { - for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { - NBN_Connection *client = nbn_game_server.clients[i].value; +static int Server_CloseStaleClientConnections(NBN_Server *server) { + for (unsigned int i = 0; i < hmlen(server->clients); i++) { + NBN_Connection *client = server->clients[i].value; - if (!client->is_stale && Connection_CheckIfStale(client, nbn_game_server.endpoint.time)) { + if (!client->is_stale && Connection_CheckIfStale(client, server->endpoint.time)) { LogInfo("Client %lld connection is stale, closing it.", client->handle.id); client->is_stale = true; - if (Server_CloseClientWithCode(client, -1, false) < 0) + if (Server_CloseClientWithCode(server, client, -1, false) < 0) return NBN_ERROR; } } @@ -2827,8 +2877,8 @@ static int Server_CloseStaleClientConnections(void) { return 0; } -static void Server_RemoveClosedClientConnections(void) { - NBN_ConnectionListNode *current = nbn_game_server.closed_clients_head; +static void Server_RemoveClosedClientConnections(NBN_Server *server) { + NBN_ConnectionListNode *current = server->closed_clients_head; while (current) { NBN_ConnectionListNode *prev = current->prev; @@ -2840,10 +2890,10 @@ static void Server_RemoveClosedClientConnections(void) { if (client->is_stale) { LogDebug("Remove closed client connection (ID: %lld)", client->handle.id); - client->driver->impl.serv_cleanup_connection(&nbn_game_server, - client); // Notify the driver to clean up the connection + // Notify the driver to clean up the connection + client->driver->impl.serv_cleanup_connection(server, client); - int ret = hmdel(nbn_game_server.clients, client->handle.id); + int ret = hmdel(server->clients, client->handle.id); NBN_Assert(ret == 1); // Destroy the connection @@ -2854,7 +2904,7 @@ static void Server_RemoveClosedClientConnections(void) { free(current); - if (current == nbn_game_server.closed_clients_head) { + if (current == server->closed_clients_head) { // delete the head of the list NBN_ConnectionListNode *new_head = next; @@ -2862,7 +2912,7 @@ static void Server_RemoveClosedClientConnections(void) { new_head->prev = NULL; } - nbn_game_server.closed_clients_head = new_head; + server->closed_clients_head = new_head; } else { // delete a node in the middle of the list prev->next = next; @@ -2876,21 +2926,21 @@ static void Server_RemoveClosedClientConnections(void) { } } -static bool Server_HandleEvent(NBN_Server_Event *ev) { - if (nbn_game_server.last_event.type == NBN_SERVER_MESSAGE_RECEIVED) { - return Server_HandleMessageReceivedEvent(ev); +static bool Server_HandleEvent(NBN_Server *server, NBN_Server_Event *ev) { + if (server->last_event.type == NBN_SERVER_MESSAGE_RECEIVED) { + return Server_HandleMessageReceivedEvent(server, ev); } - *ev = nbn_game_server.last_event.type; + *ev = server->last_event.type; return true; } // TODO: big ass function -static bool Server_HandleMessageReceivedEvent(NBN_Server_Event *ev) { - NBN_Event *last_event = &nbn_game_server.last_event; +static bool Server_HandleMessageReceivedEvent(NBN_Server *server, NBN_Server_Event *ev) { + NBN_Event *last_event = &server->last_event; NBN_MessageInfo message_info = last_event->data.message_info; NBN_Connection *sender = HANDLE_TO_CONN(message_info.sender); - NBN_Endpoint *endpoint = &nbn_game_server.endpoint; + NBN_Endpoint *endpoint = &server->endpoint; if (sender->is_closed || sender->is_stale) { return false; @@ -2900,7 +2950,7 @@ static bool Server_HandleMessageReceivedEvent(NBN_Server_Event *ev) { LogInfo("Received a disconnection request from client %lld (user_data: %p)", sender->handle.id, sender->handle.user_data); - if (Server_CloseClientWithCode(sender, -1, true) < 0) { + if (Server_CloseClientWithCode(server, sender, -1, true) < 0) { *ev = NBN_ERROR; return true; } @@ -2910,14 +2960,14 @@ static bool Server_HandleMessageReceivedEvent(NBN_Server_Event *ev) { last_event->type = NBN_SERVER_DISCONNECTION; last_event->data.disconnection = (NBN_DisconnectionInfo){sender->handle.id, sender->handle.user_data}; - Server_RemoveClosedClientConnections(); + Server_RemoveClosedClientConnections(server); *ev = NBN_SERVER_DISCONNECTION; return true; } if (message_info.type != NBN_CONNECTION_REQUEST_MESSAGE_TYPE) { - nbn_game_server.server_data_writer.position = 0; + server->server_data_writer.position = 0; *ev = NBN_SERVER_MESSAGE_RECEIVED; return true; @@ -2935,7 +2985,7 @@ static bool Server_HandleMessageReceivedEvent(NBN_Server_Event *ev) { return true; } - NBN_Reader *reader = NBN_Server_ReadMessage(); + NBN_Reader *reader = NBN_Server_ReadMessage(server); unsigned int data_length; if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) { @@ -2953,7 +3003,7 @@ static bool Server_HandleMessageReceivedEvent(NBN_Server_Event *ev) { return true; } - if (NBN_Reader_ReadBytes(reader, nbn_game_server.endpoint.connection_request_data_buffer, data_length) < 0) { + if (NBN_Reader_ReadBytes(reader, server->endpoint.connection_request_data_buffer, data_length) < 0) { LogError("Failed to read client data"); *ev = NBN_ERROR; @@ -2961,7 +3011,7 @@ static bool Server_HandleMessageReceivedEvent(NBN_Server_Event *ev) { } } - nbn_game_server.endpoint.client_connection_request_data_len = data_length; + server->endpoint.client_connection_request_data_len = data_length; NBN_Event e; @@ -2977,14 +3027,16 @@ static bool Server_HandleMessageReceivedEvent(NBN_Server_Event *ev) { return true; } -static void ServerDriver_OnClientConnected(NBN_Connection *client) { Server_AddClient(client); } +static void ServerDriver_OnClientConnected(NBN_Server *server, NBN_Connection *client) { + Server_AddClient(server, client); +} -static int ServerDriver_OnClientPacketReceived(NBN_Packet *packet) { - if (Endpoint_ProcessReceivedPacket(&nbn_game_server.endpoint, packet, packet->sender) < 0) { +static int ServerDriver_OnClientPacketReceived(NBN_Server *server, NBN_Packet *packet) { + if (Endpoint_ProcessReceivedPacket(&server->endpoint, packet, packet->sender) < 0) { LogError("An error occured while processing packet from client %d, closing the client", packet->sender->handle.id); - return Server_CloseClientWithCode(packet->sender, -1, false); + return Server_CloseClientWithCode(server, packet->sender, -1, false); } return 0; @@ -3005,12 +3057,12 @@ static char err_msg[32]; #endif -static int UDP_InitSocket(void); -static void UDP_DeinitSocket(void); -static int UDP_BindSocket(uint16_t); +static SOCKET UDP_InitSocket(void); +static void UDP_DeinitSocket(int); +static int UDP_BindSocket(int, uint16_t); static char *UDP_GetLastErrorMessage(void); -static int UDP_InitSocket(void) { +static SOCKET UDP_InitSocket(void) { #ifdef NBN_PLATFORM_WINDOWS WSADATA wsa; int err = WSAStartup(MAKEWORD(2, 2), &wsa); @@ -3021,7 +3073,9 @@ static int UDP_InitSocket(void) { } #endif - if ((nbn_udp_sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) + SOCKET sock; + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) return NBN_ERROR; #if defined(NBN_PLATFORM_WINDOWS) @@ -3035,32 +3089,32 @@ static int UDP_InitSocket(void) { #elif defined(NBN_PLATFORM_MAC) || defined(NBN_PLATFORM_UNIX) int non_blocking = 1; - if (fcntl(nbn_udp_sock, F_SETFL, O_NONBLOCK, non_blocking) < 0) { + if (fcntl(sock, F_SETFL, O_NONBLOCK, non_blocking) < 0) { LogError("fcntl() failed: %s", UDP_GetLastErrorMessage()); return NBN_ERROR; } #endif - return 0; + return sock; } -static void UDP_DeinitSocket(void) { - closesocket(nbn_udp_sock); +static void UDP_DeinitSocket(SOCKET sock) { + closesocket(sock); #ifdef NBN_PLATFORM_WINDOWS WSACleanup(); #endif } -static int UDP_BindSocket(uint16_t port) { +static int UDP_BindSocket(SOCKET sock, uint16_t port) { SOCKADDR_IN sin; sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_family = AF_INET; sin.sin_port = htons(port); - if (bind(nbn_udp_sock, (SOCKADDR *)&sin, sizeof(sin)) < 0) { + if (bind(sock, (SOCKADDR *)&sin, sizeof(sin)) < 0) { LogError("bind() failed: %s", UDP_GetLastErrorMessage()); return NBN_ERROR; @@ -3073,22 +3127,23 @@ static NBN_Connection_ID UDP_BuildConnectionID(NBN_IPAddress address) { return ((NBN_Connection_ID)address.host << 2) | address.port; } -static NBN_Connection *UDP_FindOrCreateClientConnectionByAddress(NBN_IPAddress address) { +static NBN_Connection *UDP_FindOrCreateClientConnectionByAddress(NBN_Server *server, NBN_IPAddress address) { NBN_Connection_ID conn_id = UDP_BuildConnectionID(address); conn_id = NBN_BuildConnectionHash(conn_id, NBN_DRIVER_UDP); - NBN_ConnectionHandle *handle = NBN_Server_GetConnection(conn_id); + NBN_ConnectionHandle *handle = NBN_Server_GetConnection(server, conn_id); if (handle) { return HANDLE_TO_CONN(handle); } // this is a new connection - NBN_Connection *conn = CreateClientConnection(NBN_DRIVER_UDP, conn_id); + NBN_Connection *conn = CreateClientConnection(server, NBN_DRIVER_UDP, conn_id); conn->driver_data.udp.ip_address = address; + conn->driver_data.endpoint_ptr = server; LogInfo("New UDP connection (id: %lld, addr: %d, port: %d)", conn->handle.id, address.host, address.port); - ServerDriver_OnClientConnected(conn); + ServerDriver_OnClientConnected(server, conn); return conn; } @@ -3130,21 +3185,16 @@ static char *UDP_GetLastErrorMessage(void) { } static int UDP_Server_Start(NBN_Server *server, uint16_t port) { - (void)server; - - if (UDP_InitSocket() < 0) + if ((server->driver_data.udp.sock = UDP_InitSocket()) < 0) return NBN_ERROR; - if (UDP_BindSocket(port) < 0) + if (UDP_BindSocket(server->driver_data.udp.sock, port) < 0) return NBN_ERROR; return 0; } -static void UDP_Server_Stop(NBN_Server *server) { - (void)server; - UDP_DeinitSocket(); -} +static void UDP_Server_Stop(NBN_Server *server) { UDP_DeinitSocket(server->driver_data.udp.sock); } static int UDP_Server_RecvPackets(NBN_Server *server) { static NBN_Packet packet = {0}; @@ -3152,8 +3202,8 @@ static int UDP_Server_RecvPackets(NBN_Server *server) { socklen_t src_addr_len = sizeof(src_addr); while (true) { - int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, - &src_addr_len); + int bytes = recvfrom(server->driver_data.udp.sock, (char *)packet.buffer, sizeof(packet.buffer), 0, + (SOCKADDR *)&src_addr, &src_addr_len); if (bytes <= 0) break; @@ -3172,9 +3222,9 @@ static int UDP_Server_RecvPackets(NBN_Server *server) { LogDebug("Received valid UDP packet from %d:%d", ip_address.host, ip_address.port); - packet.sender = UDP_FindOrCreateClientConnectionByAddress(ip_address); + packet.sender = UDP_FindOrCreateClientConnectionByAddress(server, ip_address); - ServerDriver_OnClientPacketReceived(&packet); + ServerDriver_OnClientPacketReceived(server, &packet); } return 0; @@ -3186,8 +3236,6 @@ static void UDP_Server_CleanupConnection(NBN_Server *server, NBN_Connection *con } static int UDP_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_Connection *connection) { - (void)server; - NBN_IPAddress dest_address = connection->driver_data.udp.ip_address; SOCKADDR_IN dest_addr; @@ -3195,7 +3243,7 @@ static int UDP_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_C dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(dest_address.port); - if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, + if (sendto(server->driver_data.udp.sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, sizeof(dest_addr)) == SOCKET_ERROR) { LogError("sendto() failed: %s", UDP_GetLastErrorMessage()); @@ -3210,19 +3258,16 @@ static int UDP_Client_Start(NBN_Client *client, const char *host, uint16_t port) UDP_ParseIpAddress(host, port, ip_address); - if (UDP_InitSocket() < 0) + if ((client->driver_data.udp.sock = UDP_InitSocket()) < 0) return NBN_ERROR; - if (UDP_BindSocket(0) < 0) + if (UDP_BindSocket(client->driver_data.udp.sock, 0) < 0) return NBN_ERROR; return 0; } -static void UDP_Client_Stop(NBN_Client *client) { - (void)client; - UDP_DeinitSocket(); -} +static void UDP_Client_Stop(NBN_Client *client) { UDP_DeinitSocket(client->driver_data.udp.sock); } static int UDP_Client_RecvPackets(NBN_Client *client) { NBN_IPAddress server_address = client->server_connection->driver_data.udp.ip_address; @@ -3231,8 +3276,8 @@ static int UDP_Client_RecvPackets(NBN_Client *client) { socklen_t src_addr_len = sizeof(src_addr); while (true) { - int bytes = recvfrom(nbn_udp_sock, (char *)packet.buffer, sizeof(packet.buffer), 0, (SOCKADDR *)&src_addr, - &src_addr_len); + int bytes = recvfrom(client->driver_data.udp.sock, (char *)packet.buffer, sizeof(packet.buffer), 0, + (SOCKADDR *)&src_addr, &src_addr_len); if (bytes <= 0) break; @@ -3253,21 +3298,21 @@ static int UDP_Client_RecvPackets(NBN_Client *client) { packet.sender = client->server_connection; - ClientDriver_OnPacketReceived(&packet); + ClientDriver_OnPacketReceived(client, &packet); } return 0; } -static int UDP_Client_SendPacket(NBN_Client *client, NBN_Packet *packet) { - NBN_IPAddress server_address = client->server_connection->driver_data.udp.ip_address; +static int UDP_Client_SendPacket(NBN_Client *client, NBN_Packet *packet, NBN_Connection *connection) { + NBN_IPAddress server_address = connection->driver_data.udp.ip_address; SOCKADDR_IN dest_addr; dest_addr.sin_addr.s_addr = htonl(server_address.host); dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(server_address.port); - if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, + if (sendto(client->driver_data.udp.sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, sizeof(dest_addr)) == SOCKET_ERROR) { LogError("sendto() failed: %s", UDP_GetLastErrorMessage()); @@ -3334,8 +3379,9 @@ static int WebRTC_Server_RecvPackets(NBN_Server *server) { if (handle == NULL) { LogInfo("Peer %d has connected", peer_id); - conn = CreateClientConnection(NBN_DRIVER_WEBRTC_EMSCRIPTEN, conn_id); + conn = CreateClientConnection(server, NBN_DRIVER_WEBRTC_EMSCRIPTEN, conn_id); conn->driver_data.webrtc.peer_id = peer_id; + conn->driver_data.endpoint_ptr = server; ServerDriver_OnClientConnected(conn); } else { @@ -3347,7 +3393,7 @@ static int WebRTC_Server_RecvPackets(NBN_Server *server) { packet.sender = conn; - ServerDriver_OnClientPacketReceived(&packet); + ServerDriver_OnClientPacketReceived(server, &packet); } return 0; @@ -3359,7 +3405,7 @@ static void WebRTC_Server_CleanupConnection(NBN_Server *server, NBN_Connection * __js_game_server_close_client_peer(conn->driver_data.webrtc.peer_id); } -static int WebRTC_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_Connection *conn) { +static int WebRTC_Server_SendPacketTo(NBN_Endpoint *server, NBN_Packet *packet, NBN_Connection *conn) { return __js_game_server_send_packet_to(packet->buffer, packet->size, conn->driver_data.webrtc.peer_id); } @@ -3386,13 +3432,13 @@ static int WebRTC_Client_RecvPackets(NBN_Client *client) { packet.sender = client->server_connection; - ClientDriver_OnPacketReceived(&packet); + ClientDriver_OnPacketReceived(client, &packet); } return 0; } -static int WebRTC_Client_SendPacket(NBN_Client *client, NBN_Packet *packet) { +static int WebRTC_Client_SendPacket(NBN_Endpoint *client, NBN_Packet *packet, NBN_Connection *connection) { return __js_game_client_send_packet(packet->buffer, packet->size); } @@ -3414,10 +3460,13 @@ static int WebRTC_Client_SendPacket(NBN_Client *client, NBN_Packet *packet) { #include "json.h" -static NBN_WebRTC_Config nbn_wrtc_cfg; -static int wsserver = -1; +void NBN_Server_SetWebRTC_Config(NBN_Server *server, NBN_WebRTC_Config config) { + server->driver_data.webrtc.cfg = config; +} -void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config) { nbn_wrtc_cfg = config; } +void NBN_Client_SetWebRTC_Config(NBN_Client *client, NBN_WebRTC_Config config) { + client->driver_data.webrtc.cfg = config; +} static void WS_OnError(int ws, const char *err_msg, void *user_ptr) { (void)user_ptr; @@ -3595,7 +3644,7 @@ static void ClosePeer(NBN_Connection *conn) { rtcDelete(ws); } -static void Server_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { +static void WS_Server_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { (void)pc; LogDebug("Processing local description of type '%s'", type); @@ -3615,22 +3664,22 @@ static void Server_OnLocalDescription(int pc, const char *sdp, const char *type, } } -static void Server_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { +static void WS_Server_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { LogDebug("Peer %d state changed to %d", pc, state); if (state == RTC_CONNECTED) { NBN_Connection *conn = (NBN_Connection *)user_ptr; + NBN_Server *server = (NBN_Server *)conn->driver_data.endpoint_ptr; LogDebug("Peer %d is connected !", pc); - ServerDriver_OnClientConnected(conn); + ServerDriver_OnClientConnected(server, conn); } } -static int CreatePeer(NBN_WebRTC_Peer_ID *peer_id, int *channel_id, rtcDescriptionCallbackFunc on_rtc_description_cb, - rtcStateChangeCallbackFunc state_changed_cb) { - rtcConfiguration rtcCfg = {.iceServers = nbn_wrtc_cfg.ice_servers, - .iceServersCount = (int)nbn_wrtc_cfg.ice_servers_count, - .disableAutoNegotiation = false}; +static int CreatePeer(NBN_WebRTC_Peer_ID *peer_id, int *channel_id, NBN_WebRTC_Config cfg, + rtcDescriptionCallbackFunc on_rtc_description_cb, rtcStateChangeCallbackFunc state_changed_cb) { + rtcConfiguration rtcCfg = { + .iceServers = cfg.ice_servers, .iceServersCount = (int)cfg.ice_servers_count, .disableAutoNegotiation = false}; *peer_id = rtcCreatePeerConnection(&rtcCfg); if (*peer_id < 0) { @@ -3669,24 +3718,25 @@ static int CreatePeer(NBN_WebRTC_Peer_ID *peer_id, int *channel_id, rtcDescripti } static void WS_Server_OnOpen(int ws, void *user_ptr) { - (void)user_ptr; - LogDebug("WS %d is open", ws); + NBN_Server *server = (NBN_Server *)user_ptr; NBN_WebRTC_Peer_ID peer_id; int channel_id; - if (CreatePeer(&peer_id, &channel_id, Server_OnLocalDescription, Server_OnPeerStateChanged) < 0) { + if (CreatePeer(&peer_id, &channel_id, server->driver_data.webrtc.cfg, WS_Server_OnLocalDescription, + WS_Server_OnPeerStateChanged) < 0) { LogError("Failed to create peer"); return; } NBN_Connection_ID conn_id = NBN_BuildConnectionHash(ws, NBN_DRIVER_WEBRTC_NATIVE); - NBN_Connection *conn = CreateClientConnection(NBN_DRIVER_WEBRTC_NATIVE, conn_id); + NBN_Connection *conn = CreateClientConnection(server, NBN_DRIVER_WEBRTC_NATIVE, conn_id); conn->driver_data.webrtc.peer_id = peer_id; conn->driver_data.webrtc.ws = ws; conn->driver_data.webrtc.channel_id = channel_id; + conn->driver_data.endpoint_ptr = server; rtcSetUserPointer(peer_id, conn); rtcSetUserPointer(ws, conn); @@ -3713,11 +3763,12 @@ static void WS_Server_OnMessage(int ws, const char *msg, int size, void *user_pt ProcessSignalingMessage(peer_id, ws, msg, size, "offer"); } -static void OnWs_Connection(int wsserver, int ws, void *user_ptr) { +static void WS_Server_OnConnection(int wsserver, int ws, void *user_ptr) { (void)wsserver; LogDebug("New WS connection %d (user_ptr: %p)", ws, user_ptr); + rtcSetUserPointer(ws, user_ptr); rtcSetOpenCallback(ws, WS_Server_OnOpen); rtcSetClosedCallback(ws, WS_Server_OnClosed); rtcSetErrorCallback(ws, WS_OnError); @@ -3725,32 +3776,36 @@ static void OnWs_Connection(int wsserver, int ws, void *user_ptr) { } static int WebRTC_Native_Server_Start(NBN_Server *server, uint16_t port) { - (void)server; + NBN_WebRTC_Config cfg = server->driver_data.webrtc.cfg; - rtcInitLogger(nbn_wrtc_cfg.log_level, WebRTC_Native_Log); + rtcInitLogger(cfg.log_level, WebRTC_Native_Log); rtcPreload(); - rtcWsServerConfiguration cfg = {.port = port, - .enableTls = nbn_wrtc_cfg.enable_tls, - .certificatePemFile = nbn_wrtc_cfg.cert_path, - .keyPemFile = nbn_wrtc_cfg.key_path, - .keyPemPass = nbn_wrtc_cfg.passphrase}; + rtcWsServerConfiguration rtc_cfg = {.port = port, + .enableTls = cfg.enable_tls, + .certificatePemFile = cfg.cert_path, + .keyPemFile = cfg.key_path, + .keyPemPass = cfg.passphrase}; - wsserver = rtcCreateWebSocketServer(&cfg, OnWs_Connection); + int ws_server = rtcCreateWebSocketServer(&rtc_cfg, WS_Server_OnConnection); + + if (ws_server < 0) { + LogError("Failed to start WS server (code: %d)", ws_server); - if (wsserver < 0) { - LogError("Failed to start WS server (code: %d)", wsserver); return NBN_ERROR; } + rtcSetUserPointer(ws_server, server); + server->driver_data.webrtc.ws_server = ws_server; + return 0; } static void WebRTC_Native_Server_Stop(NBN_Server *server) { - (void)server; + int ws_server = server->driver_data.webrtc.ws_server; - if (wsserver >= 0) { - rtcDeleteWebSocketServer(wsserver); + if (ws_server >= 0) { + rtcDeleteWebSocketServer(ws_server); } rtcCleanup(); @@ -3761,7 +3816,7 @@ static int WebRTC_Native_Server_RecvPackets(NBN_Server *server) { const int buffer_size = sizeof(packet.buffer); int size = buffer_size; - for (unsigned int i = 0; i < hmlen(nbn_game_server.clients); i++) { + for (unsigned int i = 0; i < hmlen(server->clients); i++) { NBN_Connection *conn = server->clients[i].value; if (conn->driver->id != NBN_DRIVER_WEBRTC_NATIVE) @@ -3776,7 +3831,7 @@ static int WebRTC_Native_Server_RecvPackets(NBN_Server *server) { packet.sender = conn; size = buffer_size; - ServerDriver_OnClientPacketReceived(&packet); + ServerDriver_OnClientPacketReceived(server, &packet); } } @@ -3805,9 +3860,7 @@ static int WebRTC_Native_Server_SendPacketTo(NBN_Server *server, NBN_Packet *pac return 0; } -static bool wrtc_client_connected; - -static void Client_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { +static void WS_Client_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { (void)pc; LogDebug("Processing local description of type '%s'", type); @@ -3823,33 +3876,35 @@ static void Client_OnLocalDescription(int pc, const char *sdp, const char *type, ProcessLocalDescription(ws, sdp, "offer"); } -static void Client_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { - (void)user_ptr; +static void WS_Client_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { + NBN_Connection *conn = (NBN_Connection *)user_ptr; LogDebug("Server peer state changed to %d", pc, state); if (state == RTC_CONNECTED) { LogDebug("Server peer is connected !", pc); - wrtc_client_connected = true; + NBN_Client *client = (NBN_Client *)conn->driver_data.endpoint_ptr; + client->driver_data.webrtc.is_connected = true; } } static void WS_Client_OnOpen(int ws, void *user_ptr) { - (void)user_ptr; + NBN_Client *client = (NBN_Client *)user_ptr; LogDebug("WS %d is open, creating peer...", ws); NBN_WebRTC_Peer_ID peer_id; int channel_id; - if (CreatePeer(&peer_id, &channel_id, Client_OnLocalDescription, Client_OnPeerStateChanged) < 0) { + if (CreatePeer(&peer_id, &channel_id, client->driver_data.webrtc.cfg, WS_Client_OnLocalDescription, + WS_Client_OnPeerStateChanged) < 0) { LogError("Failed to create peer"); return; } LogDebug("Successfully created peer: %d", peer_id); - NBN_Connection *server_conn = nbn_game_client.server_connection; + NBN_Connection *server_conn = client->server_connection; NBN_Assert(server_conn != NULL); rtcSetUserPointer(peer_id, server_conn); @@ -3874,11 +3929,9 @@ static void WS_Client_OnMessage(int ws, const char *msg, int size, void *user_pt } static int WebRTC_Native_Client_Start(NBN_Client *client, const char *host, uint16_t port) { - (void)client; + NBN_WebRTC_Config cfg = client->driver_data.webrtc.cfg; - wrtc_client_connected = false; - - rtcInitLogger(nbn_wrtc_cfg.log_level, WebRTC_Native_Log); + rtcInitLogger(cfg.log_level, WebRTC_Native_Log); rtcPreload(); char ws_addr[256] = {0}; @@ -3894,6 +3947,7 @@ static int WebRTC_Native_Client_Start(NBN_Client *client, const char *host, uint LogDebug("Successfully created client WS: %d", cli_ws); + rtcSetUserPointer(cli_ws, client); rtcSetOpenCallback(cli_ws, WS_Client_OnOpen); rtcSetClosedCallback(cli_ws, WS_Client_OnClosed); rtcSetErrorCallback(cli_ws, WS_OnError); @@ -3925,20 +3979,20 @@ static int WebRTC_Native_Client_Start(NBN_Client *client, const char *host, uint current_time_sec += delay; - if (current_time_sec >= timeout || wrtc_client_connected) { + if (current_time_sec >= timeout || client->driver_data.webrtc.is_connected) { break; } } - return wrtc_client_connected ? 0 : NBN_ERROR; + return client->driver_data.webrtc.is_connected ? 0 : NBN_ERROR; } static void WebRTC_Native_Client_Stop(NBN_Client *client) { - if (wrtc_client_connected) { + if (client->driver_data.webrtc.is_connected) { ClosePeer(client->server_connection); } - wrtc_client_connected = false; + client->driver_data.webrtc.is_connected = false; rtcCleanup(); } @@ -3955,14 +4009,16 @@ static int WebRTC_Native_Client_RecvPackets(NBN_Client *client) { packet.sender = NULL; size = buffer_size; - ClientDriver_OnPacketReceived(&packet); + ClientDriver_OnPacketReceived(client, &packet); } return 0; } -static int WebRTC_Native_Client_SendPacket(NBN_Client *client, NBN_Packet *packet) { - int channel_id = client->server_connection->driver_data.webrtc.channel_id; +static int WebRTC_Native_Client_SendPacket(NBN_Client *client, NBN_Packet *packet, NBN_Connection *connection) { + (void)client; + + int channel_id = connection->driver_data.webrtc.channel_id; if (rtcSendMessage(channel_id, (char *)packet->buffer, packet->size) < 0) { LogError("rtcSendMessage failed"); @@ -3992,18 +4048,18 @@ DWORD WINAPI PacketSimulator_Routine(LPVOID); static void *PacketSimulator_Routine(void *); #endif -void NBN_Client_SetPing(float v) { nbn_game_client.endpoint.packet_simulator.ping = v; } -void NBN_Client_SetJitter(float v) { nbn_game_client.endpoint.packet_simulator.jitter = v; } -void NBN_Client_SetPacketLoss(float v) { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } -void NBN_Client_SetPacketDuplication(float v) { - nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; +void NBN_Client_SetPing(NBN_Client *client, float v) { client->endpoint.packet_simulator.ping = v; } +void NBN_Client_SetJitter(NBN_Client *client, float v) { client->endpoint.packet_simulator.jitter = v; } +void NBN_Client_SetPacketLoss(NBN_Client *client, float v) { client->endpoint.packet_simulator.packet_loss_ratio = v; } +void NBN_Client_SetPacketDuplication(NBN_Client *client, float v) { + client->endpoint.packet_simulator.packet_duplication_ratio = v; } -void NBN_Server_SetPing(float v) { nbn_game_server.endpoint.packet_simulator.ping = v; } -void NBN_Server_SetJitter(float v) { nbn_game_server.endpoint.packet_simulator.jitter = v; } -void NBN_Server_SetPacketLoss(float v) { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } -void NBN_Server_SetPacketDuplication(float v) { - nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; +void NBN_Server_SetPing(NBN_Server *server, float v) { server->endpoint.packet_simulator.ping = v; } +void NBN_Server_SetJitter(NBN_Server *server, float v) { server->endpoint.packet_simulator.jitter = v; } +void NBN_Server_SetPacketLoss(NBN_Server *server, float v) { server->endpoint.packet_simulator.packet_loss_ratio = v; } +void NBN_Server_SetPacketDuplication(NBN_Server *server, float v) { + server->endpoint.packet_simulator.packet_duplication_ratio = v; } static void PacketSimulator_Init(NBN_PacketSimulator *packet_simulator, NBN_Endpoint *endpoint) { @@ -4195,9 +4251,9 @@ static int PacketSimulator_SendPacket(NBN_PacketSimulator *packet_simulator, NBN if (receiver->is_stale) return 0; - return driver->impl.serv_send_packet_to(&nbn_game_server, packet, receiver); + return driver->impl.serv_send_packet_to((NBN_Server *)packet_simulator->endpoint, packet, receiver); } else { - return driver->impl.cli_send_packet(&nbn_game_client, packet); + return driver->impl.cli_send_packet((NBN_Client *)packet_simulator->endpoint, packet, receiver); } } diff --git a/nbnet.h b/nbnet.h index 69deebc..3ce1776 100644 --- a/nbnet.h +++ b/nbnet.h @@ -56,6 +56,9 @@ extern "C" { #define NBN_CONNECTION_STALE_TIME_THRESHOLD 3 #endif +typedef struct NBN_Client NBN_Client; +typedef struct NBN_Server NBN_Server; + typedef uint64_t NBN_Connection_ID; typedef struct NBN_ConnectionHandle { @@ -200,32 +203,33 @@ int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len); * @param host Host to connect to * @param port Port to connect to */ -void NBN_Client_Init(const char *protocol_name, const char *host, uint16_t port); +NBN_Client *NBN_Client_Create(const char *protocol_name, const char *host, uint16_t port); // TODO: doc -uint8_t NBN_Client_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len); +uint8_t NBN_Client_CreateChannel(NBN_Client *client, NBN_Channel_Mode mode, unsigned int buffer_size, + unsigned int max_message_len); // TODO: doc -unsigned int NBN_Client_GetChannelCurrentCapacity(uint8_t channel_id); +unsigned int NBN_Client_GetChannelCurrentCapacity(NBN_Client *client, uint8_t channel_id); // TODO: doc -NBN_Writer *NBN_Client_WriteConnectionRequestData(void); +NBN_Writer *NBN_Client_WriteConnectionRequestData(NBN_Client *client); /** * Start the game client. * * @return 0 when successully started, -1 otherwise */ -int NBN_Client_Start(void); +int NBN_Client_Start(NBN_Client *client); /** * Disconnect from the server. The client can be restarted by calling NBN_Client_Start or * NBN_Client_StartWithData again. */ -void NBN_Client_Stop(void); +void NBN_Client_Stop(NBN_Client *client); // TODO: doc -NBN_Reader *NBN_Client_ReadServerData(void); +NBN_Reader *NBN_Client_ReadServerData(NBN_Client *client); /** * Poll game client events. @@ -234,7 +238,7 @@ NBN_Reader *NBN_Client_ReadServerData(void); * * @return The code of the polled event or NBN_NO_EVENT when there is no more events. */ -NBN_Client_Event NBN_Client_Poll(void); +NBN_Client_Event NBN_Client_Poll(NBN_Client *client); /** * Pack all enqueued messages into packets and send them. @@ -244,19 +248,19 @@ NBN_Client_Event NBN_Client_Poll(void); * * @return 0 when successful, -1 otherwise */ -int NBN_Client_Flush(void); +int NBN_Client_Flush(NBN_Client *client); // TODO: doc -NBN_Writer *NBN_Client_CreateMessage(uint8_t type, uint8_t channel_id); +NBN_Writer *NBN_Client_CreateMessage(NBN_Client *client, uint8_t type, uint8_t channel_id); // TODO: doc -NBN_Writer *NBN_Client_CreateReliableMessage(uint8_t type); +NBN_Writer *NBN_Client_CreateReliableMessage(NBN_Client *client, uint8_t type); // TODO: doc -NBN_Writer *NBN_Client_CreateUnreliableMessage(uint8_t type); +NBN_Writer *NBN_Client_CreateUnreliableMessage(NBN_Client *client, uint8_t type); // TODO: doc -NBN_Reader *NBN_Client_ReadMessage(void); +NBN_Reader *NBN_Client_ReadMessage(NBN_Client *client); /** * Retrieve the info about the last received message. @@ -266,14 +270,14 @@ NBN_Reader *NBN_Client_ReadMessage(void); * * @return A structure containing information about the received message */ -NBN_MessageInfo NBN_Client_GetMessageInfo(void); +NBN_MessageInfo NBN_Client_GetMessageInfo(NBN_Client *client); /** * Retrieve network stats about the game client. * * @return A structure containing network related stats about the game client */ -NBN_ConnectionStats NBN_Client_GetStats(void); +NBN_ConnectionStats NBN_Client_GetStats(NBN_Client *client); /** * Retrieve the code sent by the server when closing the connection. @@ -282,12 +286,12 @@ NBN_ConnectionStats NBN_Client_GetStats(void); * * @return The code used by the server when closing the connection or -1 (the default code) */ -int NBN_Client_GetServerCloseCode(void); +int NBN_Client_GetServerCloseCode(NBN_Client *client); /** * @return true if connected, false otherwise */ -bool NBN_Client_IsConnected(void); +bool NBN_Client_IsConnected(NBN_Client *client); /** * Initialize the game server with minimal configuration. @@ -296,33 +300,34 @@ bool NBN_Client_IsConnected(void); * able to communicate * @param port The port clients will connect to */ -void NBN_Server_Init(const char *protocol_name, uint16_t port); +NBN_Server *NBN_Server_Create(const char *protocol_name, uint16_t port); // TODO: doc -uint8_t NBN_Server_CreateChannel(NBN_Channel_Mode mode, unsigned int buffer_size, unsigned int max_message_len); +uint8_t NBN_Server_CreateChannel(NBN_Server *server, NBN_Channel_Mode mode, unsigned int buffer_size, + unsigned int max_message_len); // TODO: doc -unsigned int NBN_Server_GetChannelCurrentCapacity(uint8_t channel_id, NBN_ConnectionHandle *conn); +unsigned int NBN_Server_GetChannelCurrentCapacity(NBN_Server *server, uint8_t channel_id, NBN_ConnectionHandle *conn); /** * Start the game server with the provided configuration. * * @return 0 when successfully started, -1 otherwise */ -int NBN_Server_Start(void); +int NBN_Server_Start(NBN_Server *server); /** * Stop the game server and clean everything up. */ -void NBN_Server_Stop(void); +void NBN_Server_Stop(NBN_Server *server); // TODO: doc -NBN_ConnectionHandle *NBN_Server_GetConnection(NBN_Connection_ID); +NBN_ConnectionHandle *NBN_Server_GetConnection(NBN_Server *server, NBN_Connection_ID); // TODO: doc -unsigned int NBN_Server_GetClientCount(void); +unsigned int NBN_Server_GetClientCount(NBN_Server *server); -NBN_ConnectionHandle *NBN_Server_GetNextClient(NBN_Client_Iterator *it); +NBN_ConnectionHandle *NBN_Server_GetNextClient(NBN_Server *server, NBN_Client_Iterator *it); /** * Poll game server events. @@ -331,7 +336,7 @@ NBN_ConnectionHandle *NBN_Server_GetNextClient(NBN_Client_Iterator *it); * * @return The code of the polled event or NBN_NO_EVENT when there is no more events. */ -NBN_Server_Event NBN_Server_Poll(void); +NBN_Server_Event NBN_Server_Poll(NBN_Server *server); /** * Pack all enqueued messages into packets and send them. @@ -341,7 +346,7 @@ NBN_Server_Event NBN_Server_Poll(void); * * @return 0 when successful, -1 otherwise */ -int NBN_Server_Flush(void); +int NBN_Server_Flush(NBN_Server *server); /** * Close a client's connection without a specific code (default code is -1) @@ -350,7 +355,7 @@ int NBN_Server_Flush(void); * * @return 0 when successful, -1 otherwise */ -int NBN_Server_CloseClient(NBN_ConnectionHandle *conn); +int NBN_Server_CloseClient(NBN_Server *server, NBN_ConnectionHandle *conn); /** * Close a client's connection with a specific code. @@ -362,25 +367,26 @@ int NBN_Server_CloseClient(NBN_ConnectionHandle *conn); * * @return 0 when successful, -1 otherwise */ -int NBN_Server_CloseClientWithCode(NBN_ConnectionHandle *conn, int code); +int NBN_Server_CloseClientWithCode(NBN_Server *server, NBN_ConnectionHandle *conn, int code); // TODO: doc -NBN_Writer *NBN_Server_CreateMessage(uint8_t type, uint8_t channel_id, NBN_ConnectionHandle *receiver); +NBN_Writer *NBN_Server_CreateMessage(NBN_Server *server, uint8_t type, uint8_t channel_id, + NBN_ConnectionHandle *receiver); // TODO: doc -NBN_Writer *NBN_Server_CreateReliableMessage(uint8_t type, NBN_ConnectionHandle *receiver); +NBN_Writer *NBN_Server_CreateReliableMessage(NBN_Server *server, uint8_t type, NBN_ConnectionHandle *receiver); // TODO: doc -NBN_Writer *NBN_Server_CreateUnreliableMessage(uint8_t type, NBN_ConnectionHandle *receiver); +NBN_Writer *NBN_Server_CreateUnreliableMessage(NBN_Server *server, uint8_t type, NBN_ConnectionHandle *receiver); // TODO: doc -NBN_Reader *NBN_Server_ReadMessage(void); +NBN_Reader *NBN_Server_ReadMessage(NBN_Server *server); // TODO: doc -NBN_Writer *NBN_Server_WriteConnectionData(void); +NBN_Writer *NBN_Server_WriteConnectionData(NBN_Server *server); // TODO: doc -int NBN_Server_AcceptIncomingConnection(void); +int NBN_Server_AcceptIncomingConnection(NBN_Server *server); /** * Reject the last client connection request with a specific code. @@ -392,7 +398,7 @@ int NBN_Server_AcceptIncomingConnection(void); * * @return 0 when successful, -1 otherwise */ -int NBN_Server_RejectIncomingConnectionWithCode(int code); +int NBN_Server_RejectIncomingConnectionWithCode(NBN_Server *server, int code); /** * Reject the last client connection request without any specific code (default code is -1) @@ -401,7 +407,7 @@ int NBN_Server_RejectIncomingConnectionWithCode(int code); * * @return 0 when successful, -1 otherwise */ -int NBN_Server_RejectIncomingConnection(void); +int NBN_Server_RejectIncomingConnection(NBN_Server *server); /** * Retrieve the last connection to the game server. @@ -410,10 +416,10 @@ int NBN_Server_RejectIncomingConnection(void); * * @return A pointer to a NBN_Connection representing the new connection */ -NBN_ConnectionHandle *NBN_Server_GetIncomingConnection(void); +NBN_ConnectionHandle *NBN_Server_GetIncomingConnection(NBN_Server *server); // TODO: doc -NBN_Reader *NBN_Server_ReadConnectionRequestData(void); +NBN_Reader *NBN_Server_ReadConnectionRequestData(NBN_Server *server); /** * Return the information about the last disconnected client. @@ -423,7 +429,7 @@ NBN_Reader *NBN_Server_ReadConnectionRequestData(void); * * @return information about the last disconnected client */ -NBN_DisconnectionInfo NBN_Server_GetDisconnectionInfo(void); +NBN_DisconnectionInfo NBN_Server_GetDisconnectionInfo(NBN_Server *server); /** * Retrieve the info about the last received message. @@ -433,14 +439,14 @@ NBN_DisconnectionInfo NBN_Server_GetDisconnectionInfo(void); * * @return A structure containing information about the received message */ -NBN_MessageInfo NBN_Server_GetMessageInfo(void); +NBN_MessageInfo NBN_Server_GetMessageInfo(NBN_Server *server); /** * Retrieve network stats about the game server. * * @return A structure containing network related stats about the game server */ -NBN_ServerStats NBN_Server_GetStats(void); +NBN_ServerStats NBN_Server_GetStats(NBN_Server *server); #ifdef __EMSCRIPTEN__ @@ -495,21 +501,22 @@ static const char *default_ice_servers[] = {"stun:stun01.sipphone.com"}; .log_level = NBN_DEFAULT_RTC_LOG_LEVEL \ } -void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config); +void NBN_Client_SetWebRTC_Config(NBN_Client *client, NBN_WebRTC_Config config); +void NBN_Server_SetWebRTC_Config(NBN_Server *server, NBN_WebRTC_Config config); #endif #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) -void NBN_Client_SetPing(float v); -void NBN_Client_SetJitter(float v); -void NBN_Client_SetPacketLoss(float v); -void NBN_Client_SetPacketDuplication(float v); +void NBN_Client_SetPing(NBN_Client *client, float v); +void NBN_Client_SetJitter(NBN_Client *client, float v); +void NBN_Client_SetPacketLoss(NBN_Client *client, float v); +void NBN_Client_SetPacketDuplication(NBN_Client *client, float v); -void NBN_Server_SetPing(float v); -void NBN_Server_SetJitter(float v); -void NBN_Server_SetPacketLoss(float v); -void NBN_Server_SetPacketDuplication(float v); +void NBN_Server_SetPing(NBN_Server *server, float v); +void NBN_Server_SetJitter(NBN_Server *server, float v); +void NBN_Server_SetPacketLoss(NBN_Server *server, float v); +void NBN_Server_SetPacketDuplication(NBN_Server *server, float v); #else @@ -517,15 +524,15 @@ void NBN_Server_SetPacketDuplication(float v); do { \ } while (0); -#define NBN_Client_SetPing(v) NBN_PacketSimulator_Disabled -#define NBN_Client_SetJitter(v) NBN_PacketSimulator_Disabled -#define NBN_Client_SetPacketLoss(v) NBN_PacketSimulator_Disabled -#define NBN_Client_SetPacketDuplication(v) NBN_PacketSimulator_Disabled +#define NBN_Client_SetPing(client, v) NBN_PacketSimulator_Disabled +#define NBN_Client_SetJitter(client, v) NBN_PacketSimulator_Disabled +#define NBN_Client_SetPacketLoss(client, v) NBN_PacketSimulator_Disabled +#define NBN_Client_SetPacketDuplication(client, v) NBN_PacketSimulator_Disabled -#define NBN_Server_SetPing(v) NBN_PacketSimulator_Disabled -#define NBN_Server_SetJitter(v) NBN_PacketSimulator_Disabled -#define NBN_Server_SetPacketLoss(v) NBN_PacketSimulator_Disabled -#define NBN_Server_SetPacketDuplication(v) NBN_PacketSimulator_Disabled +#define NBN_Server_SetPing(server, v) NBN_PacketSimulator_Disabled +#define NBN_Server_SetJitter(server, v) NBN_PacketSimulator_Disabled +#define NBN_Server_SetPacketLoss(server, v) NBN_PacketSimulator_Disabled +#define NBN_Server_SetPacketDuplication(server, v) NBN_PacketSimulator_Disabled #endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ diff --git a/soak/client.c b/soak/client.c index be976af..25b6e3e 100644 --- a/soak/client.c +++ b/soak/client.c @@ -51,15 +51,18 @@ typedef struct { Soak_MessageEntry messages[SOAK_CLIENT_MAX_PENDING_MESSAGES]; } SoakChannel; -static bool connected = false; -static unsigned int done_channel_count = 0; +typedef struct { + unsigned int done_channel_count; + SoakChannel *channels; + NBN_Client *client; +} Soak_Client_State; static void GenerateRandomBytes(uint8_t *data, unsigned int length) { - for (int i = 0; i < length; i++) + for (unsigned int i = 0; i < length; i++) data[i] = rand() % 255 + 1; } -static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { +static int SendSoakMessages(NBN_Client *client, SoakChannel *channel, uint8_t channel_id) { unsigned int msg_count = channel->message_count; if (channel->sent_message_count < msg_count) { @@ -72,7 +75,7 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { log_info("Compute number of soak messages to send (sent: %d, pending: %d, remaining: %d)", channel->sent_message_count, pending_message_count, remaining_message_count); - unsigned int capacity = NBN_Client_GetChannelCurrentCapacity(channel->id); + unsigned int capacity = NBN_Client_GetChannelCurrentCapacity(client, channel->id); // make sure that we don't exceed channel capacity unsigned int max_pending_messages = (unsigned int)fmin(SOAK_CLIENT_MAX_PENDING_MESSAGES, capacity); @@ -88,7 +91,7 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { log_info("Will send %d soak messages this tick", send_message_count); - for (int i = 0; i < send_message_count; i++) { + for (unsigned int i = 0; i < send_message_count; i++) { int percent = rand() % 100 + 1; unsigned int min_len; unsigned int max_len; @@ -117,7 +120,7 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { log_info("Send soak message (id: %d, data length: %d)", msg_id, data_length); // TODO: support big messages - NBN_Writer *writer = NBN_Client_CreateMessage(SOAK_MESSAGE_SMALL, channel->id); + NBN_Writer *writer = NBN_Client_CreateMessage(client, SOAK_MESSAGE_SMALL, channel->id); if (!writer) { return -1; @@ -133,15 +136,15 @@ static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) { return 0; } -static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) { +static int HandleReceivedSoakMessage(Soak_Client_State *state, uint8_t channel_id) { assert(channel_id >= 2 && channel_id < SOAK_CHANNEL_COUNT + 2); - SoakChannel *channel = &channels[channel_id - 2]; + SoakChannel *channel = &state->channels[channel_id - 2]; unsigned int msg_id; unsigned int data_length; static uint8_t recv_buffer[SOAK_MESSAGE_BIG_MAX_DATA_LENGTH]; - NBN_Reader *reader = NBN_Client_ReadMessage(); + NBN_Reader *reader = NBN_Client_ReadMessage(state->client); if (SoakMessage_Read(reader, &msg_id, recv_buffer, &data_length) < 0) { log_error("Failed to read soak message"); @@ -186,10 +189,10 @@ static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) if (channel->last_recved_message_id == channel->message_count) { log_info("Received all soak message echoes on channel %d", channel_id); - done_channel_count++; + state->done_channel_count++; } - if (done_channel_count >= SOAK_CHANNEL_COUNT) { + if (state->done_channel_count >= SOAK_CHANNEL_COUNT) { log_info("Received all soak message echoes on all channels"); Soak_Stop(); @@ -199,13 +202,13 @@ static int HandleReceivedSoakMessage(uint8_t channel_id, SoakChannel *channels) return 0; } -static int HandleReceivedMessage(SoakChannel *channels) { - NBN_MessageInfo msg = NBN_Client_GetMessageInfo(); +static int HandleReceivedMessage(Soak_Client_State *state) { + NBN_MessageInfo msg = NBN_Client_GetMessageInfo(state->client); int ret; if (msg.type == SOAK_MESSAGE_SMALL) { - ret = HandleReceivedSoakMessage(msg.channel_id, channels); + ret = HandleReceivedSoakMessage(state, msg.channel_id); } else { log_error("Received unexpected message (type: %d, channel_id: %d)", msg.type, msg.channel_id); @@ -216,11 +219,11 @@ static int HandleReceivedMessage(SoakChannel *channels) { } static int Tick(void *data) { - SoakChannel *channels = (SoakChannel *)data; + Soak_Client_State *state = (Soak_Client_State *)data; int ev; - while ((ev = NBN_Client_Poll()) != NBN_CLIENT_NO_EVENT) { + while ((ev = NBN_Client_Poll(state->client)) != NBN_CLIENT_NO_EVENT) { if (ev < 0) { log_error("Error while poling client events"); return -1; @@ -228,19 +231,16 @@ static int Tick(void *data) { switch (ev) { case NBN_CLIENT_DISCONNECTED: - connected = false; - - log_info("Disconnected from server (code: %d)", NBN_Client_GetServerCloseCode()); + log_info("Disconnected from server (code: %d)", NBN_Client_GetServerCloseCode(state->client)); Soak_Stop(); return 0; case NBN_CLIENT_CONNECTED: log_info("Connected to server"); - connected = true; break; case NBN_CLIENT_MESSAGE_RECEIVED: - if (HandleReceivedMessage(channels) < 0) { + if (HandleReceivedMessage(state) < 0) { log_error("Error processing received message"); return -1; } @@ -248,18 +248,18 @@ static int Tick(void *data) { } } - if (connected) { + if (NBN_Client_IsConnected(state->client)) { for (unsigned int c = 0; c < SOAK_CHANNEL_COUNT; c++) { - SoakChannel *channel = &channels[c]; + SoakChannel *channel = &state->channels[c]; - if (SendSoakMessages(channel, channel->id) < 0) { + if (SendSoakMessages(state->client, channel, channel->id) < 0) { log_error("An error occured while sending messages on channel %d", c); return -1; } } } - if (NBN_Client_Flush() < 0) { + if (NBN_Client_Flush(state->client) < 0) { log_error("Failed to flush game client send queue. Exit"); return -1; @@ -269,6 +269,7 @@ static int Tick(void *data) { } int main(int argc, char *argv[]) { + srand(SOAK_SEED); NBN_SetLogLevel(NBN_LOG_DEBUG); if (Soak_ReadCommandLine(argc, argv) < 0) @@ -276,18 +277,21 @@ int main(int argc, char *argv[]) { SoakOptions options = Soak_GetOptions(); - NBN_Client_Init(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT); + log_info("Starting soak test client... (Packet loss: %f, Packet duplication: %f, Ping: %f, Jitter: %f)", + options.packet_loss, options.packet_duplication, options.ping, options.jitter); + + NBN_Client *client = NBN_Client_Create(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT); for (uint8_t c = 0; c < SOAK_CHANNEL_COUNT; c++) { uint8_t channel_id = - NBN_Client_CreateChannel(NBN_CHANNEL_RELIABLE, SOAK_CHANNEL_BUFFER_SIZE, SOAK_MAX_MESSAGE_SIZE); + NBN_Client_CreateChannel(client, NBN_CHANNEL_RELIABLE, SOAK_CHANNEL_BUFFER_SIZE, SOAK_MAX_MESSAGE_SIZE); // channels 0 and 1 are the default nbnet channels assert(channel_id == 2 + c); } - if (NBN_Client_Start() < 0) { - log_error("Failed to start game client. Exit"); + if (NBN_Client_Start(client) < 0) { + log_error("Failed to start client. Exit"); #ifdef __EMSCRIPTEN__ emscripten_force_exit(1); @@ -296,18 +300,22 @@ int main(int argc, char *argv[]) { #endif } - if (Soak_Init(argc, argv) < 0) { - log_error("Failed to initialize soak test"); - return 1; - } + NBN_Client_SetPing(client, options.ping); + NBN_Client_SetJitter(client, options.jitter); + NBN_Client_SetPacketLoss(client, options.packet_loss); + NBN_Client_SetPacketDuplication(client, options.packet_duplication); unsigned int message_count = options.message_count; unsigned int message_per_channel = message_count / SOAK_CHANNEL_COUNT; unsigned int leftover_message_count = message_count % SOAK_CHANNEL_COUNT; - SoakChannel *channels = (SoakChannel *)malloc(sizeof(SoakChannel) * SOAK_CHANNEL_COUNT); + + Soak_Client_State state; + state.channels = (SoakChannel *)malloc(sizeof(SoakChannel) * SOAK_CHANNEL_COUNT); + state.done_channel_count = 0; + state.client = client; for (int c = 0; c < SOAK_CHANNEL_COUNT; c++) { - SoakChannel *channel = &channels[c]; + SoakChannel *channel = &state.channels[c]; channel->id = 2 + c; // channels 0 and 1 are the default nbnet channels channel->next_msg_id = 1; @@ -321,12 +329,12 @@ int main(int argc, char *argv[]) { } } - channels[SOAK_CHANNEL_COUNT - 1].message_count += leftover_message_count; + state.channels[SOAK_CHANNEL_COUNT - 1].message_count += leftover_message_count; - int ret = Soak_MainLoop(Tick, channels); + int ret = Soak_MainLoop(Tick, &state); - NBN_Client_Stop(); - free(channels); + NBN_Client_Stop(client); + free(state.channels); #ifdef __EMSCRIPTEN__ emscripten_force_exit(ret); diff --git a/soak/server.c b/soak/server.c index f17cdb0..50da623 100644 --- a/soak/server.c +++ b/soak/server.c @@ -62,10 +62,10 @@ typedef struct { SoakChannel *channels; } SoakClient; -static void HandleNewConnection(void) { - NBN_Server_AcceptIncomingConnection(); +static void HandleNewConnection(NBN_Server *server) { + NBN_Server_AcceptIncomingConnection(server); - NBN_ConnectionHandle *conn = NBN_Server_GetIncomingConnection(); + NBN_ConnectionHandle *conn = NBN_Server_GetIncomingConnection(server); SoakClient *soak_client = (SoakClient *)malloc(sizeof(SoakClient)); soak_client->error = false; @@ -103,11 +103,11 @@ static void HandleClientDisconnection(NBN_DisconnectionInfo info) { free(soak_client); } -static void EchoReceivedSoakMessages(void) { +static void EchoReceivedSoakMessages(NBN_Server *server) { NBN_Client_Iterator it = 0; NBN_ConnectionHandle *conn; - while ((conn = NBN_Server_GetNextClient(&it))) { + while ((conn = NBN_Server_GetNextClient(server, &it))) { SoakClient *soak_client = (SoakClient *)conn->user_data; @@ -118,18 +118,18 @@ static void EchoReceivedSoakMessages(void) { for (unsigned int c = 0; c < SOAK_CHANNEL_COUNT; c++) { SoakChannel *channel = &soak_client->channels[c]; - int send_count = NBN_Server_GetChannelCurrentCapacity(channel->id, conn); + int send_count = NBN_Server_GetChannelCurrentCapacity(server, channel->id, conn); // make sure that we don't exceed channel capacity while (channel->echo_queue.count > 0 && --send_count >= 0) { Soak_MessageEntry *msg_entry = &channel->echo_queue.messages[channel->echo_queue.head]; NBN_Writer *writer = - NBN_Server_CreateMessage(SOAK_MESSAGE_SMALL, channel->id, conn); // TODO: support big + NBN_Server_CreateMessage(server, SOAK_MESSAGE_SMALL, channel->id, conn); // TODO: support big if (!writer) { log_error("Failed to send soak message to client %llu, closing client", conn->id); - if (NBN_Server_CloseClient(conn) < 0) { + if (NBN_Server_CloseClient(server, conn) < 0) { log_error("Failed to close client %llu", conn->id); abort(); } @@ -204,15 +204,15 @@ static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_ConnectionHandle *s return 0; } -static void HandleReceivedMessage(void) { - NBN_MessageInfo msg_info = NBN_Server_GetMessageInfo(); - NBN_Reader *reader = NBN_Server_ReadMessage(); +static void HandleReceivedMessage(NBN_Server *server) { + NBN_MessageInfo msg_info = NBN_Server_GetMessageInfo(server); + NBN_Reader *reader = NBN_Server_ReadMessage(server); SoakClient *soak_client = (SoakClient *)msg_info.sender->user_data; switch (msg_info.type) { case SOAK_MESSAGE_SMALL: if (HandleReceivedSoakMessage(reader, msg_info.sender, msg_info.channel_id) < 0) { - if (NBN_Server_CloseClient(msg_info.sender) < 0) { + if (NBN_Server_CloseClient(server, msg_info.sender) < 0) { log_error("Failed to close client %llu", msg_info.sender->id); abort(); } @@ -226,7 +226,7 @@ static void HandleReceivedMessage(void) { default: log_error("Received unexpected message (type: %d, channel_id: %d)", msg_info.type, msg_info.channel_id); - if (NBN_Server_CloseClient(msg_info.sender) < 0) { + if (NBN_Server_CloseClient(server, msg_info.sender) < 0) { log_error("Failed to close client %llu", msg_info.sender->id); abort(); } @@ -237,32 +237,31 @@ static void HandleReceivedMessage(void) { } static int Tick(void *data) { - (void)data; - + NBN_Server *server = (NBN_Server *)data; int ev; - while ((ev = NBN_Server_Poll()) != NBN_SERVER_NO_EVENT) { + while ((ev = NBN_Server_Poll(server)) != NBN_SERVER_NO_EVENT) { if (ev < 0) return -1; switch (ev) { case NBN_SERVER_NEW_CONNECTION: - HandleNewConnection(); + HandleNewConnection(server); break; case NBN_SERVER_DISCONNECTION: - HandleClientDisconnection(NBN_Server_GetDisconnectionInfo()); + HandleClientDisconnection(NBN_Server_GetDisconnectionInfo(server)); break; case NBN_SERVER_MESSAGE_RECEIVED: - HandleReceivedMessage(); + HandleReceivedMessage(server); break; } } - EchoReceivedSoakMessages(); + EchoReceivedSoakMessages(server); - if (NBN_Server_Flush() < 0) { + if (NBN_Server_Flush(server) < 0) { log_error("Failed to flush game server send queue. Exit"); return -1; @@ -274,6 +273,7 @@ static int Tick(void *data) { static void SigintHandler(int dummy) { Soak_Stop(); } int main(int argc, char *argv[]) { + srand(SOAK_SEED); signal(SIGINT, SigintHandler); NBN_SetLogLevel(NBN_LOG_DEBUG); @@ -283,32 +283,33 @@ int main(int argc, char *argv[]) { SoakOptions options = Soak_GetOptions(); - NBN_Server_Init(SOAK_PROTOCOL_NAME, SOAK_PORT); + log_info("Starting soak test server... (Packet loss: %f, Packet duplication: %f, Ping: %f, Jitter: %f)", + options.packet_loss, options.packet_duplication, options.ping, options.jitter); + + NBN_Server *server = NBN_Server_Create(SOAK_PROTOCOL_NAME, SOAK_PORT); for (uint8_t c = 0; c < SOAK_CHANNEL_COUNT; c++) { uint8_t channel_id = - NBN_Server_CreateChannel(NBN_CHANNEL_RELIABLE, SOAK_CHANNEL_BUFFER_SIZE, SOAK_MAX_MESSAGE_SIZE); + NBN_Server_CreateChannel(server, NBN_CHANNEL_RELIABLE, SOAK_CHANNEL_BUFFER_SIZE, SOAK_MAX_MESSAGE_SIZE); // channels 0 and 1 are the default nbnet channels assert(channel_id == 2 + c); } - if (NBN_Server_Start()) { + if (NBN_Server_Start(server)) { log_error("Failed to start game server"); return 1; } - if (Soak_Init(argc, argv) < 0) { - log_error("Failed to initialize soak test"); - - return 1; - } + NBN_Server_SetPing(server, options.ping); + NBN_Server_SetJitter(server, options.jitter); + NBN_Server_SetPacketLoss(server, options.packet_loss); + NBN_Server_SetPacketDuplication(server, options.packet_duplication); - int ret = Soak_MainLoop(Tick, NULL); + int ret = Soak_MainLoop(Tick, server); - NBN_Server_Stop(); - Soak_Deinit(); + NBN_Server_Stop(server); #ifdef WEBRTC_NATIVE NBN_WebRTC_Native_Unregister(); diff --git a/soak/soak.c b/soak/soak.c index 4d86181..d7c5df5 100644 --- a/soak/soak.c +++ b/soak/soak.c @@ -65,38 +65,6 @@ static void Usage(void) { #endif } -int Soak_Init(int argc, char *argv[]) { - srand(SOAK_SEED); - - SoakOptions options = Soak_GetOptions(); - - log_info("Soak test initialized (Packet loss: %f, Packet duplication: %f, Ping: %f, Jitter: %f)", - options.packet_loss, options.packet_duplication, options.ping, options.jitter); - - /* Packet simulator configuration */ -#ifdef SOAK_CLIENT - NBN_Client_SetPing(soak_options.ping); - NBN_Client_SetJitter(soak_options.jitter); - NBN_Client_SetPacketLoss(soak_options.packet_loss); - NBN_Client_SetPacketDuplication(soak_options.packet_duplication); -#endif - -#ifdef SOAK_SERVER - NBN_Server_SetPing(soak_options.ping); - NBN_Server_SetJitter(soak_options.jitter); - NBN_Server_SetPacketLoss(soak_options.packet_loss); - NBN_Server_SetPacketDuplication(soak_options.packet_duplication); -#endif - - return 0; -} - -void Soak_Deinit(void) { - log_info("Done."); - log_info("Memory report:\n"); - // TODO -} - int Soak_ReadCommandLine(int argc, char *argv[]) { struct cag_option options[] = { #ifdef SOAK_CLIENT diff --git a/soak/soak.h b/soak/soak.h index e1651f0..68c3656 100644 --- a/soak/soak.h +++ b/soak/soak.h @@ -68,7 +68,6 @@ typedef struct { } SoakOptions; int Soak_Init(int, char *[]); -void Soak_Deinit(void); int Soak_ReadCommandLine(int, char *[]); int Soak_MainLoop(int (*Tick)(void *), void *data); void Soak_Stop(void); From 893f6f68c2c7a3e76e9c8c4dffb9b25a03c2ebe2 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 8 Apr 2026 17:27:46 +0200 Subject: [PATCH 83/85] fix emscripten driver issues --- nbnet.c | 51 ++++++++++++++++++++++++++++++++++++--------------- nbnet.h | 3 ++- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/nbnet.c b/nbnet.c index 8e94f48..d0672f0 100644 --- a/nbnet.c +++ b/nbnet.c @@ -34,6 +34,7 @@ #include #include #include +#include #if defined(_WIN32) || defined(_WIN64) @@ -85,7 +86,6 @@ typedef int socklen_t; #elif defined(NBN_PLATFORM_UNIX) || defined(NBN_PLATFORM_MAC) -#include #include #include #include @@ -478,9 +478,7 @@ struct NBN_Server { #if defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) struct { NBN_WebRTC_Config cfg; -#ifdef NBN_WEBRTC_NATIVE int ws_server; -#endif // NBN_WEBRTC_NATIVE } webrtc; #endif // defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) } driver_data; @@ -600,7 +598,7 @@ static NBN_Driver nbn_udp_driver = {.id = NBN_DRIVER_UDP, static int WebRTC_Client_Start(NBN_Client *client, const char *host, uint16_t port); static void WebRTC_Client_Stop(NBN_Client *client); static int WebRTC_Client_RecvPackets(NBN_Client *client); -static int WebRTC_Client_SendPacket(NBN_Client *client, NBN_Packet *packet); +static int WebRTC_Client_SendPacket(NBN_Client *client, NBN_Packet *packet, NBN_Connection *connection); static int WebRTC_Server_Start(NBN_Server *server, uint16_t port); static void WebRTC_Server_Stop(NBN_Server *server); @@ -3352,11 +3350,18 @@ extern int __js_game_client_dequeue_packet(uint8_t *); extern int __js_game_client_send_packet(uint8_t *, unsigned int); extern void __js_game_client_close(void); -void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config) { nbn_wrtc_cfg = config; } +void NBN_Client_SetWebRTC_Config(NBN_Client *client, NBN_WebRTC_Config config) { + client->driver_data.webrtc.cfg = config; +} + +void NBN_Server_SetWebRTC_Config(NBN_Server *server, NBN_WebRTC_Config config) { + server->driver_data.webrtc.cfg = config; +} static int WebRTC_Server_Start(NBN_Server *server, uint16_t port) { - __js_game_server_init(server->endpoint.protocol_id, nbn_wrtc_cfg.enable_tls, nbn_wrtc_cfg.key_path, - nbn_wrtc_cfg.cert_path); + NBN_WebRTC_Config cfg = server->driver_data.webrtc.cfg; + + __js_game_server_init(server->endpoint.protocol_id, cfg.enable_tls, cfg.key_path, cfg.cert_path); if (__js_game_server_start(port) < 0) return -1; @@ -3364,7 +3369,11 @@ static int WebRTC_Server_Start(NBN_Server *server, uint16_t port) { return 0; } -static void WebRTC_Server_Stop(NBN_Server *server) { __js_game_server_stop(); } +static void WebRTC_Server_Stop(NBN_Server *server) { + (void)server; + + __js_game_server_stop(); +} static int WebRTC_Server_RecvPackets(NBN_Server *server) { static NBN_Packet packet = {0}; @@ -3373,7 +3382,7 @@ static int WebRTC_Server_RecvPackets(NBN_Server *server) { while ((len = __js_game_server_dequeue_packet(&peer_id, packet.buffer)) > 0) { NBN_Connection_ID conn_id = NBN_BuildConnectionHash(peer_id, NBN_DRIVER_WEBRTC_EMSCRIPTEN); - NBN_ConnectionHandle *handle = NBN_Server_GetConnection(conn_id); + NBN_ConnectionHandle *handle = NBN_Server_GetConnection(server, conn_id); NBN_Connection *conn = NULL; if (handle == NULL) { @@ -3383,7 +3392,7 @@ static int WebRTC_Server_RecvPackets(NBN_Server *server) { conn->driver_data.webrtc.peer_id = peer_id; conn->driver_data.endpoint_ptr = server; - ServerDriver_OnClientConnected(conn); + ServerDriver_OnClientConnected(server, conn); } else { conn = HANDLE_TO_CONN(handle); } @@ -3400,17 +3409,22 @@ static int WebRTC_Server_RecvPackets(NBN_Server *server) { } static void WebRTC_Server_CleanupConnection(NBN_Server *server, NBN_Connection *conn) { - NBN_Assert(conn != NULL); + (void)server; + NBN_Assert(conn != NULL); __js_game_server_close_client_peer(conn->driver_data.webrtc.peer_id); } -static int WebRTC_Server_SendPacketTo(NBN_Endpoint *server, NBN_Packet *packet, NBN_Connection *conn) { +static int WebRTC_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_Connection *conn) { + (void)server; + return __js_game_server_send_packet_to(packet->buffer, packet->size, conn->driver_data.webrtc.peer_id); } static int WebRTC_Client_Start(NBN_Client *client, const char *host, uint16_t port) { - __js_game_client_init(client->endpoint.protocol_id, nbn_wrtc_cfg.enable_tls); + NBN_WebRTC_Config cfg = client->driver_data.webrtc.cfg; + + __js_game_client_init(client->endpoint.protocol_id, cfg.enable_tls); int res; @@ -3420,7 +3434,11 @@ static int WebRTC_Client_Start(NBN_Client *client, const char *host, uint16_t po return 0; } -static void WebRTC_Client_Stop(NBN_Client *client) { __js_game_client_close(); } +static void WebRTC_Client_Stop(NBN_Client *client) { + (void)client; + + __js_game_client_close(); +} static int WebRTC_Client_RecvPackets(NBN_Client *client) { static NBN_Packet packet = {0}; @@ -3438,7 +3456,10 @@ static int WebRTC_Client_RecvPackets(NBN_Client *client) { return 0; } -static int WebRTC_Client_SendPacket(NBN_Endpoint *client, NBN_Packet *packet, NBN_Connection *connection) { +static int WebRTC_Client_SendPacket(NBN_Client *client, NBN_Packet *packet, NBN_Connection *connection) { + (void)client; + (void)connection; + return __js_game_client_send_packet(packet->buffer, packet->size); } diff --git a/nbnet.h b/nbnet.h index 3ce1776..14ece59 100644 --- a/nbnet.h +++ b/nbnet.h @@ -458,7 +458,8 @@ typedef struct NBN_WebRTC_Config { const char *key_path; } NBN_WebRTC_Config; -void NBN_WebRTC_SetConfig(NBN_WebRTC_Config config); +void NBN_Client_SetWebRTC_Config(NBN_Client *client, NBN_WebRTC_Config config); +void NBN_Server_SetWebRTC_Config(NBN_Server *server, NBN_WebRTC_Config config); // TODO: ice servers currently hard coded in driver js code #define NBN_WEBRTC_DEFAULT_CONFIG (NBN_WebRTC_Config){.enable_tls = false, .cert_path = NULL, .key_path = NULL}; From 4b312b18cb079da9e81d3df25b442f034873bd52 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Sun, 12 Apr 2026 19:36:06 +0200 Subject: [PATCH 84/85] remove usage of static variables --- nbnet.c | 107 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 54 insertions(+), 53 deletions(-) diff --git a/nbnet.c b/nbnet.c index d0672f0..211f895 100644 --- a/nbnet.c +++ b/nbnet.c @@ -443,6 +443,7 @@ struct NBN_Endpoint { NBN_Channel_Config *channels; uint8_t default_reliable_channel; uint8_t default_unreliable_channel; + NBN_Packet read_packet; #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) NBN_PacketSimulator packet_simulator; #endif @@ -1330,7 +1331,7 @@ static int Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connecti for (int i = 0; i < packet->header.messages_count; i++) { LogDebug("Reading message number %d from packet %d", i, packet->header.seq_number); - static NBN_MessageHeader header = {0}; + NBN_MessageHeader header = {0}; int msg_len = Connection_ReadNextMessageHeader(&msg_reader, &header); if (msg_len < 0) { @@ -1384,20 +1385,20 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn double time) { LogDebug("Flushing all channels"); - static NBN_Packet packet = {0}; - static NBN_PacketEntry *packet_entry; + NBN_PacketEntry *packet_entry; + NBN_Packet *packet = &endpoint->read_packet; unsigned int sent_packet_count = 0; unsigned int sent_bytes = 0; - Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); + Connection_InitOutgoingPacket(connection, protocol_id, packet, &packet_entry); for (unsigned int i = 0; i < endpoint->channel_count; i++) { NBN_Channel *channel = &connection->channels[i]; LogDebug("Flushing channel %d (message count: %d)", channel->id, channel->outgoing_message_count); - static NBN_Message out_msg; + NBN_Message out_msg = {0}; unsigned int j = 0; // TODO: use bandwidth to determine how many packets to send at most @@ -1407,26 +1408,26 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn uint16_t msg_id = out_msg.header.id; uint16_t msg_len = out_msg.header.length; bool message_sent = false; - NBN_PacketResult ret = Packet_WriteMessage(&packet, &out_msg); + NBN_PacketResult ret = Packet_WriteMessage(packet, &out_msg); if (ret == NBN_PACKET_WRITE_OK) { message_sent = true; } else if (ret == NBN_PACKET_WRITE_NO_SPACE) { - if (Connection_SendPacket(endpoint, connection, &packet, packet_entry, time, endpoint->is_server) < 0) { - LogError("Failed to send packet %d", packet.header.seq_number); + if (Connection_SendPacket(endpoint, connection, packet, packet_entry, time, endpoint->is_server) < 0) { + LogError("Failed to send packet %d", packet->header.seq_number); return NBN_ERROR; } sent_packet_count++; - sent_bytes += packet.size; + sent_bytes += packet->size; - Connection_InitOutgoingPacket(connection, protocol_id, &packet, &packet_entry); + Connection_InitOutgoingPacket(connection, protocol_id, packet, &packet_entry); - NBN_PacketResult ret = Packet_WriteMessage(&packet, &out_msg); + NBN_PacketResult ret = Packet_WriteMessage(packet, &out_msg); if (ret != NBN_PACKET_WRITE_OK) { - LogError("Failed to send packet %d", packet.header.seq_number); + LogError("Failed to send packet %d", packet->header.seq_number); return NBN_ERROR; } @@ -1434,13 +1435,13 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn message_sent = true; } else if (ret == NBN_PACKET_WRITE_ERROR) { LogError("Failed to write message %d of type %d to packet %d", msg_id, msg_type, - packet.header.seq_number); + packet->header.seq_number); return NBN_ERROR; } if (message_sent) { - LogDebug("Message %d added to packet %d (length: %d, type: %d)", msg_id, packet.header.seq_number, + LogDebug("Message %d added to packet %d (length: %d, type: %d)", msg_id, packet->header.seq_number, msg_len, msg_type); Channel_UpdateMessageSendTime(channel, msg_id, time); @@ -1454,13 +1455,13 @@ static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *conn } } - if (Connection_SendPacket(endpoint, connection, &packet, packet_entry, time, endpoint->is_server) < 0) { - LogError("Failed to send packet %d to connection %lld", packet.header.seq_number, connection->handle.id); + if (Connection_SendPacket(endpoint, connection, packet, packet_entry, time, endpoint->is_server) < 0) { + LogError("Failed to send packet %d to connection %lld", packet->header.seq_number, connection->handle.id); return NBN_ERROR; } - sent_bytes += packet.size; + sent_bytes += packet->size; sent_packet_count++; double t = time - connection->last_flush_time; @@ -1644,13 +1645,13 @@ static int Connection_SendPacket(NBN_Endpoint *endpoint, NBN_Connection *connect if (connection->is_stale) return 0; - return connection->driver->impl.serv_send_packet_to(endpoint, packet, connection); + return connection->driver->impl.serv_send_packet_to((NBN_Server *)endpoint, packet, connection); #endif } else { #if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) return PacketSimulator_EnqueuePacket(&endpoint->packet_simulator, packet, connection); #else - return connection->driver->impl.cli_send_packet(endpoint, packet, connection); + return connection->driver->impl.cli_send_packet((NBN_Client *)endpoint, packet, connection); #endif } } @@ -2187,7 +2188,7 @@ NBN_Client_Event NBN_Client_Poll(NBN_Client *client) { } int NBN_Client_Flush(NBN_Client *client) { - return Connection_FlushChannels(&client->endpoint, client->server_connection, client->endpoint.protocol_id, + return Connection_FlushChannels((NBN_Endpoint *)client, client->server_connection, client->endpoint.protocol_id, client->endpoint.time); } @@ -2604,7 +2605,7 @@ int NBN_Server_Flush(NBN_Server *server) { NBN_Assert(!(client->is_closed && client->is_stale)); - if (!client->is_stale && Connection_FlushChannels(&server->endpoint, client, server->endpoint.protocol_id, + if (!client->is_stale && Connection_FlushChannels((NBN_Endpoint *)server, client, server->endpoint.protocol_id, server->endpoint.time) < 0) { return NBN_ERROR; } @@ -3195,12 +3196,12 @@ static int UDP_Server_Start(NBN_Server *server, uint16_t port) { static void UDP_Server_Stop(NBN_Server *server) { UDP_DeinitSocket(server->driver_data.udp.sock); } static int UDP_Server_RecvPackets(NBN_Server *server) { - static NBN_Packet packet = {0}; + NBN_Packet *packet = &server->endpoint.read_packet; SOCKADDR_IN src_addr; socklen_t src_addr_len = sizeof(src_addr); while (true) { - int bytes = recvfrom(server->driver_data.udp.sock, (char *)packet.buffer, sizeof(packet.buffer), 0, + int bytes = recvfrom(server->driver_data.udp.sock, (char *)packet->buffer, sizeof(packet->buffer), 0, (SOCKADDR *)&src_addr, &src_addr_len); if (bytes <= 0) @@ -3209,7 +3210,7 @@ static int UDP_Server_RecvPackets(NBN_Server *server) { if (bytes < NBN_PACKET_HEADER_SIZE) continue; - if (Packet_InitRead(&packet, server->endpoint.protocol_id, bytes) < 0) { + if (Packet_InitRead(packet, server->endpoint.protocol_id, bytes) < 0) { LogDebug("Discarded invalid packet"); continue; } @@ -3220,9 +3221,9 @@ static int UDP_Server_RecvPackets(NBN_Server *server) { LogDebug("Received valid UDP packet from %d:%d", ip_address.host, ip_address.port); - packet.sender = UDP_FindOrCreateClientConnectionByAddress(server, ip_address); + packet->sender = UDP_FindOrCreateClientConnectionByAddress(server, ip_address); - ServerDriver_OnClientPacketReceived(server, &packet); + ServerDriver_OnClientPacketReceived(server, packet); } return 0; @@ -3269,12 +3270,12 @@ static void UDP_Client_Stop(NBN_Client *client) { UDP_DeinitSocket(client->drive static int UDP_Client_RecvPackets(NBN_Client *client) { NBN_IPAddress server_address = client->server_connection->driver_data.udp.ip_address; - static NBN_Packet packet = {0}; + NBN_Packet *packet = &client->endpoint.read_packet; SOCKADDR_IN src_addr; socklen_t src_addr_len = sizeof(src_addr); while (true) { - int bytes = recvfrom(client->driver_data.udp.sock, (char *)packet.buffer, sizeof(packet.buffer), 0, + int bytes = recvfrom(client->driver_data.udp.sock, (char *)packet->buffer, sizeof(packet->buffer), 0, (SOCKADDR *)&src_addr, &src_addr_len); if (bytes <= 0) @@ -3289,14 +3290,14 @@ static int UDP_Client_RecvPackets(NBN_Client *client) { if (host != server_address.host || port != server_address.port) continue; - if (Packet_InitRead(&packet, client->endpoint.protocol_id, bytes) < 0) { + if (Packet_InitRead(packet, client->endpoint.protocol_id, bytes) < 0) { LogDebug("Discarded invalid packet"); continue; } - packet.sender = client->server_connection; + packet->sender = client->server_connection; - ClientDriver_OnPacketReceived(client, &packet); + ClientDriver_OnPacketReceived(client, packet); } return 0; @@ -3376,11 +3377,11 @@ static void WebRTC_Server_Stop(NBN_Server *server) { } static int WebRTC_Server_RecvPackets(NBN_Server *server) { - static NBN_Packet packet = {0}; + NBN_Packet *packet = &server->endpoint.read_packet; uint32_t peer_id; unsigned int len; - while ((len = __js_game_server_dequeue_packet(&peer_id, packet.buffer)) > 0) { + while ((len = __js_game_server_dequeue_packet(&peer_id, packet->buffer)) > 0) { NBN_Connection_ID conn_id = NBN_BuildConnectionHash(peer_id, NBN_DRIVER_WEBRTC_EMSCRIPTEN); NBN_ConnectionHandle *handle = NBN_Server_GetConnection(server, conn_id); NBN_Connection *conn = NULL; @@ -3397,12 +3398,12 @@ static int WebRTC_Server_RecvPackets(NBN_Server *server) { conn = HANDLE_TO_CONN(handle); } - if (Packet_InitRead(&packet, server->endpoint.protocol_id, len) < 0) + if (Packet_InitRead(packet, server->endpoint.protocol_id, len) < 0) continue; - packet.sender = conn; + packet->sender = conn; - ServerDriver_OnClientPacketReceived(server, &packet); + ServerDriver_OnClientPacketReceived(server, packet); } return 0; @@ -3441,16 +3442,16 @@ static void WebRTC_Client_Stop(NBN_Client *client) { } static int WebRTC_Client_RecvPackets(NBN_Client *client) { - static NBN_Packet packet = {0}; + NBN_Packet *packet = &client->endpoint.read_packet; unsigned int len; - while ((len = __js_game_client_dequeue_packet(packet.buffer)) > 0) { - if (Packet_InitRead(&packet, client->endpoint.protocol_id, len) < 0) + while ((len = __js_game_client_dequeue_packet(packet->buffer)) > 0) { + if (Packet_InitRead(packet, client->endpoint.protocol_id, len) < 0) continue; - packet.sender = client->server_connection; + packet->sender = client->server_connection; - ClientDriver_OnPacketReceived(client, &packet); + ClientDriver_OnPacketReceived(client, packet); } return 0; @@ -3833,8 +3834,8 @@ static void WebRTC_Native_Server_Stop(NBN_Server *server) { } static int WebRTC_Native_Server_RecvPackets(NBN_Server *server) { - static NBN_Packet packet = {0}; - const int buffer_size = sizeof(packet.buffer); + NBN_Packet *packet = &server->endpoint.read_packet; + const int buffer_size = sizeof(packet->buffer); int size = buffer_size; for (unsigned int i = 0; i < hmlen(server->clients); i++) { @@ -3845,14 +3846,14 @@ static int WebRTC_Native_Server_RecvPackets(NBN_Server *server) { int channel_id = conn->driver_data.webrtc.channel_id; - while (rtcReceiveMessage(channel_id, (char *)packet.buffer, &size) == RTC_ERR_SUCCESS) { - if (Packet_InitRead(&packet, server->endpoint.protocol_id, size) < 0) + while (rtcReceiveMessage(channel_id, (char *)packet->buffer, &size) == RTC_ERR_SUCCESS) { + if (Packet_InitRead(packet, server->endpoint.protocol_id, size) < 0) continue; - packet.sender = conn; + packet->sender = conn; size = buffer_size; - ServerDriver_OnClientPacketReceived(server, &packet); + ServerDriver_OnClientPacketReceived(server, packet); } } @@ -4018,19 +4019,19 @@ static void WebRTC_Native_Client_Stop(NBN_Client *client) { } static int WebRTC_Native_Client_RecvPackets(NBN_Client *client) { - static NBN_Packet packet = {0}; - const int buffer_size = sizeof(packet.buffer); + NBN_Packet *packet = &client->endpoint.read_packet; + const int buffer_size = sizeof(packet->buffer); int size = buffer_size; int channel_id = client->server_connection->driver_data.webrtc.channel_id; - while (rtcReceiveMessage(channel_id, (char *)packet.buffer, &size) == RTC_ERR_SUCCESS) { - if (Packet_InitRead(&packet, client->endpoint.protocol_id, size) < 0) + while (rtcReceiveMessage(channel_id, (char *)packet->buffer, &size) == RTC_ERR_SUCCESS) { + if (Packet_InitRead(packet, client->endpoint.protocol_id, size) < 0) continue; - packet.sender = NULL; + packet->sender = NULL; size = buffer_size; - ClientDriver_OnPacketReceived(client, &packet); + ClientDriver_OnPacketReceived(client, packet); } return 0; From 505acec005ab5664241f777663dc8d18d333f544 Mon Sep 17 00:00:00 2001 From: BIAGINI Nathan Date: Wed, 29 Apr 2026 16:55:01 +0200 Subject: [PATCH 85/85] fix segfault: initialize server->clients to NULL --- nbnet.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nbnet.c b/nbnet.c index 211f895..f26fa05 100644 --- a/nbnet.c +++ b/nbnet.c @@ -2353,8 +2353,9 @@ NBN_Server *NBN_Server_Create(const char *protocol_name, uint16_t port) { NBN_Server *server = malloc(sizeof(NBN_Server)); server->config = (NBN_Server_Config){.protocol_name = protocol_name, .port = port}; - server->server_data_writer.position = 0; + server->clients = NULL; + hmdefault(server->clients, NULL); server->endpoint.default_reliable_channel = NBN_Server_CreateChannel(