-
Notifications
You must be signed in to change notification settings - Fork 30
/
nbnet.h
6064 lines (4602 loc) · 185 KB
/
nbnet.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
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 NBNET_H
#define NBNET_H
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#include <assert.h>
#if defined(_WIN32) || defined(_WIN64)
#include <winsock2.h>
#include <windows.h>
#include <wincrypt.h>
#define NBNET_WINDOWS
#endif
#ifndef NBNET_WINDOWS
#include <sys/time.h>
#include <time.h>
#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
#define NBN_Abort abort
#endif /* NBN_Abort */
#define NBN_ERROR -1
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
typedef struct NBN_ConnectionVector
{
NBN_Connection **connections;
unsigned int count;
unsigned int capacity;
} 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_BYTE_ARRAY_MESSAGE,
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 */
#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 >= (int64_t)min && v <= (int64_t)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
{
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
typedef struct NBN_WriteStream
{
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 *);
#pragma endregion /* NBN_MeasureStream */
#pragma endregion /* Serialization */
#pragma region NBN_Message
#define NBN_MAX_CHANNELS 8
#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) */
typedef int (*NBN_MessageSerializer)(void *, NBN_Stream *);
typedef void *(*NBN_MessageBuilder)(void);
typedef void (*NBN_MessageDestructor)(void *);
typedef struct NBN_MessageHeader
{
uint16_t id;
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;
} NBN_Message;
/**
* Information about a received message.
*/
typedef struct NBN_MessageInfo
{
/** User defined message's type */
uint8_t type;
/** Channel the message was received on */
uint8_t channel_id;
/** Message's data */
void *data;
/**
* The message's sender.
*
* On the client side, it will always be 0 (all received messages come from the game server).
*/
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
/*
* 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 255 /* Maximum value of uint8_t, see packet header */
#define NBN_PACKET_HEADER_SIZE sizeof(NBN_PacketHeader)
/* 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;
typedef struct NBN_PacketHeader
{
uint32_t protocol_id;
uint16_t seq_number;
uint16_t ack;
uint32_t ack_bits;
uint8_t messages_count;
} NBN_PacketHeader;
typedef struct NBN_Packet
{
NBN_PacketHeader header;
NBN_PacketMode mode;
struct NBN_Connection *sender; /* not serialized, fill by the network driver upon reception */
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_Seal(NBN_Packet *, NBN_Connection *);
#pragma endregion /* NBN_Packet */
#pragma region NBN_MessageChunk
/* 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 */
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 region NBN_Channel
#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 (NBN_MAX_CHANNELS - 1)
/* 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)
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;
struct NBN_Channel
{
uint8_t id;
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;
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_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];
bool (*AddReceivedMessage)(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 *);
};
void NBN_Channel_Destroy(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
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
{
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
#define NBN_MAX_PACKET_ENTRIES 1024
/* 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
{
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 struct NBN_ConnectionDebugCallback {
void (*OnMessageAddedToRecvQueue)(NBN_Connection *, NBN_Message *);
} NBN_ConnectionDebugCallback;
#endif /* NBN_DEBUG */
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 */
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) */
NBN_ConnectionStats stats;
void *driver_data; /* Data attached to the connection by the underlying driver */
#ifdef NBN_DEBUG
NBN_ConnectionDebugCallback debug_callbacks;
#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;
};
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);
#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 union NBN_EventData
{
NBN_MessageInfo message_info;
NBN_Connection *connection;
NBN_ConnectionHandle connection_handle;
} 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 *);
#pragma endregion /* NBN_EventQueue */
#pragma region Packet simulator
#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 NBNET_WINDOWS
#include <pthread.h> // 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; }
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 NBNET_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;
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 *);
#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_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")
#endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */
#pragma endregion /* Packet simulator */
#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 \
|| type == NBN_RPC_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 */
#ifdef NBN_DEBUG
NBN_ConnectionDebugCallback debug_callbacks;
#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,
/* Client is disconnected from the server */
NBN_DISCONNECTED,
/* Client has received a message from the server */
NBN_MESSAGE_RECEIVED
};
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 */
NBN_Event last_event;
int closed_code;
} NBN_GameClient;
extern NBN_GameClient nbn_game_client;
/**
* Start the game client and send a connection request to the 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 host Host to connect to
* @param port Port to connect to
*
* @return 0 when successully started, -1 otherwise
*/
int NBN_GameClient_Start(const char *protocol_name, const char *host, uint16_t port);
/**
* Same as NBN_GameClient_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 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
*
* @return 0 when successully started, -1 otherwise
*/