From 7eec0dc57d531703175984bc97f2184c1f2d99a4 Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 5 May 2015 12:51:38 -0700 Subject: [PATCH 1/4] add enet for some client/server tests --- build3/premake4.lua | 4 +- .../ImportURDFDemo/ImportURDFSetup.cpp | 1 + examples/ThirdPartyLibs/enet/callbacks.c | 47 + examples/ThirdPartyLibs/enet/compress.c | 654 ++++++ examples/ThirdPartyLibs/enet/host.c | 491 +++++ .../enet/include/enet/callbacks.h | 27 + .../ThirdPartyLibs/enet/include/enet/enet.h | 571 +++++ .../ThirdPartyLibs/enet/include/enet/list.h | 43 + .../enet/include/enet/protocol.h | 199 ++ .../ThirdPartyLibs/enet/include/enet/time.h | 18 + .../ThirdPartyLibs/enet/include/enet/types.h | 13 + .../ThirdPartyLibs/enet/include/enet/unix.h | 45 + .../enet/include/enet/utility.h | 12 + .../ThirdPartyLibs/enet/include/enet/win32.h | 58 + examples/ThirdPartyLibs/enet/list.c | 75 + examples/ThirdPartyLibs/enet/packet.c | 165 ++ examples/ThirdPartyLibs/enet/peer.c | 959 +++++++++ examples/ThirdPartyLibs/enet/premake4.lua | 31 + examples/ThirdPartyLibs/enet/protocol.c | 1899 +++++++++++++++++ examples/ThirdPartyLibs/enet/unix.c | 475 +++++ examples/ThirdPartyLibs/enet/win32.c | 368 ++++ test/enet/client/main.cpp | 179 ++ test/enet/client/premake4.lua | 25 + test/enet/server/main.cpp | 112 + test/enet/server/premake4.lua | 26 + 25 files changed, 6496 insertions(+), 1 deletion(-) create mode 100644 examples/ThirdPartyLibs/enet/callbacks.c create mode 100644 examples/ThirdPartyLibs/enet/compress.c create mode 100644 examples/ThirdPartyLibs/enet/host.c create mode 100644 examples/ThirdPartyLibs/enet/include/enet/callbacks.h create mode 100644 examples/ThirdPartyLibs/enet/include/enet/enet.h create mode 100644 examples/ThirdPartyLibs/enet/include/enet/list.h create mode 100644 examples/ThirdPartyLibs/enet/include/enet/protocol.h create mode 100644 examples/ThirdPartyLibs/enet/include/enet/time.h create mode 100644 examples/ThirdPartyLibs/enet/include/enet/types.h create mode 100644 examples/ThirdPartyLibs/enet/include/enet/unix.h create mode 100644 examples/ThirdPartyLibs/enet/include/enet/utility.h create mode 100644 examples/ThirdPartyLibs/enet/include/enet/win32.h create mode 100644 examples/ThirdPartyLibs/enet/list.c create mode 100644 examples/ThirdPartyLibs/enet/packet.c create mode 100644 examples/ThirdPartyLibs/enet/peer.c create mode 100644 examples/ThirdPartyLibs/enet/premake4.lua create mode 100644 examples/ThirdPartyLibs/enet/protocol.c create mode 100644 examples/ThirdPartyLibs/enet/unix.c create mode 100644 examples/ThirdPartyLibs/enet/win32.c create mode 100644 test/enet/client/main.cpp create mode 100644 test/enet/client/premake4.lua create mode 100644 test/enet/server/main.cpp create mode 100644 test/enet/server/premake4.lua diff --git a/build3/premake4.lua b/build3/premake4.lua index 797e5c4f4..9a6965110 100644 --- a/build3/premake4.lua +++ b/build3/premake4.lua @@ -122,7 +122,9 @@ include "../examples/HelloWorld" include "../examples/BasicDemo" - + include "../examples/ThirdPartyLibs/enet" + include "../test/enet/client" + include "../test/enet/server" if not _OPTIONS["without-gtest"] then include "../test/gtest-1.7.0" diff --git a/examples/Importers/ImportURDFDemo/ImportURDFSetup.cpp b/examples/Importers/ImportURDFDemo/ImportURDFSetup.cpp index 06fb8bd79..a3160a799 100644 --- a/examples/Importers/ImportURDFDemo/ImportURDFSetup.cpp +++ b/examples/Importers/ImportURDFDemo/ImportURDFSetup.cpp @@ -106,6 +106,7 @@ ImportUrdfSetup::ImportUrdfSetup(struct GUIHelperInterface* helper, int option, do { result = fscanf(f,"%s",fileName); + b3Printf("urdf_files.txt entry %s",fileName); if (result==1) { gFileNameArray.push_back(fileName); diff --git a/examples/ThirdPartyLibs/enet/callbacks.c b/examples/ThirdPartyLibs/enet/callbacks.c new file mode 100644 index 000000000..f94128256 --- /dev/null +++ b/examples/ThirdPartyLibs/enet/callbacks.c @@ -0,0 +1,47 @@ +/** + @file callbacks.c + @brief ENet callback functions +*/ +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +static ENetCallbacks callbacks = { malloc, free, abort }; + +int +enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits) +{ + if (version < ENET_VERSION_CREATE (1, 3, 0)) + return -1; + + if (inits -> malloc != NULL || inits -> free != NULL) + { + if (inits -> malloc == NULL || inits -> free == NULL) + return -1; + + callbacks.malloc = inits -> malloc; + callbacks.free = inits -> free; + } + + if (inits -> no_memory != NULL) + callbacks.no_memory = inits -> no_memory; + + return enet_initialize (); +} + +void * +enet_malloc (size_t size) +{ + void * memory = callbacks.malloc (size); + + if (memory == NULL) + callbacks.no_memory (); + + return memory; +} + +void +enet_free (void * memory) +{ + callbacks.free (memory); +} + diff --git a/examples/ThirdPartyLibs/enet/compress.c b/examples/ThirdPartyLibs/enet/compress.c new file mode 100644 index 000000000..784489a78 --- /dev/null +++ b/examples/ThirdPartyLibs/enet/compress.c @@ -0,0 +1,654 @@ +/** + @file compress.c + @brief An adaptive order-2 PPM range coder +*/ +#define ENET_BUILDING_LIB 1 +#include +#include "enet/enet.h" + +typedef struct _ENetSymbol +{ + /* binary indexed tree of symbols */ + enet_uint8 value; + enet_uint8 count; + enet_uint16 under; + enet_uint16 left, right; + + /* context defined by this symbol */ + enet_uint16 symbols; + enet_uint16 escapes; + enet_uint16 total; + enet_uint16 parent; +} ENetSymbol; + +/* adaptation constants tuned aggressively for small packet sizes rather than large file compression */ +enum +{ + ENET_RANGE_CODER_TOP = 1<<24, + ENET_RANGE_CODER_BOTTOM = 1<<16, + + ENET_CONTEXT_SYMBOL_DELTA = 3, + ENET_CONTEXT_SYMBOL_MINIMUM = 1, + ENET_CONTEXT_ESCAPE_MINIMUM = 1, + + ENET_SUBCONTEXT_ORDER = 2, + ENET_SUBCONTEXT_SYMBOL_DELTA = 2, + ENET_SUBCONTEXT_ESCAPE_DELTA = 5 +}; + +/* context exclusion roughly halves compression speed, so disable for now */ +#undef ENET_CONTEXT_EXCLUSION + +typedef struct _ENetRangeCoder +{ + /* only allocate enough symbols for reasonable MTUs, would need to be larger for large file compression */ + ENetSymbol symbols[4096]; +} ENetRangeCoder; + +void * +enet_range_coder_create (void) +{ + ENetRangeCoder * rangeCoder = (ENetRangeCoder *) enet_malloc (sizeof (ENetRangeCoder)); + if (rangeCoder == NULL) + return NULL; + + return rangeCoder; +} + +void +enet_range_coder_destroy (void * context) +{ + ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context; + if (rangeCoder == NULL) + return; + + enet_free (rangeCoder); +} + +#define ENET_SYMBOL_CREATE(symbol, value_, count_) \ +{ \ + symbol = & rangeCoder -> symbols [nextSymbol ++]; \ + symbol -> value = value_; \ + symbol -> count = count_; \ + symbol -> under = count_; \ + symbol -> left = 0; \ + symbol -> right = 0; \ + symbol -> symbols = 0; \ + symbol -> escapes = 0; \ + symbol -> total = 0; \ + symbol -> parent = 0; \ +} + +#define ENET_CONTEXT_CREATE(context, escapes_, minimum) \ +{ \ + ENET_SYMBOL_CREATE (context, 0, 0); \ + (context) -> escapes = escapes_; \ + (context) -> total = escapes_ + 256*minimum; \ + (context) -> symbols = 0; \ +} + +static enet_uint16 +enet_symbol_rescale (ENetSymbol * symbol) +{ + enet_uint16 total = 0; + for (;;) + { + symbol -> count -= symbol->count >> 1; + symbol -> under = symbol -> count; + if (symbol -> left) + symbol -> under += enet_symbol_rescale (symbol + symbol -> left); + total += symbol -> under; + if (! symbol -> right) break; + symbol += symbol -> right; + } + return total; +} + +#define ENET_CONTEXT_RESCALE(context, minimum) \ +{ \ + (context) -> total = (context) -> symbols ? enet_symbol_rescale ((context) + (context) -> symbols) : 0; \ + (context) -> escapes -= (context) -> escapes >> 1; \ + (context) -> total += (context) -> escapes + 256*minimum; \ +} + +#define ENET_RANGE_CODER_OUTPUT(value) \ +{ \ + if (outData >= outEnd) \ + return 0; \ + * outData ++ = value; \ +} + +#define ENET_RANGE_CODER_ENCODE(under, count, total) \ +{ \ + encodeRange /= (total); \ + encodeLow += (under) * encodeRange; \ + encodeRange *= (count); \ + for (;;) \ + { \ + if((encodeLow ^ (encodeLow + encodeRange)) >= ENET_RANGE_CODER_TOP) \ + { \ + if(encodeRange >= ENET_RANGE_CODER_BOTTOM) break; \ + encodeRange = -encodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \ + } \ + ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \ + encodeRange <<= 8; \ + encodeLow <<= 8; \ + } \ +} + +#define ENET_RANGE_CODER_FLUSH \ +{ \ + while (encodeLow) \ + { \ + ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \ + encodeLow <<= 8; \ + } \ +} + +#define ENET_RANGE_CODER_FREE_SYMBOLS \ +{ \ + if (nextSymbol >= sizeof (rangeCoder -> symbols) / sizeof (ENetSymbol) - ENET_SUBCONTEXT_ORDER ) \ + { \ + nextSymbol = 0; \ + ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); \ + predicted = 0; \ + order = 0; \ + } \ +} + +#define ENET_CONTEXT_ENCODE(context, symbol_, value_, under_, count_, update, minimum) \ +{ \ + under_ = value*minimum; \ + count_ = minimum; \ + if (! (context) -> symbols) \ + { \ + ENET_SYMBOL_CREATE (symbol_, value_, update); \ + (context) -> symbols = symbol_ - (context); \ + } \ + else \ + { \ + ENetSymbol * node = (context) + (context) -> symbols; \ + for (;;) \ + { \ + if (value_ < node -> value) \ + { \ + node -> under += update; \ + if (node -> left) { node += node -> left; continue; } \ + ENET_SYMBOL_CREATE (symbol_, value_, update); \ + node -> left = symbol_ - node; \ + } \ + else \ + if (value_ > node -> value) \ + { \ + under_ += node -> under; \ + if (node -> right) { node += node -> right; continue; } \ + ENET_SYMBOL_CREATE (symbol_, value_, update); \ + node -> right = symbol_ - node; \ + } \ + else \ + { \ + count_ += node -> count; \ + under_ += node -> under - node -> count; \ + node -> under += update; \ + node -> count += update; \ + symbol_ = node; \ + } \ + break; \ + } \ + } \ +} + +#ifdef ENET_CONTEXT_EXCLUSION +static const ENetSymbol emptyContext = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +#define ENET_CONTEXT_WALK(context, body) \ +{ \ + const ENetSymbol * node = (context) + (context) -> symbols; \ + const ENetSymbol * stack [256]; \ + size_t stackSize = 0; \ + while (node -> left) \ + { \ + stack [stackSize ++] = node; \ + node += node -> left; \ + } \ + for (;;) \ + { \ + body; \ + if (node -> right) \ + { \ + node += node -> right; \ + while (node -> left) \ + { \ + stack [stackSize ++] = node; \ + node += node -> left; \ + } \ + } \ + else \ + if (stackSize <= 0) \ + break; \ + else \ + node = stack [-- stackSize]; \ + } \ +} + +#define ENET_CONTEXT_ENCODE_EXCLUDE(context, value_, under, total, minimum) \ +ENET_CONTEXT_WALK(context, { \ + if (node -> value != value_) \ + { \ + enet_uint16 parentCount = rangeCoder -> symbols [node -> parent].count + minimum; \ + if (node -> value < value_) \ + under -= parentCount; \ + total -= parentCount; \ + } \ +}) +#endif + +size_t +enet_range_coder_compress (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit) +{ + ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context; + enet_uint8 * outStart = outData, * outEnd = & outData [outLimit]; + const enet_uint8 * inData, * inEnd; + enet_uint32 encodeLow = 0, encodeRange = ~0; + ENetSymbol * root; + enet_uint16 predicted = 0; + size_t order = 0, nextSymbol = 0; + + if (rangeCoder == NULL || inBufferCount <= 0 || inLimit <= 0) + return 0; + + inData = (const enet_uint8 *) inBuffers -> data; + inEnd = & inData [inBuffers -> dataLength]; + inBuffers ++; + inBufferCount --; + + ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); + + for (;;) + { + ENetSymbol * subcontext, * symbol; +#ifdef ENET_CONTEXT_EXCLUSION + const ENetSymbol * childContext = & emptyContext; +#endif + enet_uint8 value; + enet_uint16 count, under, * parent = & predicted, total; + if (inData >= inEnd) + { + if (inBufferCount <= 0) + break; + inData = (const enet_uint8 *) inBuffers -> data; + inEnd = & inData [inBuffers -> dataLength]; + inBuffers ++; + inBufferCount --; + } + value = * inData ++; + + for (subcontext = & rangeCoder -> symbols [predicted]; + subcontext != root; +#ifdef ENET_CONTEXT_EXCLUSION + childContext = subcontext, +#endif + subcontext = & rangeCoder -> symbols [subcontext -> parent]) + { + ENET_CONTEXT_ENCODE (subcontext, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0); + * parent = symbol - rangeCoder -> symbols; + parent = & symbol -> parent; + total = subcontext -> total; +#ifdef ENET_CONTEXT_EXCLUSION + if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA) + ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, 0); +#endif + if (count > 0) + { + ENET_RANGE_CODER_ENCODE (subcontext -> escapes + under, count, total); + } + else + { + if (subcontext -> escapes > 0 && subcontext -> escapes < total) + ENET_RANGE_CODER_ENCODE (0, subcontext -> escapes, total); + subcontext -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA; + subcontext -> total += ENET_SUBCONTEXT_ESCAPE_DELTA; + } + subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA; + if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100) + ENET_CONTEXT_RESCALE (subcontext, 0); + if (count > 0) goto nextInput; + } + + ENET_CONTEXT_ENCODE (root, symbol, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM); + * parent = symbol - rangeCoder -> symbols; + parent = & symbol -> parent; + total = root -> total; +#ifdef ENET_CONTEXT_EXCLUSION + if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA) + ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, ENET_CONTEXT_SYMBOL_MINIMUM); +#endif + ENET_RANGE_CODER_ENCODE (root -> escapes + under, count, total); + root -> total += ENET_CONTEXT_SYMBOL_DELTA; + if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100) + ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM); + + nextInput: + if (order >= ENET_SUBCONTEXT_ORDER) + predicted = rangeCoder -> symbols [predicted].parent; + else + order ++; + ENET_RANGE_CODER_FREE_SYMBOLS; + } + + ENET_RANGE_CODER_FLUSH; + + return (size_t) (outData - outStart); +} + +#define ENET_RANGE_CODER_SEED \ +{ \ + if (inData < inEnd) decodeCode |= * inData ++ << 24; \ + if (inData < inEnd) decodeCode |= * inData ++ << 16; \ + if (inData < inEnd) decodeCode |= * inData ++ << 8; \ + if (inData < inEnd) decodeCode |= * inData ++; \ +} + +#define ENET_RANGE_CODER_READ(total) ((decodeCode - decodeLow) / (decodeRange /= (total))) + +#define ENET_RANGE_CODER_DECODE(under, count, total) \ +{ \ + decodeLow += (under) * decodeRange; \ + decodeRange *= (count); \ + for (;;) \ + { \ + if((decodeLow ^ (decodeLow + decodeRange)) >= ENET_RANGE_CODER_TOP) \ + { \ + if(decodeRange >= ENET_RANGE_CODER_BOTTOM) break; \ + decodeRange = -decodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \ + } \ + decodeCode <<= 8; \ + if (inData < inEnd) \ + decodeCode |= * inData ++; \ + decodeRange <<= 8; \ + decodeLow <<= 8; \ + } \ +} + +#define ENET_CONTEXT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, createRoot, visitNode, createRight, createLeft) \ +{ \ + under_ = 0; \ + count_ = minimum; \ + if (! (context) -> symbols) \ + { \ + createRoot; \ + } \ + else \ + { \ + ENetSymbol * node = (context) + (context) -> symbols; \ + for (;;) \ + { \ + enet_uint16 after = under_ + node -> under + (node -> value + 1)*minimum, before = node -> count + minimum; \ + visitNode; \ + if (code >= after) \ + { \ + under_ += node -> under; \ + if (node -> right) { node += node -> right; continue; } \ + createRight; \ + } \ + else \ + if (code < after - before) \ + { \ + node -> under += update; \ + if (node -> left) { node += node -> left; continue; } \ + createLeft; \ + } \ + else \ + { \ + value_ = node -> value; \ + count_ += node -> count; \ + under_ = after - before; \ + node -> under += update; \ + node -> count += update; \ + symbol_ = node; \ + } \ + break; \ + } \ + } \ +} + +#define ENET_CONTEXT_TRY_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \ +ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, return 0, exclude (node -> value, after, before), return 0, return 0) + +#define ENET_CONTEXT_ROOT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \ +ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, \ + { \ + value_ = code / minimum; \ + under_ = code - code%minimum; \ + ENET_SYMBOL_CREATE (symbol_, value_, update); \ + (context) -> symbols = symbol_ - (context); \ + }, \ + exclude (node -> value, after, before), \ + { \ + value_ = node->value + 1 + (code - after)/minimum; \ + under_ = code - (code - after)%minimum; \ + ENET_SYMBOL_CREATE (symbol_, value_, update); \ + node -> right = symbol_ - node; \ + }, \ + { \ + value_ = node->value - 1 - (after - before - code - 1)/minimum; \ + under_ = code - (after - before - code - 1)%minimum; \ + ENET_SYMBOL_CREATE (symbol_, value_, update); \ + node -> left = symbol_ - node; \ + }) \ + +#ifdef ENET_CONTEXT_EXCLUSION +typedef struct _ENetExclude +{ + enet_uint8 value; + enet_uint16 under; +} ENetExclude; + +#define ENET_CONTEXT_DECODE_EXCLUDE(context, total, minimum) \ +{ \ + enet_uint16 under = 0; \ + nextExclude = excludes; \ + ENET_CONTEXT_WALK (context, { \ + under += rangeCoder -> symbols [node -> parent].count + minimum; \ + nextExclude -> value = node -> value; \ + nextExclude -> under = under; \ + nextExclude ++; \ + }); \ + total -= under; \ +} + +#define ENET_CONTEXT_EXCLUDED(value_, after, before) \ +{ \ + size_t low = 0, high = nextExclude - excludes; \ + for(;;) \ + { \ + size_t mid = (low + high) >> 1; \ + const ENetExclude * exclude = & excludes [mid]; \ + if (value_ < exclude -> value) \ + { \ + if (low + 1 < high) \ + { \ + high = mid; \ + continue; \ + } \ + if (exclude > excludes) \ + after -= exclude [-1].under; \ + } \ + else \ + { \ + if (value_ > exclude -> value) \ + { \ + if (low + 1 < high) \ + { \ + low = mid; \ + continue; \ + } \ + } \ + else \ + before = 0; \ + after -= exclude -> under; \ + } \ + break; \ + } \ +} +#endif + +#define ENET_CONTEXT_NOT_EXCLUDED(value_, after, before) + +size_t +enet_range_coder_decompress (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit) +{ + ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context; + enet_uint8 * outStart = outData, * outEnd = & outData [outLimit]; + const enet_uint8 * inEnd = & inData [inLimit]; + enet_uint32 decodeLow = 0, decodeCode = 0, decodeRange = ~0; + ENetSymbol * root; + enet_uint16 predicted = 0; + size_t order = 0, nextSymbol = 0; +#ifdef ENET_CONTEXT_EXCLUSION + ENetExclude excludes [256]; + ENetExclude * nextExclude = excludes; +#endif + + if (rangeCoder == NULL || inLimit <= 0) + return 0; + + ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); + + ENET_RANGE_CODER_SEED; + + for (;;) + { + ENetSymbol * subcontext, * symbol, * patch; +#ifdef ENET_CONTEXT_EXCLUSION + const ENetSymbol * childContext = & emptyContext; +#endif + enet_uint8 value = 0; + enet_uint16 code, under, count, bottom, * parent = & predicted, total; + + for (subcontext = & rangeCoder -> symbols [predicted]; + subcontext != root; +#ifdef ENET_CONTEXT_EXCLUSION + childContext = subcontext, +#endif + subcontext = & rangeCoder -> symbols [subcontext -> parent]) + { + if (subcontext -> escapes <= 0) + continue; + total = subcontext -> total; +#ifdef ENET_CONTEXT_EXCLUSION + if (childContext -> total > 0) + ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, 0); +#endif + if (subcontext -> escapes >= total) + continue; + code = ENET_RANGE_CODER_READ (total); + if (code < subcontext -> escapes) + { + ENET_RANGE_CODER_DECODE (0, subcontext -> escapes, total); + continue; + } + code -= subcontext -> escapes; +#ifdef ENET_CONTEXT_EXCLUSION + if (childContext -> total > 0) + { + ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_EXCLUDED); + } + else +#endif + { + ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_NOT_EXCLUDED); + } + bottom = symbol - rangeCoder -> symbols; + ENET_RANGE_CODER_DECODE (subcontext -> escapes + under, count, total); + subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA; + if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100) + ENET_CONTEXT_RESCALE (subcontext, 0); + goto patchContexts; + } + + total = root -> total; +#ifdef ENET_CONTEXT_EXCLUSION + if (childContext -> total > 0) + ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, ENET_CONTEXT_SYMBOL_MINIMUM); +#endif + code = ENET_RANGE_CODER_READ (total); + if (code < root -> escapes) + { + ENET_RANGE_CODER_DECODE (0, root -> escapes, total); + break; + } + code -= root -> escapes; +#ifdef ENET_CONTEXT_EXCLUSION + if (childContext -> total > 0) + { + ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_EXCLUDED); + } + else +#endif + { + ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_NOT_EXCLUDED); + } + bottom = symbol - rangeCoder -> symbols; + ENET_RANGE_CODER_DECODE (root -> escapes + under, count, total); + root -> total += ENET_CONTEXT_SYMBOL_DELTA; + if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100) + ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM); + + patchContexts: + for (patch = & rangeCoder -> symbols [predicted]; + patch != subcontext; + patch = & rangeCoder -> symbols [patch -> parent]) + { + ENET_CONTEXT_ENCODE (patch, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0); + * parent = symbol - rangeCoder -> symbols; + parent = & symbol -> parent; + if (count <= 0) + { + patch -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA; + patch -> total += ENET_SUBCONTEXT_ESCAPE_DELTA; + } + patch -> total += ENET_SUBCONTEXT_SYMBOL_DELTA; + if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || patch -> total > ENET_RANGE_CODER_BOTTOM - 0x100) + ENET_CONTEXT_RESCALE (patch, 0); + } + * parent = bottom; + + ENET_RANGE_CODER_OUTPUT (value); + + if (order >= ENET_SUBCONTEXT_ORDER) + predicted = rangeCoder -> symbols [predicted].parent; + else + order ++; + ENET_RANGE_CODER_FREE_SYMBOLS; + } + + return (size_t) (outData - outStart); +} + +/** @defgroup host ENet host functions + @{ +*/ + +/** Sets the packet compressor the host should use to the default range coder. + @param host host to enable the range coder for + @returns 0 on success, < 0 on failure +*/ +int +enet_host_compress_with_range_coder (ENetHost * host) +{ + ENetCompressor compressor; + memset (& compressor, 0, sizeof (compressor)); + compressor.context = enet_range_coder_create(); + if (compressor.context == NULL) + return -1; + compressor.compress = enet_range_coder_compress; + compressor.decompress = enet_range_coder_decompress; + compressor.destroy = enet_range_coder_destroy; + enet_host_compress (host, & compressor); + return 0; +} + +/** @} */ + + diff --git a/examples/ThirdPartyLibs/enet/host.c b/examples/ThirdPartyLibs/enet/host.c new file mode 100644 index 000000000..d0ee595cf --- /dev/null +++ b/examples/ThirdPartyLibs/enet/host.c @@ -0,0 +1,491 @@ +/** + @file host.c + @brief ENet host management functions +*/ +#define ENET_BUILDING_LIB 1 +#include +#include +#include "enet/enet.h" + +/** @defgroup host ENet host functions + @{ +*/ + +/** Creates a host for communicating to peers. + + @param address the address at which other peers may connect to this host. If NULL, then no peers may connect to the host. + @param peerCount the maximum number of peers that should be allocated for the host. + @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT + @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. + @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. + + @returns the host on success and NULL on failure + + @remarks ENet will strategically drop packets on specific sides of a connection between hosts + to ensure the host's bandwidth is not overwhelmed. The bandwidth parameters also determine + the window size of a connection which limits the amount of reliable packets that may be in transit + at any given time. +*/ +ENetHost * +enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) +{ + ENetHost * host; + ENetPeer * currentPeer; + + if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID) + return NULL; + + host = (ENetHost *) enet_malloc (sizeof (ENetHost)); + if (host == NULL) + return NULL; + memset (host, 0, sizeof (ENetHost)); + + host -> peers = (ENetPeer *) enet_malloc (peerCount * sizeof (ENetPeer)); + if (host -> peers == NULL) + { + enet_free (host); + + return NULL; + } + memset (host -> peers, 0, peerCount * sizeof (ENetPeer)); + + host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM); + if (host -> socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind (host -> socket, address) < 0)) + { + if (host -> socket != ENET_SOCKET_NULL) + enet_socket_destroy (host -> socket); + + enet_free (host -> peers); + enet_free (host); + + return NULL; + } + + enet_socket_set_option (host -> socket, ENET_SOCKOPT_NONBLOCK, 1); + enet_socket_set_option (host -> socket, ENET_SOCKOPT_BROADCAST, 1); + enet_socket_set_option (host -> socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE); + enet_socket_set_option (host -> socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE); + + if (address != NULL) + host -> address = * address; + + if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) + channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; + else + if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) + channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; + + host -> randomSeed = (enet_uint32) (size_t) host; +#ifdef WIN32 + host -> randomSeed += (enet_uint32) timeGetTime(); +#else + host -> randomSeed += (enet_uint32) time(NULL); +#endif + host -> randomSeed = (host -> randomSeed << 16) | (host -> randomSeed >> 16); + host -> channelLimit = channelLimit; + host -> incomingBandwidth = incomingBandwidth; + host -> outgoingBandwidth = outgoingBandwidth; + host -> bandwidthThrottleEpoch = 0; + host -> recalculateBandwidthLimits = 0; + host -> mtu = ENET_HOST_DEFAULT_MTU; + host -> peerCount = peerCount; + host -> commandCount = 0; + host -> bufferCount = 0; + host -> checksum = NULL; + host -> receivedAddress.host = ENET_HOST_ANY; + host -> receivedAddress.port = 0; + host -> receivedData = NULL; + host -> receivedDataLength = 0; + + host -> totalSentData = 0; + host -> totalSentPackets = 0; + host -> totalReceivedData = 0; + host -> totalReceivedPackets = 0; + + host -> compressor.context = NULL; + host -> compressor.compress = NULL; + host -> compressor.decompress = NULL; + host -> compressor.destroy = NULL; + + host -> intercept = NULL; + + enet_list_clear (& host -> dispatchQueue); + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + currentPeer -> host = host; + currentPeer -> incomingPeerID = currentPeer - host -> peers; + currentPeer -> outgoingSessionID = currentPeer -> incomingSessionID = 0xFF; + currentPeer -> data = NULL; + + enet_list_clear (& currentPeer -> acknowledgements); + enet_list_clear (& currentPeer -> sentReliableCommands); + enet_list_clear (& currentPeer -> sentUnreliableCommands); + enet_list_clear (& currentPeer -> outgoingReliableCommands); + enet_list_clear (& currentPeer -> outgoingUnreliableCommands); + enet_list_clear (& currentPeer -> dispatchedCommands); + + enet_peer_reset (currentPeer); + } + + return host; +} + +/** Destroys the host and all resources associated with it. + @param host pointer to the host to destroy +*/ +void +enet_host_destroy (ENetHost * host) +{ + ENetPeer * currentPeer; + + if (host == NULL) + return; + + enet_socket_destroy (host -> socket); + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + enet_peer_reset (currentPeer); + } + + if (host -> compressor.context != NULL && host -> compressor.destroy) + (* host -> compressor.destroy) (host -> compressor.context); + + enet_free (host -> peers); + enet_free (host); +} + +/** Initiates a connection to a foreign host. + @param host host seeking the connection + @param address destination for the connection + @param channelCount number of channels to allocate + @param data user data supplied to the receiving host + @returns a peer representing the foreign host on success, NULL on failure + @remarks The peer returned will have not completed the connection until enet_host_service() + notifies of an ENET_EVENT_TYPE_CONNECT event for the peer. +*/ +ENetPeer * +enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount, enet_uint32 data) +{ + ENetPeer * currentPeer; + ENetChannel * channel; + ENetProtocol command; + + if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) + channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; + else + if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) + channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED) + break; + } + + if (currentPeer >= & host -> peers [host -> peerCount]) + return NULL; + + currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel)); + if (currentPeer -> channels == NULL) + return NULL; + currentPeer -> channelCount = channelCount; + currentPeer -> state = ENET_PEER_STATE_CONNECTING; + currentPeer -> address = * address; + currentPeer -> connectID = ++ host -> randomSeed; + + if (host -> outgoingBandwidth == 0) + currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + currentPeer -> windowSize = (host -> outgoingBandwidth / + ENET_PEER_WINDOW_SIZE_SCALE) * + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + for (channel = currentPeer -> channels; + channel < & currentPeer -> channels [channelCount]; + ++ channel) + { + channel -> outgoingReliableSequenceNumber = 0; + channel -> outgoingUnreliableSequenceNumber = 0; + channel -> incomingReliableSequenceNumber = 0; + channel -> incomingUnreliableSequenceNumber = 0; + + enet_list_clear (& channel -> incomingReliableCommands); + enet_list_clear (& channel -> incomingUnreliableCommands); + + channel -> usedReliableWindows = 0; + memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows)); + } + + command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + command.header.channelID = 0xFF; + command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID); + command.connect.incomingSessionID = currentPeer -> incomingSessionID; + command.connect.outgoingSessionID = currentPeer -> outgoingSessionID; + command.connect.mtu = ENET_HOST_TO_NET_32 (currentPeer -> mtu); + command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize); + command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount); + command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth); + command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); + command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval); + command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration); + command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration); + command.connect.connectID = currentPeer -> connectID; + command.connect.data = ENET_HOST_TO_NET_32 (data); + + enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0); + + return currentPeer; +} + +/** Queues a packet to be sent to all peers associated with the host. + @param host host on which to broadcast the packet + @param channelID channel on which to broadcast + @param packet packet to broadcast +*/ +void +enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet) +{ + ENetPeer * currentPeer; + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state != ENET_PEER_STATE_CONNECTED) + continue; + + enet_peer_send (currentPeer, channelID, packet); + } + + if (packet -> referenceCount == 0) + enet_packet_destroy (packet); +} + +/** Sets the packet compressor the host should use to compress and decompress packets. + @param host host to enable or disable compression for + @param compressor callbacks for for the packet compressor; if NULL, then compression is disabled +*/ +void +enet_host_compress (ENetHost * host, const ENetCompressor * compressor) +{ + if (host -> compressor.context != NULL && host -> compressor.destroy) + (* host -> compressor.destroy) (host -> compressor.context); + + if (compressor) + host -> compressor = * compressor; + else + host -> compressor.context = NULL; +} + +/** Limits the maximum allowed channels of future incoming connections. + @param host host to limit + @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT +*/ +void +enet_host_channel_limit (ENetHost * host, size_t channelLimit) +{ + if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) + channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; + else + if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) + channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; + + host -> channelLimit = channelLimit; +} + + +/** Adjusts the bandwidth limits of a host. + @param host host to adjust + @param incomingBandwidth new incoming bandwidth + @param outgoingBandwidth new outgoing bandwidth + @remarks the incoming and outgoing bandwidth parameters are identical in function to those + specified in enet_host_create(). +*/ +void +enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) +{ + host -> incomingBandwidth = incomingBandwidth; + host -> outgoingBandwidth = outgoingBandwidth; + host -> recalculateBandwidthLimits = 1; +} + +void +enet_host_bandwidth_throttle (ENetHost * host) +{ + enet_uint32 timeCurrent = enet_time_get (), + elapsedTime = timeCurrent - host -> bandwidthThrottleEpoch, + peersTotal = 0, + dataTotal = 0, + peersRemaining, + bandwidth, + throttle = 0, + bandwidthLimit = 0; + int needsAdjustment; + ENetPeer * peer; + ENetProtocol command; + + if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL) + return; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + continue; + + ++ peersTotal; + dataTotal += peer -> outgoingDataTotal; + } + + if (peersTotal == 0) + return; + + peersRemaining = peersTotal; + needsAdjustment = 1; + + if (host -> outgoingBandwidth == 0) + bandwidth = ~0; + else + bandwidth = (host -> outgoingBandwidth * elapsedTime) / 1000; + + while (peersRemaining > 0 && needsAdjustment != 0) + { + needsAdjustment = 0; + + if (dataTotal < bandwidth) + throttle = ENET_PEER_PACKET_THROTTLE_SCALE; + else + throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + enet_uint32 peerBandwidth; + + if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) || + peer -> incomingBandwidth == 0 || + peer -> outgoingBandwidthThrottleEpoch == timeCurrent) + continue; + + peerBandwidth = (peer -> incomingBandwidth * elapsedTime) / 1000; + if ((throttle * peer -> outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth) + continue; + + peer -> packetThrottleLimit = (peerBandwidth * + ENET_PEER_PACKET_THROTTLE_SCALE) / peer -> outgoingDataTotal; + + if (peer -> packetThrottleLimit == 0) + peer -> packetThrottleLimit = 1; + + if (peer -> packetThrottle > peer -> packetThrottleLimit) + peer -> packetThrottle = peer -> packetThrottleLimit; + + peer -> outgoingBandwidthThrottleEpoch = timeCurrent; + + + needsAdjustment = 1; + -- peersRemaining; + bandwidth -= peerBandwidth; + dataTotal -= peerBandwidth; + } + } + + if (peersRemaining > 0) + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) || + peer -> outgoingBandwidthThrottleEpoch == timeCurrent) + continue; + + peer -> packetThrottleLimit = throttle; + + if (peer -> packetThrottle > peer -> packetThrottleLimit) + peer -> packetThrottle = peer -> packetThrottleLimit; + } + + if (host -> recalculateBandwidthLimits) + { + host -> recalculateBandwidthLimits = 0; + + peersRemaining = peersTotal; + bandwidth = host -> incomingBandwidth; + needsAdjustment = 1; + + if (bandwidth == 0) + bandwidthLimit = 0; + else + while (peersRemaining > 0 && needsAdjustment != 0) + { + needsAdjustment = 0; + bandwidthLimit = bandwidth / peersRemaining; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) || + peer -> incomingBandwidthThrottleEpoch == timeCurrent) + continue; + + if (peer -> outgoingBandwidth > 0 && + peer -> outgoingBandwidth >= bandwidthLimit) + continue; + + peer -> incomingBandwidthThrottleEpoch = timeCurrent; + + needsAdjustment = 1; + -- peersRemaining; + bandwidth -= peer -> outgoingBandwidth; + } + } + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + continue; + + command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + command.header.channelID = 0xFF; + command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); + + if (peer -> incomingBandwidthThrottleEpoch == timeCurrent) + command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth); + else + command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit); + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); + } + } + + host -> bandwidthThrottleEpoch = timeCurrent; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + peer -> incomingDataTotal = 0; + peer -> outgoingDataTotal = 0; + } +} + +/** @} */ diff --git a/examples/ThirdPartyLibs/enet/include/enet/callbacks.h b/examples/ThirdPartyLibs/enet/include/enet/callbacks.h new file mode 100644 index 000000000..340a4a989 --- /dev/null +++ b/examples/ThirdPartyLibs/enet/include/enet/callbacks.h @@ -0,0 +1,27 @@ +/** + @file callbacks.h + @brief ENet callbacks +*/ +#ifndef __ENET_CALLBACKS_H__ +#define __ENET_CALLBACKS_H__ + +#include + +typedef struct _ENetCallbacks +{ + void * (ENET_CALLBACK * malloc) (size_t size); + void (ENET_CALLBACK * free) (void * memory); + void (ENET_CALLBACK * no_memory) (void); +} ENetCallbacks; + +/** @defgroup callbacks ENet internal callbacks + @{ + @ingroup private +*/ +extern void * enet_malloc (size_t); +extern void enet_free (void *); + +/** @} */ + +#endif /* __ENET_CALLBACKS_H__ */ + diff --git a/examples/ThirdPartyLibs/enet/include/enet/enet.h b/examples/ThirdPartyLibs/enet/include/enet/enet.h new file mode 100644 index 000000000..5f9d5403c --- /dev/null +++ b/examples/ThirdPartyLibs/enet/include/enet/enet.h @@ -0,0 +1,571 @@ +/** + @file enet.h + @brief ENet public header file +*/ +#ifndef __ENET_ENET_H__ +#define __ENET_ENET_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#ifdef WIN32 +#include "enet/win32.h" +#else +#include "enet/unix.h" +#endif + +#include "enet/types.h" +#include "enet/protocol.h" +#include "enet/list.h" +#include "enet/callbacks.h" + +#define ENET_VERSION_MAJOR 1 +#define ENET_VERSION_MINOR 3 +#define ENET_VERSION_PATCH 7 +#define ENET_VERSION_CREATE(major, minor, patch) (((major)<<16) | ((minor)<<8) | (patch)) +#define ENET_VERSION ENET_VERSION_CREATE(ENET_VERSION_MAJOR, ENET_VERSION_MINOR, ENET_VERSION_PATCH) + +typedef enet_uint32 ENetVersion; + +struct _ENetHost; +struct _ENetEvent; +struct _ENetPacket; + +typedef enum _ENetSocketType +{ + ENET_SOCKET_TYPE_STREAM = 1, + ENET_SOCKET_TYPE_DATAGRAM = 2 +} ENetSocketType; + +typedef enum _ENetSocketWait +{ + ENET_SOCKET_WAIT_NONE = 0, + ENET_SOCKET_WAIT_SEND = (1 << 0), + ENET_SOCKET_WAIT_RECEIVE = (1 << 1) +} ENetSocketWait; + +typedef enum _ENetSocketOption +{ + ENET_SOCKOPT_NONBLOCK = 1, + ENET_SOCKOPT_BROADCAST = 2, + ENET_SOCKOPT_RCVBUF = 3, + ENET_SOCKOPT_SNDBUF = 4, + ENET_SOCKOPT_REUSEADDR = 5, + ENET_SOCKOPT_RCVTIMEO = 6, + ENET_SOCKOPT_SNDTIMEO = 7 +} ENetSocketOption; + +typedef enum _ENetSocketShutdown +{ + ENET_SOCKET_SHUTDOWN_READ = 0, + ENET_SOCKET_SHUTDOWN_WRITE = 1, + ENET_SOCKET_SHUTDOWN_READ_WRITE = 2 +} ENetSocketShutdown; + +enum +{ + ENET_HOST_ANY = 0, /**< specifies the default server host */ + ENET_HOST_BROADCAST = 0xFFFFFFFF, /**< specifies a subnet-wide broadcast */ + + ENET_PORT_ANY = 0 /**< specifies that a port should be automatically chosen */ +}; + +/** + * Portable internet address structure. + * + * The host must be specified in network byte-order, and the port must be in host + * byte-order. The constant ENET_HOST_ANY may be used to specify the default + * server host. The constant ENET_HOST_BROADCAST may be used to specify the + * broadcast address (255.255.255.255). This makes sense for enet_host_connect, + * but not for enet_host_create. Once a server responds to a broadcast, the + * address is updated from ENET_HOST_BROADCAST to the server's actual IP address. + */ +typedef struct _ENetAddress +{ + enet_uint32 host; + enet_uint16 port; +} ENetAddress; + +/** + * Packet flag bit constants. + * + * The host must be specified in network byte-order, and the port must be in + * host byte-order. The constant ENET_HOST_ANY may be used to specify the + * default server host. + + @sa ENetPacket +*/ +typedef enum _ENetPacketFlag +{ + /** packet must be received by the target peer and resend attempts should be + * made until the packet is delivered */ + ENET_PACKET_FLAG_RELIABLE = (1 << 0), + /** packet will not be sequenced with other packets + * not supported for reliable packets + */ + ENET_PACKET_FLAG_UNSEQUENCED = (1 << 1), + /** packet will not allocate data, and user must supply it instead */ + ENET_PACKET_FLAG_NO_ALLOCATE = (1 << 2), + /** packet will be fragmented using unreliable (instead of reliable) sends + * if it exceeds the MTU */ + ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT = (1 << 3), + + /** whether the packet has been sent from all queues it has been entered into */ + ENET_PACKET_FLAG_SENT = (1<<8) +} ENetPacketFlag; + +typedef void (ENET_CALLBACK * ENetPacketFreeCallback) (struct _ENetPacket *); + +/** + * ENet packet structure. + * + * An ENet data packet that may be sent to or received from a peer. The shown + * fields should only be read and never modified. The data field contains the + * allocated data for the packet. The dataLength fields specifies the length + * of the allocated data. The flags field is either 0 (specifying no flags), + * or a bitwise-or of any combination of the following flags: + * + * ENET_PACKET_FLAG_RELIABLE - packet must be received by the target peer + * and resend attempts should be made until the packet is delivered + * + * ENET_PACKET_FLAG_UNSEQUENCED - packet will not be sequenced with other packets + * (not supported for reliable packets) + * + * ENET_PACKET_FLAG_NO_ALLOCATE - packet will not allocate data, and user must supply it instead + + @sa ENetPacketFlag + */ +typedef struct _ENetPacket +{ + size_t referenceCount; /**< internal use only */ + enet_uint32 flags; /**< bitwise-or of ENetPacketFlag constants */ + enet_uint8 * data; /**< allocated data for packet */ + size_t dataLength; /**< length of data */ + ENetPacketFreeCallback freeCallback; /**< function to be called when the packet is no longer in use */ + void * userData; /**< application private data, may be freely modified */ +} ENetPacket; + +typedef struct _ENetAcknowledgement +{ + ENetListNode acknowledgementList; + enet_uint32 sentTime; + ENetProtocol command; +} ENetAcknowledgement; + +typedef struct _ENetOutgoingCommand +{ + ENetListNode outgoingCommandList; + enet_uint16 reliableSequenceNumber; + enet_uint16 unreliableSequenceNumber; + enet_uint32 sentTime; + enet_uint32 roundTripTimeout; + enet_uint32 roundTripTimeoutLimit; + enet_uint32 fragmentOffset; + enet_uint16 fragmentLength; + enet_uint16 sendAttempts; + ENetProtocol command; + ENetPacket * packet; +} ENetOutgoingCommand; + +typedef struct _ENetIncomingCommand +{ + ENetListNode incomingCommandList; + enet_uint16 reliableSequenceNumber; + enet_uint16 unreliableSequenceNumber; + ENetProtocol command; + enet_uint32 fragmentCount; + enet_uint32 fragmentsRemaining; + enet_uint32 * fragments; + ENetPacket * packet; +} ENetIncomingCommand; + +typedef enum _ENetPeerState +{ + ENET_PEER_STATE_DISCONNECTED = 0, + ENET_PEER_STATE_CONNECTING = 1, + ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2, + ENET_PEER_STATE_CONNECTION_PENDING = 3, + ENET_PEER_STATE_CONNECTION_SUCCEEDED = 4, + ENET_PEER_STATE_CONNECTED = 5, + ENET_PEER_STATE_DISCONNECT_LATER = 6, + ENET_PEER_STATE_DISCONNECTING = 7, + ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT = 8, + ENET_PEER_STATE_ZOMBIE = 9 +} ENetPeerState; + +#ifndef ENET_BUFFER_MAXIMUM +#define ENET_BUFFER_MAXIMUM (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS) +#endif + +enum +{ + ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024, + ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024, + ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000, + ENET_HOST_DEFAULT_MTU = 1400, + + ENET_PEER_DEFAULT_ROUND_TRIP_TIME = 500, + ENET_PEER_DEFAULT_PACKET_THROTTLE = 32, + ENET_PEER_PACKET_THROTTLE_SCALE = 32, + ENET_PEER_PACKET_THROTTLE_COUNTER = 7, + ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2, + ENET_PEER_PACKET_THROTTLE_DECELERATION = 2, + ENET_PEER_PACKET_THROTTLE_INTERVAL = 5000, + ENET_PEER_PACKET_LOSS_SCALE = (1 << 16), + ENET_PEER_PACKET_LOSS_INTERVAL = 10000, + ENET_PEER_WINDOW_SIZE_SCALE = 64 * 1024, + ENET_PEER_TIMEOUT_LIMIT = 32, + ENET_PEER_TIMEOUT_MINIMUM = 5000, + ENET_PEER_TIMEOUT_MAXIMUM = 30000, + ENET_PEER_PING_INTERVAL = 500, + ENET_PEER_UNSEQUENCED_WINDOWS = 64, + ENET_PEER_UNSEQUENCED_WINDOW_SIZE = 1024, + ENET_PEER_FREE_UNSEQUENCED_WINDOWS = 32, + ENET_PEER_RELIABLE_WINDOWS = 16, + ENET_PEER_RELIABLE_WINDOW_SIZE = 0x1000, + ENET_PEER_FREE_RELIABLE_WINDOWS = 8 +}; + +typedef struct _ENetChannel +{ + enet_uint16 outgoingReliableSequenceNumber; + enet_uint16 outgoingUnreliableSequenceNumber; + enet_uint16 usedReliableWindows; + enet_uint16 reliableWindows [ENET_PEER_RELIABLE_WINDOWS]; + enet_uint16 incomingReliableSequenceNumber; + enet_uint16 incomingUnreliableSequenceNumber; + ENetList incomingReliableCommands; + ENetList incomingUnreliableCommands; +} ENetChannel; + +/** + * An ENet peer which data packets may be sent or received from. + * + * No fields should be modified unless otherwise specified. + */ +typedef struct _ENetPeer +{ + ENetListNode dispatchList; + struct _ENetHost * host; + enet_uint16 outgoingPeerID; + enet_uint16 incomingPeerID; + enet_uint32 connectID; + enet_uint8 outgoingSessionID; + enet_uint8 incomingSessionID; + ENetAddress address; /**< Internet address of the peer */ + void * data; /**< Application private data, may be freely modified */ + ENetPeerState state; + ENetChannel * channels; + size_t channelCount; /**< Number of channels allocated for communication with peer */ + enet_uint32 incomingBandwidth; /**< Downstream bandwidth of the client in bytes/second */ + enet_uint32 outgoingBandwidth; /**< Upstream bandwidth of the client in bytes/second */ + enet_uint32 incomingBandwidthThrottleEpoch; + enet_uint32 outgoingBandwidthThrottleEpoch; + enet_uint32 incomingDataTotal; + enet_uint32 outgoingDataTotal; + enet_uint32 lastSendTime; + enet_uint32 lastReceiveTime; + enet_uint32 nextTimeout; + enet_uint32 earliestTimeout; + enet_uint32 packetLossEpoch; + enet_uint32 packetsSent; + enet_uint32 packetsLost; + enet_uint32 packetLoss; /**< mean packet loss of reliable packets as a ratio with respect to the constant ENET_PEER_PACKET_LOSS_SCALE */ + enet_uint32 packetLossVariance; + enet_uint32 packetThrottle; + enet_uint32 packetThrottleLimit; + enet_uint32 packetThrottleCounter; + enet_uint32 packetThrottleEpoch; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; + enet_uint32 packetThrottleInterval; + enet_uint32 pingInterval; + enet_uint32 timeoutLimit; + enet_uint32 timeoutMinimum; + enet_uint32 timeoutMaximum; + enet_uint32 lastRoundTripTime; + enet_uint32 lowestRoundTripTime; + enet_uint32 lastRoundTripTimeVariance; + enet_uint32 highestRoundTripTimeVariance; + enet_uint32 roundTripTime; /**< mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgement */ + enet_uint32 roundTripTimeVariance; + enet_uint32 mtu; + enet_uint32 windowSize; + enet_uint32 reliableDataInTransit; + enet_uint16 outgoingReliableSequenceNumber; + ENetList acknowledgements; + ENetList sentReliableCommands; + ENetList sentUnreliableCommands; + ENetList outgoingReliableCommands; + ENetList outgoingUnreliableCommands; + ENetList dispatchedCommands; + int needsDispatch; + enet_uint16 incomingUnsequencedGroup; + enet_uint16 outgoingUnsequencedGroup; + enet_uint32 unsequencedWindow [ENET_PEER_UNSEQUENCED_WINDOW_SIZE / 32]; + enet_uint32 eventData; +} ENetPeer; + +/** An ENet packet compressor for compressing UDP packets before socket sends or receives. + */ +typedef struct _ENetCompressor +{ + /** Context data for the compressor. Must be non-NULL. */ + void * context; + /** Compresses from inBuffers[0:inBufferCount-1], containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */ + size_t (ENET_CALLBACK * compress) (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit); + /** Decompresses from inData, containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */ + size_t (ENET_CALLBACK * decompress) (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit); + /** Destroys the context when compression is disabled or the host is destroyed. May be NULL. */ + void (ENET_CALLBACK * destroy) (void * context); +} ENetCompressor; + +/** Callback that computes the checksum of the data held in buffers[0:bufferCount-1] */ +typedef enet_uint32 (ENET_CALLBACK * ENetChecksumCallback) (const ENetBuffer * buffers, size_t bufferCount); + +/** Callback for intercepting received raw UDP packets. Should return 1 to intercept, 0 to ignore, or -1 to propagate an error. */ +typedef int (ENET_CALLBACK * ENetInterceptCallback) (struct _ENetHost * host, struct _ENetEvent * event); + +/** An ENet host for communicating with peers. + * + * No fields should be modified unless otherwise stated. + + @sa enet_host_create() + @sa enet_host_destroy() + @sa enet_host_connect() + @sa enet_host_service() + @sa enet_host_flush() + @sa enet_host_broadcast() + @sa enet_host_compress() + @sa enet_host_compress_with_range_coder() + @sa enet_host_channel_limit() + @sa enet_host_bandwidth_limit() + @sa enet_host_bandwidth_throttle() + */ +typedef struct _ENetHost +{ + ENetSocket socket; + ENetAddress address; /**< Internet address of the host */ + enet_uint32 incomingBandwidth; /**< downstream bandwidth of the host */ + enet_uint32 outgoingBandwidth; /**< upstream bandwidth of the host */ + enet_uint32 bandwidthThrottleEpoch; + enet_uint32 mtu; + enet_uint32 randomSeed; + int recalculateBandwidthLimits; + ENetPeer * peers; /**< array of peers allocated for this host */ + size_t peerCount; /**< number of peers allocated for this host */ + size_t channelLimit; /**< maximum number of channels allowed for connected peers */ + enet_uint32 serviceTime; + ENetList dispatchQueue; + int continueSending; + size_t packetSize; + enet_uint16 headerFlags; + ENetProtocol commands [ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS]; + size_t commandCount; + ENetBuffer buffers [ENET_BUFFER_MAXIMUM]; + size_t bufferCount; + ENetChecksumCallback checksum; /**< callback the user can set to enable packet checksums for this host */ + ENetCompressor compressor; + enet_uint8 packetData [2][ENET_PROTOCOL_MAXIMUM_MTU]; + ENetAddress receivedAddress; + enet_uint8 * receivedData; + size_t receivedDataLength; + enet_uint32 totalSentData; /**< total data sent, user should reset to 0 as needed to prevent overflow */ + enet_uint32 totalSentPackets; /**< total UDP packets sent, user should reset to 0 as needed to prevent overflow */ + enet_uint32 totalReceivedData; /**< total data received, user should reset to 0 as needed to prevent overflow */ + enet_uint32 totalReceivedPackets; /**< total UDP packets received, user should reset to 0 as needed to prevent overflow */ + ENetInterceptCallback intercept; /**< callback the user can set to intercept received raw UDP packets */ +} ENetHost; + +/** + * An ENet event type, as specified in @ref ENetEvent. + */ +typedef enum _ENetEventType +{ + /** no event occurred within the specified time limit */ + ENET_EVENT_TYPE_NONE = 0, + + /** a connection request initiated by enet_host_connect has completed. + * The peer field contains the peer which successfully connected. + */ + ENET_EVENT_TYPE_CONNECT = 1, + + /** a peer has disconnected. This event is generated on a successful + * completion of a disconnect initiated by enet_pper_disconnect, if + * a peer has timed out, or if a connection request intialized by + * enet_host_connect has timed out. The peer field contains the peer + * which disconnected. The data field contains user supplied data + * describing the disconnection, or 0, if none is available. + */ + ENET_EVENT_TYPE_DISCONNECT = 2, + + /** a packet has been received from a peer. The peer field specifies the + * peer which sent the packet. The channelID field specifies the channel + * number upon which the packet was received. The packet field contains + * the packet that was received; this packet must be destroyed with + * enet_packet_destroy after use. + */ + ENET_EVENT_TYPE_RECEIVE = 3 +} ENetEventType; + +/** + * An ENet event as returned by enet_host_service(). + + @sa enet_host_service + */ +typedef struct _ENetEvent +{ + ENetEventType type; /**< type of the event */ + ENetPeer * peer; /**< peer that generated a connect, disconnect or receive event */ + enet_uint8 channelID; /**< channel on the peer that generated the event, if appropriate */ + enet_uint32 data; /**< data associated with the event, if appropriate */ + ENetPacket * packet; /**< packet associated with the event, if appropriate */ +} ENetEvent; + +/** @defgroup global ENet global functions + @{ +*/ + +/** + Initializes ENet globally. Must be called prior to using any functions in + ENet. + @returns 0 on success, < 0 on failure +*/ +ENET_API int enet_initialize (void); + +/** + Initializes ENet globally and supplies user-overridden callbacks. Must be called prior to using any functions in ENet. Do not use enet_initialize() if you use this variant. Make sure the ENetCallbacks structure is zeroed out so that any additional callbacks added in future versions will be properly ignored. + + @param version the constant ENET_VERSION should be supplied so ENet knows which version of ENetCallbacks struct to use + @param inits user-overriden callbacks where any NULL callbacks will use ENet's defaults + @returns 0 on success, < 0 on failure +*/ +ENET_API int enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits); + +/** + Shuts down ENet globally. Should be called when a program that has + initialized ENet exits. +*/ +ENET_API void enet_deinitialize (void); + +/** @} */ + +/** @defgroup private ENet private implementation functions */ + +/** + Returns the wall-time in milliseconds. Its initial value is unspecified + unless otherwise set. + */ +ENET_API enet_uint32 enet_time_get (void); +/** + Sets the current wall-time in milliseconds. + */ +ENET_API void enet_time_set (enet_uint32); + +/** @defgroup socket ENet socket functions + @{ +*/ +ENET_API ENetSocket enet_socket_create (ENetSocketType); +ENET_API int enet_socket_bind (ENetSocket, const ENetAddress *); +ENET_API int enet_socket_listen (ENetSocket, int); +ENET_API ENetSocket enet_socket_accept (ENetSocket, ENetAddress *); +ENET_API int enet_socket_connect (ENetSocket, const ENetAddress *); +ENET_API int enet_socket_send (ENetSocket, const ENetAddress *, const ENetBuffer *, size_t); +ENET_API int enet_socket_receive (ENetSocket, ENetAddress *, ENetBuffer *, size_t); +ENET_API int enet_socket_wait (ENetSocket, enet_uint32 *, enet_uint32); +ENET_API int enet_socket_set_option (ENetSocket, ENetSocketOption, int); +ENET_API int enet_socket_shutdown (ENetSocket, ENetSocketShutdown); +ENET_API void enet_socket_destroy (ENetSocket); +ENET_API int enet_socketset_select (ENetSocket, ENetSocketSet *, ENetSocketSet *, enet_uint32); + +/** @} */ + +/** @defgroup Address ENet address functions + @{ +*/ +/** Attempts to resolve the host named by the parameter hostName and sets + the host field in the address parameter if successful. + @param address destination to store resolved address + @param hostName host name to lookup + @retval 0 on success + @retval < 0 on failure + @returns the address of the given hostName in address on success +*/ +ENET_API int enet_address_set_host (ENetAddress * address, const char * hostName); + +/** Gives the printable form of the ip address specified in the address parameter. + @param address address printed + @param hostName destination for name, must not be NULL + @param nameLength maximum length of hostName. + @returns the null-terminated name of the host in hostName on success + @retval 0 on success + @retval < 0 on failure +*/ +ENET_API int enet_address_get_host_ip (const ENetAddress * address, char * hostName, size_t nameLength); + +/** Attempts to do a reverse lookup of the host field in the address parameter. + @param address address used for reverse lookup + @param hostName destination for name, must not be NULL + @param nameLength maximum length of hostName. + @returns the null-terminated name of the host in hostName on success + @retval 0 on success + @retval < 0 on failure +*/ +ENET_API int enet_address_get_host (const ENetAddress * address, char * hostName, size_t nameLength); + +/** @} */ + +ENET_API ENetPacket * enet_packet_create (const void *, size_t, enet_uint32); +ENET_API void enet_packet_destroy (ENetPacket *); +ENET_API int enet_packet_resize (ENetPacket *, size_t); +ENET_API enet_uint32 enet_crc32 (const ENetBuffer *, size_t); + +ENET_API ENetHost * enet_host_create (const ENetAddress *, size_t, size_t, enet_uint32, enet_uint32); +ENET_API void enet_host_destroy (ENetHost *); +ENET_API ENetPeer * enet_host_connect (ENetHost *, const ENetAddress *, size_t, enet_uint32); +ENET_API int enet_host_check_events (ENetHost *, ENetEvent *); +ENET_API int enet_host_service (ENetHost *, ENetEvent *, enet_uint32); +ENET_API void enet_host_flush (ENetHost *); +ENET_API void enet_host_broadcast (ENetHost *, enet_uint8, ENetPacket *); +ENET_API void enet_host_compress (ENetHost *, const ENetCompressor *); +ENET_API int enet_host_compress_with_range_coder (ENetHost * host); +ENET_API void enet_host_channel_limit (ENetHost *, size_t); +ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32); +extern void enet_host_bandwidth_throttle (ENetHost *); + +ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *); +ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID); +ENET_API void enet_peer_ping (ENetPeer *); +ENET_API void enet_peer_ping_interval (ENetPeer *, enet_uint32); +ENET_API void enet_peer_timeout (ENetPeer *, enet_uint32, enet_uint32, enet_uint32); +ENET_API void enet_peer_reset (ENetPeer *); +ENET_API void enet_peer_disconnect (ENetPeer *, enet_uint32); +ENET_API void enet_peer_disconnect_now (ENetPeer *, enet_uint32); +ENET_API void enet_peer_disconnect_later (ENetPeer *, enet_uint32); +ENET_API void enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32); +extern int enet_peer_throttle (ENetPeer *, enet_uint32); +extern void enet_peer_reset_queues (ENetPeer *); +extern void enet_peer_setup_outgoing_command (ENetPeer *, ENetOutgoingCommand *); +extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16); +extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32); +extern ENetAcknowledgement * enet_peer_queue_acknowledgement (ENetPeer *, const ENetProtocol *, enet_uint16); +extern void enet_peer_dispatch_incoming_unreliable_commands (ENetPeer *, ENetChannel *); +extern void enet_peer_dispatch_incoming_reliable_commands (ENetPeer *, ENetChannel *); + +ENET_API void * enet_range_coder_create (void); +ENET_API void enet_range_coder_destroy (void *); +ENET_API size_t enet_range_coder_compress (void *, const ENetBuffer *, size_t, size_t, enet_uint8 *, size_t); +ENET_API size_t enet_range_coder_decompress (void *, const enet_uint8 *, size_t, enet_uint8 *, size_t); + +extern size_t enet_protocol_command_size (enet_uint8); + +#ifdef __cplusplus +} +#endif + +#endif /* __ENET_ENET_H__ */ + diff --git a/examples/ThirdPartyLibs/enet/include/enet/list.h b/examples/ThirdPartyLibs/enet/include/enet/list.h new file mode 100644 index 000000000..d7b260084 --- /dev/null +++ b/examples/ThirdPartyLibs/enet/include/enet/list.h @@ -0,0 +1,43 @@ +/** + @file list.h + @brief ENet list management +*/ +#ifndef __ENET_LIST_H__ +#define __ENET_LIST_H__ + +#include + +typedef struct _ENetListNode +{ + struct _ENetListNode * next; + struct _ENetListNode * previous; +} ENetListNode; + +typedef ENetListNode * ENetListIterator; + +typedef struct _ENetList +{ + ENetListNode sentinel; +} ENetList; + +extern void enet_list_clear (ENetList *); + +extern ENetListIterator enet_list_insert (ENetListIterator, void *); +extern void * enet_list_remove (ENetListIterator); +extern ENetListIterator enet_list_move (ENetListIterator, void *, void *); + +extern size_t enet_list_size (ENetList *); + +#define enet_list_begin(list) ((list) -> sentinel.next) +#define enet_list_end(list) (& (list) -> sentinel) + +#define enet_list_empty(list) (enet_list_begin (list) == enet_list_end (list)) + +#define enet_list_next(iterator) ((iterator) -> next) +#define enet_list_previous(iterator) ((iterator) -> previous) + +#define enet_list_front(list) ((void *) (list) -> sentinel.next) +#define enet_list_back(list) ((void *) (list) -> sentinel.previous) + +#endif /* __ENET_LIST_H__ */ + diff --git a/examples/ThirdPartyLibs/enet/include/enet/protocol.h b/examples/ThirdPartyLibs/enet/include/enet/protocol.h new file mode 100644 index 000000000..f8a27d8e1 --- /dev/null +++ b/examples/ThirdPartyLibs/enet/include/enet/protocol.h @@ -0,0 +1,199 @@ +/** + @file protocol.h + @brief ENet protocol +*/ +#ifndef __ENET_PROTOCOL_H__ +#define __ENET_PROTOCOL_H__ + +#include "enet/types.h" + +enum +{ + ENET_PROTOCOL_MINIMUM_MTU = 576, + ENET_PROTOCOL_MAXIMUM_MTU = 4096, + ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32, + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE = 4096, + ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE = 32768, + ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1, + ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255, + ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xFFF, + ENET_PROTOCOL_MAXIMUM_PACKET_SIZE = 1024 * 1024 * 1024, + ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT = 1024 * 1024 +}; + +typedef enum _ENetProtocolCommand +{ + ENET_PROTOCOL_COMMAND_NONE = 0, + ENET_PROTOCOL_COMMAND_ACKNOWLEDGE = 1, + ENET_PROTOCOL_COMMAND_CONNECT = 2, + ENET_PROTOCOL_COMMAND_VERIFY_CONNECT = 3, + ENET_PROTOCOL_COMMAND_DISCONNECT = 4, + ENET_PROTOCOL_COMMAND_PING = 5, + ENET_PROTOCOL_COMMAND_SEND_RELIABLE = 6, + ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE = 7, + ENET_PROTOCOL_COMMAND_SEND_FRAGMENT = 8, + ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 9, + ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 10, + ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11, + ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT = 12, + ENET_PROTOCOL_COMMAND_COUNT = 13, + + ENET_PROTOCOL_COMMAND_MASK = 0x0F +} ENetProtocolCommand; + +typedef enum _ENetProtocolFlag +{ + ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 << 7), + ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 << 6), + + ENET_PROTOCOL_HEADER_FLAG_COMPRESSED = (1 << 14), + ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 << 15), + ENET_PROTOCOL_HEADER_FLAG_MASK = ENET_PROTOCOL_HEADER_FLAG_COMPRESSED | ENET_PROTOCOL_HEADER_FLAG_SENT_TIME, + + ENET_PROTOCOL_HEADER_SESSION_MASK = (3 << 12), + ENET_PROTOCOL_HEADER_SESSION_SHIFT = 12 +} ENetProtocolFlag; + +#ifdef _MSC_VER_ +#pragma pack(push, 1) +#define ENET_PACKED +#elif defined(__GNUC__) +#define ENET_PACKED __attribute__ ((packed)) +#else +#define ENET_PACKED +#endif + +typedef struct _ENetProtocolHeader +{ + enet_uint16 peerID; + enet_uint16 sentTime; +} ENET_PACKED ENetProtocolHeader; + +typedef struct _ENetProtocolCommandHeader +{ + enet_uint8 command; + enet_uint8 channelID; + enet_uint16 reliableSequenceNumber; +} ENET_PACKED ENetProtocolCommandHeader; + +typedef struct _ENetProtocolAcknowledge +{ + ENetProtocolCommandHeader header; + enet_uint16 receivedReliableSequenceNumber; + enet_uint16 receivedSentTime; +} ENET_PACKED ENetProtocolAcknowledge; + +typedef struct _ENetProtocolConnect +{ + ENetProtocolCommandHeader header; + enet_uint16 outgoingPeerID; + enet_uint8 incomingSessionID; + enet_uint8 outgoingSessionID; + enet_uint32 mtu; + enet_uint32 windowSize; + enet_uint32 channelCount; + enet_uint32 incomingBandwidth; + enet_uint32 outgoingBandwidth; + enet_uint32 packetThrottleInterval; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; + enet_uint32 connectID; + enet_uint32 data; +} ENET_PACKED ENetProtocolConnect; + +typedef struct _ENetProtocolVerifyConnect +{ + ENetProtocolCommandHeader header; + enet_uint16 outgoingPeerID; + enet_uint8 incomingSessionID; + enet_uint8 outgoingSessionID; + enet_uint32 mtu; + enet_uint32 windowSize; + enet_uint32 channelCount; + enet_uint32 incomingBandwidth; + enet_uint32 outgoingBandwidth; + enet_uint32 packetThrottleInterval; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; + enet_uint32 connectID; +} ENET_PACKED ENetProtocolVerifyConnect; + +typedef struct _ENetProtocolBandwidthLimit +{ + ENetProtocolCommandHeader header; + enet_uint32 incomingBandwidth; + enet_uint32 outgoingBandwidth; +} ENET_PACKED ENetProtocolBandwidthLimit; + +typedef struct _ENetProtocolThrottleConfigure +{ + ENetProtocolCommandHeader header; + enet_uint32 packetThrottleInterval; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; +} ENET_PACKED ENetProtocolThrottleConfigure; + +typedef struct _ENetProtocolDisconnect +{ + ENetProtocolCommandHeader header; + enet_uint32 data; +} ENET_PACKED ENetProtocolDisconnect; + +typedef struct _ENetProtocolPing +{ + ENetProtocolCommandHeader header; +} ENET_PACKED ENetProtocolPing; + +typedef struct _ENetProtocolSendReliable +{ + ENetProtocolCommandHeader header; + enet_uint16 dataLength; +} ENET_PACKED ENetProtocolSendReliable; + +typedef struct _ENetProtocolSendUnreliable +{ + ENetProtocolCommandHeader header; + enet_uint16 unreliableSequenceNumber; + enet_uint16 dataLength; +} ENET_PACKED ENetProtocolSendUnreliable; + +typedef struct _ENetProtocolSendUnsequenced +{ + ENetProtocolCommandHeader header; + enet_uint16 unsequencedGroup; + enet_uint16 dataLength; +} ENET_PACKED ENetProtocolSendUnsequenced; + +typedef struct _ENetProtocolSendFragment +{ + ENetProtocolCommandHeader header; + enet_uint16 startSequenceNumber; + enet_uint16 dataLength; + enet_uint32 fragmentCount; + enet_uint32 fragmentNumber; + enet_uint32 totalLength; + enet_uint32 fragmentOffset; +} ENET_PACKED ENetProtocolSendFragment; + +typedef union _ENetProtocol +{ + ENetProtocolCommandHeader header; + ENetProtocolAcknowledge acknowledge; + ENetProtocolConnect connect; + ENetProtocolVerifyConnect verifyConnect; + ENetProtocolDisconnect disconnect; + ENetProtocolPing ping; + ENetProtocolSendReliable sendReliable; + ENetProtocolSendUnreliable sendUnreliable; + ENetProtocolSendUnsequenced sendUnsequenced; + ENetProtocolSendFragment sendFragment; + ENetProtocolBandwidthLimit bandwidthLimit; + ENetProtocolThrottleConfigure throttleConfigure; +} ENET_PACKED ENetProtocol; + +#ifdef _MSC_VER_ +#pragma pack(pop) +#endif + +#endif /* __ENET_PROTOCOL_H__ */ + diff --git a/examples/ThirdPartyLibs/enet/include/enet/time.h b/examples/ThirdPartyLibs/enet/include/enet/time.h new file mode 100644 index 000000000..c82a54603 --- /dev/null +++ b/examples/ThirdPartyLibs/enet/include/enet/time.h @@ -0,0 +1,18 @@ +/** + @file time.h + @brief ENet time constants and macros +*/ +#ifndef __ENET_TIME_H__ +#define __ENET_TIME_H__ + +#define ENET_TIME_OVERFLOW 86400000 + +#define ENET_TIME_LESS(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW) +#define ENET_TIME_GREATER(a, b) ((b) - (a) >= ENET_TIME_OVERFLOW) +#define ENET_TIME_LESS_EQUAL(a, b) (! ENET_TIME_GREATER (a, b)) +#define ENET_TIME_GREATER_EQUAL(a, b) (! ENET_TIME_LESS (a, b)) + +#define ENET_TIME_DIFFERENCE(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW ? (b) - (a) : (a) - (b)) + +#endif /* __ENET_TIME_H__ */ + diff --git a/examples/ThirdPartyLibs/enet/include/enet/types.h b/examples/ThirdPartyLibs/enet/include/enet/types.h new file mode 100644 index 000000000..ab010a4b1 --- /dev/null +++ b/examples/ThirdPartyLibs/enet/include/enet/types.h @@ -0,0 +1,13 @@ +/** + @file types.h + @brief type definitions for ENet +*/ +#ifndef __ENET_TYPES_H__ +#define __ENET_TYPES_H__ + +typedef unsigned char enet_uint8; /**< unsigned 8-bit type */ +typedef unsigned short enet_uint16; /**< unsigned 16-bit type */ +typedef unsigned int enet_uint32; /**< unsigned 32-bit type */ + +#endif /* __ENET_TYPES_H__ */ + diff --git a/examples/ThirdPartyLibs/enet/include/enet/unix.h b/examples/ThirdPartyLibs/enet/include/enet/unix.h new file mode 100644 index 000000000..087015e51 --- /dev/null +++ b/examples/ThirdPartyLibs/enet/include/enet/unix.h @@ -0,0 +1,45 @@ +/** + @file unix.h + @brief ENet Unix header +*/ +#ifndef __ENET_UNIX_H__ +#define __ENET_UNIX_H__ + +#include +#include +#include +#include +#include + +typedef int ENetSocket; + +enum +{ + ENET_SOCKET_NULL = -1 +}; + +#define ENET_HOST_TO_NET_16(value) (htons (value)) /**< macro that converts host to net byte-order of a 16-bit value */ +#define ENET_HOST_TO_NET_32(value) (htonl (value)) /**< macro that converts host to net byte-order of a 32-bit value */ + +#define ENET_NET_TO_HOST_16(value) (ntohs (value)) /**< macro that converts net to host byte-order of a 16-bit value */ +#define ENET_NET_TO_HOST_32(value) (ntohl (value)) /**< macro that converts net to host byte-order of a 32-bit value */ + +typedef struct +{ + void * data; + size_t dataLength; +} ENetBuffer; + +#define ENET_CALLBACK + +#define ENET_API extern + +typedef fd_set ENetSocketSet; + +#define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset)) +#define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset)) +#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLEAR (socket, & (sockset)) +#define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset)) + +#endif /* __ENET_UNIX_H__ */ + diff --git a/examples/ThirdPartyLibs/enet/include/enet/utility.h b/examples/ThirdPartyLibs/enet/include/enet/utility.h new file mode 100644 index 000000000..e48a476be --- /dev/null +++ b/examples/ThirdPartyLibs/enet/include/enet/utility.h @@ -0,0 +1,12 @@ +/** + @file utility.h + @brief ENet utility header +*/ +#ifndef __ENET_UTILITY_H__ +#define __ENET_UTILITY_H__ + +#define ENET_MAX(x, y) ((x) > (y) ? (x) : (y)) +#define ENET_MIN(x, y) ((x) < (y) ? (x) : (y)) + +#endif /* __ENET_UTILITY_H__ */ + diff --git a/examples/ThirdPartyLibs/enet/include/enet/win32.h b/examples/ThirdPartyLibs/enet/include/enet/win32.h new file mode 100644 index 000000000..0e1cf0c5a --- /dev/null +++ b/examples/ThirdPartyLibs/enet/include/enet/win32.h @@ -0,0 +1,58 @@ +/** + @file win32.h + @brief ENet Win32 header +*/ +#ifndef __ENET_WIN32_H__ +#define __ENET_WIN32_H__ + +#ifdef ENET_BUILDING_LIB +#pragma warning (disable: 4996) // 'strncpy' was declared deprecated +#pragma warning (disable: 4267) // size_t to int conversion +#pragma warning (disable: 4244) // 64bit to 32bit int +#pragma warning (disable: 4018) // signed/unsigned mismatch +#endif + +#include +#include + +typedef SOCKET ENetSocket; + +enum +{ + ENET_SOCKET_NULL = INVALID_SOCKET +}; + +#define ENET_HOST_TO_NET_16(value) (htons (value)) +#define ENET_HOST_TO_NET_32(value) (htonl (value)) + +#define ENET_NET_TO_HOST_16(value) (ntohs (value)) +#define ENET_NET_TO_HOST_32(value) (ntohl (value)) + +typedef struct +{ + size_t dataLength; + void * data; +} ENetBuffer; + +#define ENET_CALLBACK __cdecl + +#if defined ENET_DLL +#if defined ENET_BUILDING_LIB +#define ENET_API __declspec( dllexport ) +#else +#define ENET_API __declspec( dllimport ) +#endif /* ENET_BUILDING_LIB */ +#else /* !ENET_DLL */ +#define ENET_API extern +#endif /* ENET_DLL */ + +typedef fd_set ENetSocketSet; + +#define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset)) +#define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset)) +#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLEAR (socket, & (sockset)) +#define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset)) + +#endif /* __ENET_WIN32_H__ */ + + diff --git a/examples/ThirdPartyLibs/enet/list.c b/examples/ThirdPartyLibs/enet/list.c new file mode 100644 index 000000000..1c1a8dfaa --- /dev/null +++ b/examples/ThirdPartyLibs/enet/list.c @@ -0,0 +1,75 @@ +/** + @file list.c + @brief ENet linked list functions +*/ +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +/** + @defgroup list ENet linked list utility functions + @ingroup private + @{ +*/ +void +enet_list_clear (ENetList * list) +{ + list -> sentinel.next = & list -> sentinel; + list -> sentinel.previous = & list -> sentinel; +} + +ENetListIterator +enet_list_insert (ENetListIterator position, void * data) +{ + ENetListIterator result = (ENetListIterator) data; + + result -> previous = position -> previous; + result -> next = position; + + result -> previous -> next = result; + position -> previous = result; + + return result; +} + +void * +enet_list_remove (ENetListIterator position) +{ + position -> previous -> next = position -> next; + position -> next -> previous = position -> previous; + + return position; +} + +ENetListIterator +enet_list_move (ENetListIterator position, void * dataFirst, void * dataLast) +{ + ENetListIterator first = (ENetListIterator) dataFirst, + last = (ENetListIterator) dataLast; + + first -> previous -> next = last -> next; + last -> next -> previous = first -> previous; + + first -> previous = position -> previous; + last -> next = position; + + first -> previous -> next = first; + position -> previous = last; + + return first; +} + +size_t +enet_list_size (ENetList * list) +{ + size_t size = 0; + ENetListIterator position; + + for (position = enet_list_begin (list); + position != enet_list_end (list); + position = enet_list_next (position)) + ++ size; + + return size; +} + +/** @} */ diff --git a/examples/ThirdPartyLibs/enet/packet.c b/examples/ThirdPartyLibs/enet/packet.c new file mode 100644 index 000000000..9a997be4e --- /dev/null +++ b/examples/ThirdPartyLibs/enet/packet.c @@ -0,0 +1,165 @@ +/** + @file packet.c + @brief ENet packet management functions +*/ +#include +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +/** @defgroup Packet ENet packet functions + @{ +*/ + +/** Creates a packet that may be sent to a peer. + @param dataContents initial contents of the packet's data; the packet's data will remain uninitialized if dataContents is NULL. + @param dataLength size of the data allocated for this packet + @param flags flags for this packet as described for the ENetPacket structure. + @returns the packet on success, NULL on failure +*/ +ENetPacket * +enet_packet_create (const void * data, size_t dataLength, enet_uint32 flags) +{ + ENetPacket * packet = (ENetPacket *) enet_malloc (sizeof (ENetPacket)); + if (packet == NULL) + return NULL; + + if (flags & ENET_PACKET_FLAG_NO_ALLOCATE) + packet -> data = (enet_uint8 *) data; + else + if (dataLength <= 0) + packet -> data = NULL; + else + { + packet -> data = (enet_uint8 *) enet_malloc (dataLength); + if (packet -> data == NULL) + { + enet_free (packet); + return NULL; + } + + if (data != NULL) + memcpy (packet -> data, data, dataLength); + } + + packet -> referenceCount = 0; + packet -> flags = flags; + packet -> dataLength = dataLength; + packet -> freeCallback = NULL; + packet -> userData = NULL; + + return packet; +} + +/** Destroys the packet and deallocates its data. + @param packet packet to be destroyed +*/ +void +enet_packet_destroy (ENetPacket * packet) +{ + if (packet == NULL) + return; + + if (packet -> freeCallback != NULL) + (* packet -> freeCallback) (packet); + if (! (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE) && + packet -> data != NULL) + enet_free (packet -> data); + enet_free (packet); +} + +/** Attempts to resize the data in the packet to length specified in the + dataLength parameter + @param packet packet to resize + @param dataLength new size for the packet data + @returns 0 on success, < 0 on failure +*/ +int +enet_packet_resize (ENetPacket * packet, size_t dataLength) +{ + enet_uint8 * newData; + + if (dataLength <= packet -> dataLength || (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE)) + { + packet -> dataLength = dataLength; + + return 0; + } + + newData = (enet_uint8 *) enet_malloc (dataLength); + if (newData == NULL) + return -1; + + memcpy (newData, packet -> data, packet -> dataLength); + enet_free (packet -> data); + + packet -> data = newData; + packet -> dataLength = dataLength; + + return 0; +} + +static int initializedCRC32 = 0; +static enet_uint32 crcTable [256]; + +static enet_uint32 +reflect_crc (int val, int bits) +{ + int result = 0, bit; + + for (bit = 0; bit < bits; bit ++) + { + if(val & 1) result |= 1 << (bits - 1 - bit); + val >>= 1; + } + + return result; +} + +static void +initialize_crc32 (void) +{ + int byte; + + for (byte = 0; byte < 256; ++ byte) + { + enet_uint32 crc = reflect_crc (byte, 8) << 24; + int offset; + + for(offset = 0; offset < 8; ++ offset) + { + if (crc & 0x80000000) + crc = (crc << 1) ^ 0x04c11db7; + else + crc <<= 1; + } + + crcTable [byte] = reflect_crc (crc, 32); + } + + initializedCRC32 = 1; +} + +enet_uint32 +enet_crc32 (const ENetBuffer * buffers, size_t bufferCount) +{ + enet_uint32 crc = 0xFFFFFFFF; + + if (! initializedCRC32) initialize_crc32 (); + + while (bufferCount -- > 0) + { + const enet_uint8 * data = (const enet_uint8 *) buffers -> data, + * dataEnd = & data [buffers -> dataLength]; + + while (data < dataEnd) + { + crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++]; + } + + ++ buffers; + } + + return ENET_HOST_TO_NET_32 (~ crc); +} + +/** @} */ diff --git a/examples/ThirdPartyLibs/enet/peer.c b/examples/ThirdPartyLibs/enet/peer.c new file mode 100644 index 000000000..a86d793d8 --- /dev/null +++ b/examples/ThirdPartyLibs/enet/peer.c @@ -0,0 +1,959 @@ +/** + @file peer.c + @brief ENet peer management functions +*/ +#include +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +/** @defgroup peer ENet peer functions + @{ +*/ + +/** Configures throttle parameter for a peer. + + Unreliable packets are dropped by ENet in response to the varying conditions + of the Internet connection to the peer. The throttle represents a probability + that an unreliable packet should not be dropped and thus sent by ENet to the peer. + The lowest mean round trip time from the sending of a reliable packet to the + receipt of its acknowledgement is measured over an amount of time specified by + the interval parameter in milliseconds. If a measured round trip time happens to + be significantly less than the mean round trip time measured over the interval, + then the throttle probability is increased to allow more traffic by an amount + specified in the acceleration parameter, which is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE + constant. If a measured round trip time happens to be significantly greater than + the mean round trip time measured over the interval, then the throttle probability + is decreased to limit traffic by an amount specified in the deceleration parameter, which + is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE constant. When the throttle has + a value of ENET_PEER_PACKET_THROTTLE_SCALE, no unreliable packets are dropped by + ENet, and so 100% of all unreliable packets will be sent. When the throttle has a + value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable + packets will be sent. Intermediate values for the throttle represent intermediate + probabilities between 0% and 100% of unreliable packets being sent. The bandwidth + limits of the local and foreign hosts are taken into account to determine a + sensible limit for the throttle probability above which it should not raise even in + the best of conditions. + + @param peer peer to configure + @param interval interval, in milliseconds, over which to measure lowest mean RTT; the default value is ENET_PEER_PACKET_THROTTLE_INTERVAL. + @param acceleration rate at which to increase the throttle probability as mean RTT declines + @param deceleration rate at which to decrease the throttle probability as mean RTT increases +*/ +void +enet_peer_throttle_configure (ENetPeer * peer, enet_uint32 interval, enet_uint32 acceleration, enet_uint32 deceleration) +{ + ENetProtocol command; + + peer -> packetThrottleInterval = interval; + peer -> packetThrottleAcceleration = acceleration; + peer -> packetThrottleDeceleration = deceleration; + + command.header.command = ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + command.header.channelID = 0xFF; + + command.throttleConfigure.packetThrottleInterval = ENET_HOST_TO_NET_32 (interval); + command.throttleConfigure.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (acceleration); + command.throttleConfigure.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (deceleration); + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); +} + +int +enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt) +{ + if (peer -> lastRoundTripTime <= peer -> lastRoundTripTimeVariance) + { + peer -> packetThrottle = peer -> packetThrottleLimit; + } + else + if (rtt < peer -> lastRoundTripTime) + { + peer -> packetThrottle += peer -> packetThrottleAcceleration; + + if (peer -> packetThrottle > peer -> packetThrottleLimit) + peer -> packetThrottle = peer -> packetThrottleLimit; + + return 1; + } + else + if (rtt > peer -> lastRoundTripTime + 2 * peer -> lastRoundTripTimeVariance) + { + if (peer -> packetThrottle > peer -> packetThrottleDeceleration) + peer -> packetThrottle -= peer -> packetThrottleDeceleration; + else + peer -> packetThrottle = 0; + + return -1; + } + + return 0; +} + +/** Queues a packet to be sent. + @param peer destination for the packet + @param channelID channel on which to send + @param packet packet to send + @retval 0 on success + @retval < 0 on failure +*/ +int +enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet) +{ + ENetChannel * channel = & peer -> channels [channelID]; + ENetProtocol command; + size_t fragmentLength; + + if (peer -> state != ENET_PEER_STATE_CONNECTED || + channelID >= peer -> channelCount || + packet -> dataLength > ENET_PROTOCOL_MAXIMUM_PACKET_SIZE) + return -1; + + fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment); + if (peer -> host -> checksum != NULL) + fragmentLength -= sizeof(enet_uint32); + + if (packet -> dataLength > fragmentLength) + { + enet_uint32 fragmentCount = (packet -> dataLength + fragmentLength - 1) / fragmentLength, + fragmentNumber, + fragmentOffset; + enet_uint8 commandNumber; + enet_uint16 startSequenceNumber; + ENetList fragments; + ENetOutgoingCommand * fragment; + + if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT) + return -1; + + if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT)) == ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT && + channel -> outgoingUnreliableSequenceNumber < 0xFFFF) + { + commandNumber = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT; + startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingUnreliableSequenceNumber + 1); + } + else + { + commandNumber = ENET_PROTOCOL_COMMAND_SEND_FRAGMENT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingReliableSequenceNumber + 1); + } + + enet_list_clear (& fragments); + + for (fragmentNumber = 0, + fragmentOffset = 0; + fragmentOffset < packet -> dataLength; + ++ fragmentNumber, + fragmentOffset += fragmentLength) + { + if (packet -> dataLength - fragmentOffset < fragmentLength) + fragmentLength = packet -> dataLength - fragmentOffset; + + fragment = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand)); + if (fragment == NULL) + { + while (! enet_list_empty (& fragments)) + { + fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments)); + + enet_free (fragment); + } + + return -1; + } + + fragment -> fragmentOffset = fragmentOffset; + fragment -> fragmentLength = fragmentLength; + fragment -> packet = packet; + fragment -> command.header.command = commandNumber; + fragment -> command.header.channelID = channelID; + fragment -> command.sendFragment.startSequenceNumber = startSequenceNumber; + fragment -> command.sendFragment.dataLength = ENET_HOST_TO_NET_16 (fragmentLength); + fragment -> command.sendFragment.fragmentCount = ENET_HOST_TO_NET_32 (fragmentCount); + fragment -> command.sendFragment.fragmentNumber = ENET_HOST_TO_NET_32 (fragmentNumber); + fragment -> command.sendFragment.totalLength = ENET_HOST_TO_NET_32 (packet -> dataLength); + fragment -> command.sendFragment.fragmentOffset = ENET_NET_TO_HOST_32 (fragmentOffset); + + enet_list_insert (enet_list_end (& fragments), fragment); + } + + packet -> referenceCount += fragmentNumber; + + while (! enet_list_empty (& fragments)) + { + fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments)); + + enet_peer_setup_outgoing_command (peer, fragment); + } + + return 0; + } + + command.header.channelID = channelID; + + if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNSEQUENCED)) == ENET_PACKET_FLAG_UNSEQUENCED) + { + command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; + command.sendUnsequenced.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); + } + else + if (packet -> flags & ENET_PACKET_FLAG_RELIABLE || channel -> outgoingUnreliableSequenceNumber >= 0xFFFF) + { + command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + command.sendReliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); + } + else + { + command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE; + command.sendUnreliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); + } + + if (enet_peer_queue_outgoing_command (peer, & command, packet, 0, packet -> dataLength) == NULL) + return -1; + + return 0; +} + +/** Attempts to dequeue any incoming queued packet. + @param peer peer to dequeue packets from + @param channelID holds the channel ID of the channel the packet was received on success + @returns a pointer to the packet, or NULL if there are no available incoming queued packets +*/ +ENetPacket * +enet_peer_receive (ENetPeer * peer, enet_uint8 * channelID) +{ + ENetIncomingCommand * incomingCommand; + ENetPacket * packet; + + if (enet_list_empty (& peer -> dispatchedCommands)) + return NULL; + + incomingCommand = (ENetIncomingCommand *) enet_list_remove (enet_list_begin (& peer -> dispatchedCommands)); + + if (channelID != NULL) + * channelID = incomingCommand -> command.header.channelID; + + packet = incomingCommand -> packet; + + -- packet -> referenceCount; + + if (incomingCommand -> fragments != NULL) + enet_free (incomingCommand -> fragments); + + enet_free (incomingCommand); + + return packet; +} + +static void +enet_peer_reset_outgoing_commands (ENetList * queue) +{ + ENetOutgoingCommand * outgoingCommand; + + while (! enet_list_empty (queue)) + { + outgoingCommand = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (queue)); + + if (outgoingCommand -> packet != NULL) + { + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (outgoingCommand -> packet); + } + + enet_free (outgoingCommand); + } +} + +static void +enet_peer_remove_incoming_commands (ENetList * queue, ENetListIterator startCommand, ENetListIterator endCommand) +{ + ENetListIterator currentCommand; + + for (currentCommand = startCommand; currentCommand != endCommand; ) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + currentCommand = enet_list_next (currentCommand); + + enet_list_remove (& incomingCommand -> incomingCommandList); + + if (incomingCommand -> packet != NULL) + { + -- incomingCommand -> packet -> referenceCount; + + if (incomingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (incomingCommand -> packet); + } + + if (incomingCommand -> fragments != NULL) + enet_free (incomingCommand -> fragments); + + enet_free (incomingCommand); + } +} + +static void +enet_peer_reset_incoming_commands (ENetList * queue) +{ + enet_peer_remove_incoming_commands(queue, enet_list_begin (queue), enet_list_end (queue)); +} + +void +enet_peer_reset_queues (ENetPeer * peer) +{ + ENetChannel * channel; + + if (peer -> needsDispatch) + { + enet_list_remove (& peer -> dispatchList); + + peer -> needsDispatch = 0; + } + + while (! enet_list_empty (& peer -> acknowledgements)) + enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements))); + + enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands); + enet_peer_reset_outgoing_commands (& peer -> sentUnreliableCommands); + enet_peer_reset_outgoing_commands (& peer -> outgoingReliableCommands); + enet_peer_reset_outgoing_commands (& peer -> outgoingUnreliableCommands); + enet_peer_reset_incoming_commands (& peer -> dispatchedCommands); + + if (peer -> channels != NULL && peer -> channelCount > 0) + { + for (channel = peer -> channels; + channel < & peer -> channels [peer -> channelCount]; + ++ channel) + { + enet_peer_reset_incoming_commands (& channel -> incomingReliableCommands); + enet_peer_reset_incoming_commands (& channel -> incomingUnreliableCommands); + } + + enet_free (peer -> channels); + } + + peer -> channels = NULL; + peer -> channelCount = 0; +} + +/** Forcefully disconnects a peer. + @param peer peer to forcefully disconnect + @remarks The foreign host represented by the peer is not notified of the disconnection and will timeout + on its connection to the local host. +*/ +void +enet_peer_reset (ENetPeer * peer) +{ + peer -> outgoingPeerID = ENET_PROTOCOL_MAXIMUM_PEER_ID; + peer -> connectID = 0; + + peer -> state = ENET_PEER_STATE_DISCONNECTED; + + peer -> incomingBandwidth = 0; + peer -> outgoingBandwidth = 0; + peer -> incomingBandwidthThrottleEpoch = 0; + peer -> outgoingBandwidthThrottleEpoch = 0; + peer -> incomingDataTotal = 0; + peer -> outgoingDataTotal = 0; + peer -> lastSendTime = 0; + peer -> lastReceiveTime = 0; + peer -> nextTimeout = 0; + peer -> earliestTimeout = 0; + peer -> packetLossEpoch = 0; + peer -> packetsSent = 0; + peer -> packetsLost = 0; + peer -> packetLoss = 0; + peer -> packetLossVariance = 0; + peer -> packetThrottle = ENET_PEER_DEFAULT_PACKET_THROTTLE; + peer -> packetThrottleLimit = ENET_PEER_PACKET_THROTTLE_SCALE; + peer -> packetThrottleCounter = 0; + peer -> packetThrottleEpoch = 0; + peer -> packetThrottleAcceleration = ENET_PEER_PACKET_THROTTLE_ACCELERATION; + peer -> packetThrottleDeceleration = ENET_PEER_PACKET_THROTTLE_DECELERATION; + peer -> packetThrottleInterval = ENET_PEER_PACKET_THROTTLE_INTERVAL; + peer -> pingInterval = ENET_PEER_PING_INTERVAL; + peer -> timeoutLimit = ENET_PEER_TIMEOUT_LIMIT; + peer -> timeoutMinimum = ENET_PEER_TIMEOUT_MINIMUM; + peer -> timeoutMaximum = ENET_PEER_TIMEOUT_MAXIMUM; + peer -> lastRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; + peer -> lowestRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; + peer -> lastRoundTripTimeVariance = 0; + peer -> highestRoundTripTimeVariance = 0; + peer -> roundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; + peer -> roundTripTimeVariance = 0; + peer -> mtu = peer -> host -> mtu; + peer -> reliableDataInTransit = 0; + peer -> outgoingReliableSequenceNumber = 0; + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + peer -> incomingUnsequencedGroup = 0; + peer -> outgoingUnsequencedGroup = 0; + peer -> eventData = 0; + + memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow)); + + enet_peer_reset_queues (peer); +} + +/** Sends a ping request to a peer. + @param peer destination for the ping request + @remarks ping requests factor into the mean round trip time as designated by the + roundTripTime field in the ENetPeer structure. Enet automatically pings all connected + peers at regular intervals, however, this function may be called to ensure more + frequent ping requests. +*/ +void +enet_peer_ping (ENetPeer * peer) +{ + ENetProtocol command; + + if (peer -> state != ENET_PEER_STATE_CONNECTED) + return; + + command.header.command = ENET_PROTOCOL_COMMAND_PING | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + command.header.channelID = 0xFF; + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); +} + +/** Sets the interval at which pings will be sent to a peer. + + Pings are used both to monitor the liveness of the connection and also to dynamically + adjust the throttle during periods of low traffic so that the throttle has reasonable + responsiveness during traffic spikes. + + @param peer the peer to adjust + @param pingInterval the interval at which to send pings; defaults to ENET_PEER_PING_INTERVAL if 0 +*/ +void +enet_peer_ping_interval (ENetPeer * peer, enet_uint32 pingInterval) +{ + peer -> pingInterval = pingInterval ? pingInterval : ENET_PEER_PING_INTERVAL; +} + +/** Sets the timeout parameters for a peer. + + The timeout parameter control how and when a peer will timeout from a failure to acknowledge + reliable traffic. Timeout values use an exponential backoff mechanism, where if a reliable + packet is not acknowledge within some multiple of the average RTT plus a variance tolerance, + the timeout will be doubled until it reaches a set limit. If the timeout is thus at this + limit and reliable packets have been sent but not acknowledged within a certain minimum time + period, the peer will be disconnected. Alternatively, if reliable packets have been sent + but not acknowledged for a certain maximum time period, the peer will be disconnected regardless + of the current timeout limit value. + + @param peer the peer to adjust + @param timeoutLimit the timeout limit; defaults to ENET_PEER_TIMEOUT_LIMIT if 0 + @param timeoutMinimum the timeout minimum; defaults to ENET_PEER_TIMEOUT_MINIMUM if 0 + @param timeoutMaximum the timeout maximum; defaults to ENET_PEER_TIMEOUT_MAXIMUM if 0 +*/ + +void +enet_peer_timeout (ENetPeer * peer, enet_uint32 timeoutLimit, enet_uint32 timeoutMinimum, enet_uint32 timeoutMaximum) +{ + peer -> timeoutLimit = timeoutLimit ? timeoutLimit : ENET_PEER_TIMEOUT_LIMIT; + peer -> timeoutMinimum = timeoutMinimum ? timeoutMinimum : ENET_PEER_TIMEOUT_MINIMUM; + peer -> timeoutMaximum = timeoutMaximum ? timeoutMaximum : ENET_PEER_TIMEOUT_MAXIMUM; +} + +/** Force an immediate disconnection from a peer. + @param peer peer to disconnect + @param data data describing the disconnection + @remarks No ENET_EVENT_DISCONNECT event will be generated. The foreign peer is not + guarenteed to receive the disconnect notification, and is reset immediately upon + return from this function. +*/ +void +enet_peer_disconnect_now (ENetPeer * peer, enet_uint32 data) +{ + ENetProtocol command; + + if (peer -> state == ENET_PEER_STATE_DISCONNECTED) + return; + + if (peer -> state != ENET_PEER_STATE_ZOMBIE && + peer -> state != ENET_PEER_STATE_DISCONNECTING) + { + enet_peer_reset_queues (peer); + + command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; + command.header.channelID = 0xFF; + command.disconnect.data = ENET_HOST_TO_NET_32 (data); + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); + + enet_host_flush (peer -> host); + } + + enet_peer_reset (peer); +} + +/** Request a disconnection from a peer. + @param peer peer to request a disconnection + @param data data describing the disconnection + @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service() + once the disconnection is complete. +*/ +void +enet_peer_disconnect (ENetPeer * peer, enet_uint32 data) +{ + ENetProtocol command; + + if (peer -> state == ENET_PEER_STATE_DISCONNECTING || + peer -> state == ENET_PEER_STATE_DISCONNECTED || + peer -> state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT || + peer -> state == ENET_PEER_STATE_ZOMBIE) + return; + + enet_peer_reset_queues (peer); + + command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT; + command.header.channelID = 0xFF; + command.disconnect.data = ENET_HOST_TO_NET_32 (data); + + if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) + command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + else + command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); + + if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) + peer -> state = ENET_PEER_STATE_DISCONNECTING; + else + { + enet_host_flush (peer -> host); + enet_peer_reset (peer); + } +} + +/** Request a disconnection from a peer, but only after all queued outgoing packets are sent. + @param peer peer to request a disconnection + @param data data describing the disconnection + @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service() + once the disconnection is complete. +*/ +void +enet_peer_disconnect_later (ENetPeer * peer, enet_uint32 data) +{ + if ((peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) && + ! (enet_list_empty (& peer -> outgoingReliableCommands) && + enet_list_empty (& peer -> outgoingUnreliableCommands) && + enet_list_empty (& peer -> sentReliableCommands))) + { + peer -> state = ENET_PEER_STATE_DISCONNECT_LATER; + peer -> eventData = data; + } + else + enet_peer_disconnect (peer, data); +} + +ENetAcknowledgement * +enet_peer_queue_acknowledgement (ENetPeer * peer, const ENetProtocol * command, enet_uint16 sentTime) +{ + ENetAcknowledgement * acknowledgement; + + if (command -> header.channelID < peer -> channelCount) + { + ENetChannel * channel = & peer -> channels [command -> header.channelID]; + enet_uint16 reliableWindow = command -> header.reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE, + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + + if (command -> header.reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + reliableWindow += ENET_PEER_RELIABLE_WINDOWS; + + if (reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1 && reliableWindow <= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS) + return NULL; + } + + acknowledgement = (ENetAcknowledgement *) enet_malloc (sizeof (ENetAcknowledgement)); + if (acknowledgement == NULL) + return NULL; + + peer -> outgoingDataTotal += sizeof (ENetProtocolAcknowledge); + + acknowledgement -> sentTime = sentTime; + acknowledgement -> command = * command; + + enet_list_insert (enet_list_end (& peer -> acknowledgements), acknowledgement); + + return acknowledgement; +} + +void +enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoingCommand) +{ + ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID]; + + peer -> outgoingDataTotal += enet_protocol_command_size (outgoingCommand -> command.header.command) + outgoingCommand -> fragmentLength; + + if (outgoingCommand -> command.header.channelID == 0xFF) + { + ++ peer -> outgoingReliableSequenceNumber; + + outgoingCommand -> reliableSequenceNumber = peer -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) + { + ++ channel -> outgoingReliableSequenceNumber; + channel -> outgoingUnreliableSequenceNumber = 0; + + outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED) + { + ++ peer -> outgoingUnsequencedGroup; + + outgoingCommand -> reliableSequenceNumber = 0; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + { + if (outgoingCommand -> fragmentOffset == 0) + ++ channel -> outgoingUnreliableSequenceNumber; + + outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber; + } + + outgoingCommand -> sendAttempts = 0; + outgoingCommand -> sentTime = 0; + outgoingCommand -> roundTripTimeout = 0; + outgoingCommand -> roundTripTimeoutLimit = 0; + outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> reliableSequenceNumber); + + switch (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) + { + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: + outgoingCommand -> command.sendUnreliable.unreliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> unreliableSequenceNumber); + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: + outgoingCommand -> command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup); + break; + + default: + break; + } + + if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) + enet_list_insert (enet_list_end (& peer -> outgoingReliableCommands), outgoingCommand); + else + enet_list_insert (enet_list_end (& peer -> outgoingUnreliableCommands), outgoingCommand); +} + +ENetOutgoingCommand * +enet_peer_queue_outgoing_command (ENetPeer * peer, const ENetProtocol * command, ENetPacket * packet, enet_uint32 offset, enet_uint16 length) +{ + ENetOutgoingCommand * outgoingCommand = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand)); + if (outgoingCommand == NULL) + return NULL; + + outgoingCommand -> command = * command; + outgoingCommand -> fragmentOffset = offset; + outgoingCommand -> fragmentLength = length; + outgoingCommand -> packet = packet; + if (packet != NULL) + ++ packet -> referenceCount; + + enet_peer_setup_outgoing_command (peer, outgoingCommand); + + return outgoingCommand; +} + +void +enet_peer_dispatch_incoming_unreliable_commands (ENetPeer * peer, ENetChannel * channel) +{ + ENetListIterator droppedCommand, startCommand, currentCommand; + + for (droppedCommand = startCommand = currentCommand = enet_list_begin (& channel -> incomingUnreliableCommands); + currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); + currentCommand = enet_list_next (currentCommand)) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) + continue; + + if (incomingCommand -> reliableSequenceNumber == channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> fragmentsRemaining <= 0) + { + channel -> incomingUnreliableSequenceNumber = incomingCommand -> unreliableSequenceNumber; + continue; + } + + if (startCommand != currentCommand) + { + enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand)); + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } + + droppedCommand = currentCommand; + } + else + if (droppedCommand != currentCommand) + droppedCommand = enet_list_previous (currentCommand); + } + else + { + enet_uint16 reliableWindow = incomingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE, + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + reliableWindow += ENET_PEER_RELIABLE_WINDOWS; + if (reliableWindow >= currentWindow && reliableWindow < currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) + break; + + droppedCommand = enet_list_next (currentCommand); + + if (startCommand != currentCommand) + { + enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand)); + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } + } + } + + startCommand = enet_list_next (currentCommand); + } + + if (startCommand != currentCommand) + { + enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand)); + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } + + droppedCommand = currentCommand; + } + + enet_peer_remove_incoming_commands (& channel -> incomingUnreliableCommands, enet_list_begin (& channel -> incomingUnreliableCommands), droppedCommand); +} + +void +enet_peer_dispatch_incoming_reliable_commands (ENetPeer * peer, ENetChannel * channel) +{ + ENetListIterator currentCommand; + + for (currentCommand = enet_list_begin (& channel -> incomingReliableCommands); + currentCommand != enet_list_end (& channel -> incomingReliableCommands); + currentCommand = enet_list_next (currentCommand)) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + if (incomingCommand -> fragmentsRemaining > 0 || + incomingCommand -> reliableSequenceNumber != (enet_uint16) (channel -> incomingReliableSequenceNumber + 1)) + break; + + channel -> incomingReliableSequenceNumber = incomingCommand -> reliableSequenceNumber; + + if (incomingCommand -> fragmentCount > 0) + channel -> incomingReliableSequenceNumber += incomingCommand -> fragmentCount - 1; + } + + if (currentCommand == enet_list_begin (& channel -> incomingReliableCommands)) + return; + + channel -> incomingUnreliableSequenceNumber = 0; + + enet_list_move (enet_list_end (& peer -> dispatchedCommands), enet_list_begin (& channel -> incomingReliableCommands), enet_list_previous (currentCommand)); + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } + + if (! enet_list_empty (& channel -> incomingUnreliableCommands)) + enet_peer_dispatch_incoming_unreliable_commands (peer, channel); +} + +ENetIncomingCommand * +enet_peer_queue_incoming_command (ENetPeer * peer, const ENetProtocol * command, ENetPacket * packet, enet_uint32 fragmentCount) +{ + static ENetIncomingCommand dummyCommand; + + ENetChannel * channel = & peer -> channels [command -> header.channelID]; + enet_uint32 unreliableSequenceNumber = 0, reliableSequenceNumber = 0; + enet_uint16 reliableWindow, currentWindow; + ENetIncomingCommand * incomingCommand; + ENetListIterator currentCommand; + + if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) + goto freePacket; + + if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) + { + reliableSequenceNumber = command -> header.reliableSequenceNumber; + reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + + if (reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + reliableWindow += ENET_PEER_RELIABLE_WINDOWS; + + if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) + goto freePacket; + } + + switch (command -> header.command & ENET_PROTOCOL_COMMAND_MASK) + { + case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: + case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: + if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber) + goto freePacket; + + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands)); + currentCommand != enet_list_end (& channel -> incomingReliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + incomingCommand = (ENetIncomingCommand *) currentCommand; + + if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + continue; + } + else + if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber <= reliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber) + break; + + goto freePacket; + } + } + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT: + unreliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendUnreliable.unreliableSequenceNumber); + + if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber && + unreliableSequenceNumber <= channel -> incomingUnreliableSequenceNumber) + goto freePacket; + + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands)); + currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + incomingCommand = (ENetIncomingCommand *) currentCommand; + + if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) + continue; + + if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + continue; + } + else + if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber > reliableSequenceNumber) + continue; + + if (incomingCommand -> unreliableSequenceNumber <= unreliableSequenceNumber) + { + if (incomingCommand -> unreliableSequenceNumber < unreliableSequenceNumber) + break; + + goto freePacket; + } + } + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: + currentCommand = enet_list_end (& channel -> incomingUnreliableCommands); + break; + + default: + goto freePacket; + } + + incomingCommand = (ENetIncomingCommand *) enet_malloc (sizeof (ENetIncomingCommand)); + if (incomingCommand == NULL) + goto notifyError; + + incomingCommand -> reliableSequenceNumber = command -> header.reliableSequenceNumber; + incomingCommand -> unreliableSequenceNumber = unreliableSequenceNumber & 0xFFFF; + incomingCommand -> command = * command; + incomingCommand -> fragmentCount = fragmentCount; + incomingCommand -> fragmentsRemaining = fragmentCount; + incomingCommand -> packet = packet; + incomingCommand -> fragments = NULL; + + if (fragmentCount > 0) + { + if (fragmentCount <= ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT) + incomingCommand -> fragments = (enet_uint32 *) enet_malloc ((fragmentCount + 31) / 32 * sizeof (enet_uint32)); + if (incomingCommand -> fragments == NULL) + { + enet_free (incomingCommand); + + goto notifyError; + } + memset (incomingCommand -> fragments, 0, (fragmentCount + 31) / 32 * sizeof (enet_uint32)); + } + + if (packet != NULL) + ++ packet -> referenceCount; + + enet_list_insert (enet_list_next (currentCommand), incomingCommand); + + switch (command -> header.command & ENET_PROTOCOL_COMMAND_MASK) + { + case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: + case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: + enet_peer_dispatch_incoming_reliable_commands (peer, channel); + break; + + default: + enet_peer_dispatch_incoming_unreliable_commands (peer, channel); + break; + } + + return incomingCommand; + +freePacket: + if (fragmentCount > 0) + goto notifyError; + + if (packet != NULL && packet -> referenceCount == 0) + enet_packet_destroy (packet); + + return & dummyCommand; + +notifyError: + if (packet != NULL && packet -> referenceCount == 0) + enet_packet_destroy (packet); + + return NULL; +} + +/** @} */ diff --git a/examples/ThirdPartyLibs/enet/premake4.lua b/examples/ThirdPartyLibs/enet/premake4.lua new file mode 100644 index 000000000..522db3431 --- /dev/null +++ b/examples/ThirdPartyLibs/enet/premake4.lua @@ -0,0 +1,31 @@ + project "enet" + + kind "StaticLib" + + if os.is("Windows") then + defines { "WIN32" } + files{"win32.c"} + end + if os.is("Linux") then + defines {"HAS_SOCKLEN_T"} + files {"unix.c",} + end + if os.is("MacOSX") then + files{"unix.c"} + end + + targetdir "../../../lib" + + includedirs { + ".","include" + } + files { + "callbacks.c", + "compress.c", + "host.c", + "list.c", + "packet.c", + "peer.c", + "protocol.c", + "**.h" + } diff --git a/examples/ThirdPartyLibs/enet/protocol.c b/examples/ThirdPartyLibs/enet/protocol.c new file mode 100644 index 000000000..d6cc33a49 --- /dev/null +++ b/examples/ThirdPartyLibs/enet/protocol.c @@ -0,0 +1,1899 @@ +/** + @file protocol.c + @brief ENet protocol functions +*/ +#include +#include +#define ENET_BUILDING_LIB 1 +#include "enet/utility.h" +#include "enet/time.h" +#include "enet/enet.h" + +static size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] = +{ + 0, + sizeof (ENetProtocolAcknowledge), + sizeof (ENetProtocolConnect), + sizeof (ENetProtocolVerifyConnect), + sizeof (ENetProtocolDisconnect), + sizeof (ENetProtocolPing), + sizeof (ENetProtocolSendReliable), + sizeof (ENetProtocolSendUnreliable), + sizeof (ENetProtocolSendFragment), + sizeof (ENetProtocolSendUnsequenced), + sizeof (ENetProtocolBandwidthLimit), + sizeof (ENetProtocolThrottleConfigure), + sizeof (ENetProtocolSendFragment) +}; + +size_t +enet_protocol_command_size (enet_uint8 commandNumber) +{ + return commandSizes [commandNumber & ENET_PROTOCOL_COMMAND_MASK]; +} + +static int +enet_protocol_dispatch_incoming_commands (ENetHost * host, ENetEvent * event) +{ + while (! enet_list_empty (& host -> dispatchQueue)) + { + ENetPeer * peer = (ENetPeer *) enet_list_remove (enet_list_begin (& host -> dispatchQueue)); + + peer -> needsDispatch = 0; + + switch (peer -> state) + { + case ENET_PEER_STATE_CONNECTION_PENDING: + case ENET_PEER_STATE_CONNECTION_SUCCEEDED: + peer -> state = ENET_PEER_STATE_CONNECTED; + + event -> type = ENET_EVENT_TYPE_CONNECT; + event -> peer = peer; + event -> data = peer -> eventData; + + return 1; + + case ENET_PEER_STATE_ZOMBIE: + host -> recalculateBandwidthLimits = 1; + + event -> type = ENET_EVENT_TYPE_DISCONNECT; + event -> peer = peer; + event -> data = peer -> eventData; + + enet_peer_reset (peer); + + return 1; + + case ENET_PEER_STATE_CONNECTED: + if (enet_list_empty (& peer -> dispatchedCommands)) + continue; + + event -> packet = enet_peer_receive (peer, & event -> channelID); + if (event -> packet == NULL) + continue; + + event -> type = ENET_EVENT_TYPE_RECEIVE; + event -> peer = peer; + + if (! enet_list_empty (& peer -> dispatchedCommands)) + { + peer -> needsDispatch = 1; + + enet_list_insert (enet_list_end (& host -> dispatchQueue), & peer -> dispatchList); + } + + return 1; + + default: + break; + } + } + + return 0; +} + +static void +enet_protocol_dispatch_state (ENetHost * host, ENetPeer * peer, ENetPeerState state) +{ + peer -> state = state; + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } +} + +static void +enet_protocol_notify_connect (ENetHost * host, ENetPeer * peer, ENetEvent * event) +{ + host -> recalculateBandwidthLimits = 1; + + if (event != NULL) + { + peer -> state = ENET_PEER_STATE_CONNECTED; + + event -> type = ENET_EVENT_TYPE_CONNECT; + event -> peer = peer; + event -> data = peer -> eventData; + } + else + enet_protocol_dispatch_state (host, peer, peer -> state == ENET_PEER_STATE_CONNECTING ? ENET_PEER_STATE_CONNECTION_SUCCEEDED : ENET_PEER_STATE_CONNECTION_PENDING); +} + +static void +enet_protocol_notify_disconnect (ENetHost * host, ENetPeer * peer, ENetEvent * event) +{ + if (peer -> state >= ENET_PEER_STATE_CONNECTION_PENDING) + host -> recalculateBandwidthLimits = 1; + + if (peer -> state != ENET_PEER_STATE_CONNECTING && peer -> state < ENET_PEER_STATE_CONNECTION_SUCCEEDED) + enet_peer_reset (peer); + else + if (event != NULL) + { + event -> type = ENET_EVENT_TYPE_DISCONNECT; + event -> peer = peer; + event -> data = 0; + + enet_peer_reset (peer); + } + else + { + peer -> eventData = 0; + + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + } +} + +static void +enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer) +{ + ENetOutgoingCommand * outgoingCommand; + + while (! enet_list_empty (& peer -> sentUnreliableCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentUnreliableCommands); + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + + if (outgoingCommand -> packet != NULL) + { + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + { + outgoingCommand -> packet -> flags |= ENET_PACKET_FLAG_SENT; + + enet_packet_destroy (outgoingCommand -> packet); + } + } + + enet_free (outgoingCommand); + } +} + +static ENetProtocolCommand +enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliableSequenceNumber, enet_uint8 channelID) +{ + ENetOutgoingCommand * outgoingCommand = NULL; + ENetListIterator currentCommand; + ENetProtocolCommand commandNumber; + int wasSent = 1; + + for (currentCommand = enet_list_begin (& peer -> sentReliableCommands); + currentCommand != enet_list_end (& peer -> sentReliableCommands); + currentCommand = enet_list_next (currentCommand)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber && + outgoingCommand -> command.header.channelID == channelID) + break; + } + + if (currentCommand == enet_list_end (& peer -> sentReliableCommands)) + { + for (currentCommand = enet_list_begin (& peer -> outgoingReliableCommands); + currentCommand != enet_list_end (& peer -> outgoingReliableCommands); + currentCommand = enet_list_next (currentCommand)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + if (outgoingCommand -> sendAttempts < 1) return ENET_PROTOCOL_COMMAND_NONE; + + if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber && + outgoingCommand -> command.header.channelID == channelID) + break; + } + + if (currentCommand == enet_list_end (& peer -> outgoingReliableCommands)) + return ENET_PROTOCOL_COMMAND_NONE; + + wasSent = 0; + } + + if (outgoingCommand == NULL) + return ENET_PROTOCOL_COMMAND_NONE; + + if (channelID < peer -> channelCount) + { + ENetChannel * channel = & peer -> channels [channelID]; + enet_uint16 reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + if (channel -> reliableWindows [reliableWindow] > 0) + { + -- channel -> reliableWindows [reliableWindow]; + if (! channel -> reliableWindows [reliableWindow]) + channel -> usedReliableWindows &= ~ (1 << reliableWindow); + } + } + + commandNumber = (ENetProtocolCommand) (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK); + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + + if (outgoingCommand -> packet != NULL) + { + if (wasSent) + peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength; + + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + { + outgoingCommand -> packet -> flags |= ENET_PACKET_FLAG_SENT; + + enet_packet_destroy (outgoingCommand -> packet); + } + } + + enet_free (outgoingCommand); + + if (enet_list_empty (& peer -> sentReliableCommands)) + return commandNumber; + + outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentReliableCommands); + + peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout; + + return commandNumber; +} + +static ENetPeer * +enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENetProtocol * command) +{ + enet_uint8 incomingSessionID, outgoingSessionID; + enet_uint32 mtu, windowSize; + ENetChannel * channel; + size_t channelCount; + ENetPeer * currentPeer; + ENetProtocol verifyCommand; + + channelCount = ENET_NET_TO_HOST_32 (command -> connect.channelCount); + + if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || + channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) + return NULL; + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state != ENET_PEER_STATE_DISCONNECTED && + currentPeer -> address.host == host -> receivedAddress.host && + currentPeer -> address.port == host -> receivedAddress.port && + currentPeer -> connectID == command -> connect.connectID) + return NULL; + } + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED) + break; + } + + if (currentPeer >= & host -> peers [host -> peerCount]) + return NULL; + + if (channelCount > host -> channelLimit) + channelCount = host -> channelLimit; + currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel)); + if (currentPeer -> channels == NULL) + return NULL; + currentPeer -> channelCount = channelCount; + currentPeer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT; + currentPeer -> connectID = command -> connect.connectID; + currentPeer -> address = host -> receivedAddress; + currentPeer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID); + currentPeer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth); + currentPeer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth); + currentPeer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleInterval); + currentPeer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleAcceleration); + currentPeer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleDeceleration); + currentPeer -> eventData = ENET_NET_TO_HOST_32 (command -> connect.data); + + incomingSessionID = command -> connect.incomingSessionID == 0xFF ? currentPeer -> outgoingSessionID : command -> connect.incomingSessionID; + incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); + if (incomingSessionID == currentPeer -> outgoingSessionID) + incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); + currentPeer -> outgoingSessionID = incomingSessionID; + + outgoingSessionID = command -> connect.outgoingSessionID == 0xFF ? currentPeer -> incomingSessionID : command -> connect.outgoingSessionID; + outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); + if (outgoingSessionID == currentPeer -> incomingSessionID) + outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); + currentPeer -> incomingSessionID = outgoingSessionID; + + for (channel = currentPeer -> channels; + channel < & currentPeer -> channels [channelCount]; + ++ channel) + { + channel -> outgoingReliableSequenceNumber = 0; + channel -> outgoingUnreliableSequenceNumber = 0; + channel -> incomingReliableSequenceNumber = 0; + channel -> incomingUnreliableSequenceNumber = 0; + + enet_list_clear (& channel -> incomingReliableCommands); + enet_list_clear (& channel -> incomingUnreliableCommands); + + channel -> usedReliableWindows = 0; + memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows)); + } + + mtu = ENET_NET_TO_HOST_32 (command -> connect.mtu); + + if (mtu < ENET_PROTOCOL_MINIMUM_MTU) + mtu = ENET_PROTOCOL_MINIMUM_MTU; + else + if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) + mtu = ENET_PROTOCOL_MAXIMUM_MTU; + + currentPeer -> mtu = mtu; + + if (host -> outgoingBandwidth == 0 && + currentPeer -> incomingBandwidth == 0) + currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + if (host -> outgoingBandwidth == 0 || + currentPeer -> incomingBandwidth == 0) + currentPeer -> windowSize = (ENET_MAX (host -> outgoingBandwidth, currentPeer -> incomingBandwidth) / + ENET_PEER_WINDOW_SIZE_SCALE) * + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + currentPeer -> windowSize = (ENET_MIN (host -> outgoingBandwidth, currentPeer -> incomingBandwidth) / + ENET_PEER_WINDOW_SIZE_SCALE) * + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + if (host -> incomingBandwidth == 0) + windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + windowSize = (host -> incomingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) * + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (windowSize > ENET_NET_TO_HOST_32 (command -> connect.windowSize)) + windowSize = ENET_NET_TO_HOST_32 (command -> connect.windowSize); + + if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + verifyCommand.header.command = ENET_PROTOCOL_COMMAND_VERIFY_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + verifyCommand.header.channelID = 0xFF; + verifyCommand.verifyConnect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID); + verifyCommand.verifyConnect.incomingSessionID = incomingSessionID; + verifyCommand.verifyConnect.outgoingSessionID = outgoingSessionID; + verifyCommand.verifyConnect.mtu = ENET_HOST_TO_NET_32 (currentPeer -> mtu); + verifyCommand.verifyConnect.windowSize = ENET_HOST_TO_NET_32 (windowSize); + verifyCommand.verifyConnect.channelCount = ENET_HOST_TO_NET_32 (channelCount); + verifyCommand.verifyConnect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth); + verifyCommand.verifyConnect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); + verifyCommand.verifyConnect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval); + verifyCommand.verifyConnect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration); + verifyCommand.verifyConnect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration); + verifyCommand.verifyConnect.connectID = currentPeer -> connectID; + + enet_peer_queue_outgoing_command (currentPeer, & verifyCommand, NULL, 0, 0); + + return currentPeer; +} + +static int +enet_protocol_handle_send_reliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + ENetPacket * packet; + size_t dataLength; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + dataLength = ENET_NET_TO_HOST_16 (command -> sendReliable.dataLength); + * currentData += dataLength; + if (dataLength > ENET_PROTOCOL_MAXIMUM_PACKET_SIZE || + * currentData < host -> receivedData || + * currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + packet = enet_packet_create ((const enet_uint8 *) command + sizeof (ENetProtocolSendReliable), + dataLength, + ENET_PACKET_FLAG_RELIABLE); + if (packet == NULL || + enet_peer_queue_incoming_command (peer, command, packet, 0) == NULL) + return -1; + + return 0; +} + +static int +enet_protocol_handle_send_unsequenced (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + ENetPacket * packet; + enet_uint32 unsequencedGroup, index; + size_t dataLength; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + dataLength = ENET_NET_TO_HOST_16 (command -> sendUnsequenced.dataLength); + * currentData += dataLength; + if (dataLength > ENET_PROTOCOL_MAXIMUM_PACKET_SIZE || + * currentData < host -> receivedData || + * currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + unsequencedGroup = ENET_NET_TO_HOST_16 (command -> sendUnsequenced.unsequencedGroup); + index = unsequencedGroup % ENET_PEER_UNSEQUENCED_WINDOW_SIZE; + + if (unsequencedGroup < peer -> incomingUnsequencedGroup) + unsequencedGroup += 0x10000; + + if (unsequencedGroup >= (enet_uint32) peer -> incomingUnsequencedGroup + ENET_PEER_FREE_UNSEQUENCED_WINDOWS * ENET_PEER_UNSEQUENCED_WINDOW_SIZE) + return 0; + + unsequencedGroup &= 0xFFFF; + + if (unsequencedGroup - index != peer -> incomingUnsequencedGroup) + { + peer -> incomingUnsequencedGroup = unsequencedGroup - index; + + memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow)); + } + else + if (peer -> unsequencedWindow [index / 32] & (1 << (index % 32))) + return 0; + + packet = enet_packet_create ((const enet_uint8 *) command + sizeof (ENetProtocolSendUnsequenced), + dataLength, + ENET_PACKET_FLAG_UNSEQUENCED); + if (packet == NULL || + enet_peer_queue_incoming_command (peer, command, packet, 0) == NULL) + return -1; + + peer -> unsequencedWindow [index / 32] |= 1 << (index % 32); + + return 0; +} + +static int +enet_protocol_handle_send_unreliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + ENetPacket * packet; + size_t dataLength; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + dataLength = ENET_NET_TO_HOST_16 (command -> sendUnreliable.dataLength); + * currentData += dataLength; + if (dataLength > ENET_PROTOCOL_MAXIMUM_PACKET_SIZE || + * currentData < host -> receivedData || + * currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + packet = enet_packet_create ((const enet_uint8 *) command + sizeof (ENetProtocolSendUnreliable), + dataLength, + 0); + if (packet == NULL || + enet_peer_queue_incoming_command (peer, command, packet, 0) == NULL) + return -1; + + return 0; +} + +static int +enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + enet_uint32 fragmentNumber, + fragmentCount, + fragmentOffset, + fragmentLength, + startSequenceNumber, + totalLength; + ENetChannel * channel; + enet_uint16 startWindow, currentWindow; + ENetListIterator currentCommand; + ENetIncomingCommand * startCommand = NULL; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength); + * currentData += fragmentLength; + if (fragmentLength > ENET_PROTOCOL_MAXIMUM_PACKET_SIZE || + * currentData < host -> receivedData || + * currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + channel = & peer -> channels [command -> header.channelID]; + startSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendFragment.startSequenceNumber); + startWindow = startSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + + if (startSequenceNumber < channel -> incomingReliableSequenceNumber) + startWindow += ENET_PEER_RELIABLE_WINDOWS; + + if (startWindow < currentWindow || startWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) + return 0; + + fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber); + fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount); + fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset); + totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength); + + if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT || + fragmentNumber >= fragmentCount || + totalLength > ENET_PROTOCOL_MAXIMUM_PACKET_SIZE || + fragmentOffset >= totalLength || + fragmentLength > totalLength - fragmentOffset) + return -1; + + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands)); + currentCommand != enet_list_end (& channel -> incomingReliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + if (startSequenceNumber >= channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + continue; + } + else + if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber <= startSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < startSequenceNumber) + break; + + if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_FRAGMENT || + totalLength != incomingCommand -> packet -> dataLength || + fragmentCount != incomingCommand -> fragmentCount) + return -1; + + startCommand = incomingCommand; + break; + } + } + + if (startCommand == NULL) + { + ENetProtocol hostCommand = * command; + ENetPacket * packet = enet_packet_create (NULL, totalLength, ENET_PACKET_FLAG_RELIABLE); + if (packet == NULL) + return -1; + + hostCommand.header.reliableSequenceNumber = startSequenceNumber; + + startCommand = enet_peer_queue_incoming_command (peer, & hostCommand, packet, fragmentCount); + if (startCommand == NULL) + return -1; + } + + if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0) + { + -- startCommand -> fragmentsRemaining; + + startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32)); + + if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength) + fragmentLength = startCommand -> packet -> dataLength - fragmentOffset; + + memcpy (startCommand -> packet -> data + fragmentOffset, + (enet_uint8 *) command + sizeof (ENetProtocolSendFragment), + fragmentLength); + + if (startCommand -> fragmentsRemaining <= 0) + enet_peer_dispatch_incoming_reliable_commands (peer, channel); + } + + return 0; +} + +static int +enet_protocol_handle_send_unreliable_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + enet_uint32 fragmentNumber, + fragmentCount, + fragmentOffset, + fragmentLength, + reliableSequenceNumber, + startSequenceNumber, + totalLength; + enet_uint16 reliableWindow, currentWindow; + ENetChannel * channel; + ENetListIterator currentCommand; + ENetIncomingCommand * startCommand = NULL; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength); + * currentData += fragmentLength; + if (fragmentLength > ENET_PROTOCOL_MAXIMUM_PACKET_SIZE || + * currentData < host -> receivedData || + * currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + channel = & peer -> channels [command -> header.channelID]; + reliableSequenceNumber = command -> header.reliableSequenceNumber; + startSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendFragment.startSequenceNumber); + + reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + + if (reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + reliableWindow += ENET_PEER_RELIABLE_WINDOWS; + + if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) + return 0; + + if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber && + startSequenceNumber <= channel -> incomingUnreliableSequenceNumber) + return 0; + + fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber); + fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount); + fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset); + totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength); + + if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT || + fragmentNumber >= fragmentCount || + totalLength > ENET_PROTOCOL_MAXIMUM_PACKET_SIZE || + fragmentOffset >= totalLength || + fragmentLength > totalLength - fragmentOffset) + return -1; + + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands)); + currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + continue; + } + else + if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber > reliableSequenceNumber) + continue; + + if (incomingCommand -> unreliableSequenceNumber <= startSequenceNumber) + { + if (incomingCommand -> unreliableSequenceNumber < startSequenceNumber) + break; + + if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT || + totalLength != incomingCommand -> packet -> dataLength || + fragmentCount != incomingCommand -> fragmentCount) + return -1; + + startCommand = incomingCommand; + break; + } + } + + if (startCommand == NULL) + { + ENetPacket * packet = enet_packet_create (NULL, totalLength, ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT); + if (packet == NULL) + return -1; + + startCommand = enet_peer_queue_incoming_command (peer, command, packet, fragmentCount); + if (startCommand == NULL) + return -1; + } + + if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0) + { + -- startCommand -> fragmentsRemaining; + + startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32)); + + if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength) + fragmentLength = startCommand -> packet -> dataLength - fragmentOffset; + + memcpy (startCommand -> packet -> data + fragmentOffset, + (enet_uint8 *) command + sizeof (ENetProtocolSendFragment), + fragmentLength); + + if (startCommand -> fragmentsRemaining <= 0) + enet_peer_dispatch_incoming_unreliable_commands (peer, channel); + } + + return 0; +} + +static int +enet_protocol_handle_ping (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + return -1; + + return 0; +} + +static int +enet_protocol_handle_bandwidth_limit (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + return -1; + + peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.incomingBandwidth); + peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.outgoingBandwidth); + + if (peer -> incomingBandwidth == 0 && host -> outgoingBandwidth == 0) + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + peer -> windowSize = (ENET_MIN (peer -> incomingBandwidth, host -> outgoingBandwidth) / + ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + return 0; +} + +static int +enet_protocol_handle_throttle_configure (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + return -1; + + peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleInterval); + peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleAcceleration); + peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleDeceleration); + + return 0; +} + +static int +enet_protocol_handle_disconnect (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE || peer -> state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT) + return 0; + + enet_peer_reset_queues (peer); + + if (peer -> state == ENET_PEER_STATE_CONNECTION_SUCCEEDED || peer -> state == ENET_PEER_STATE_DISCONNECTING) + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + else + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + { + if (peer -> state == ENET_PEER_STATE_CONNECTION_PENDING) host -> recalculateBandwidthLimits = 1; + + enet_peer_reset (peer); + } + else + if (command -> header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) + peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT; + else + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + + if (peer -> state != ENET_PEER_STATE_DISCONNECTED) + peer -> eventData = ENET_NET_TO_HOST_32 (command -> disconnect.data); + + return 0; +} + +static int +enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command) +{ + enet_uint32 roundTripTime, + receivedSentTime, + receivedReliableSequenceNumber; + ENetProtocolCommand commandNumber; + + if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE) + return 0; + + receivedSentTime = ENET_NET_TO_HOST_16 (command -> acknowledge.receivedSentTime); + receivedSentTime |= host -> serviceTime & 0xFFFF0000; + if ((receivedSentTime & 0x8000) > (host -> serviceTime & 0x8000)) + receivedSentTime -= 0x10000; + + if (ENET_TIME_LESS (host -> serviceTime, receivedSentTime)) + return 0; + + peer -> lastReceiveTime = host -> serviceTime; + peer -> earliestTimeout = 0; + + roundTripTime = ENET_TIME_DIFFERENCE (host -> serviceTime, receivedSentTime); + + enet_peer_throttle (peer, roundTripTime); + + peer -> roundTripTimeVariance -= peer -> roundTripTimeVariance / 4; + + if (roundTripTime >= peer -> roundTripTime) + { + peer -> roundTripTime += (roundTripTime - peer -> roundTripTime) / 8; + peer -> roundTripTimeVariance += (roundTripTime - peer -> roundTripTime) / 4; + } + else + { + peer -> roundTripTime -= (peer -> roundTripTime - roundTripTime) / 8; + peer -> roundTripTimeVariance += (peer -> roundTripTime - roundTripTime) / 4; + } + + if (peer -> roundTripTime < peer -> lowestRoundTripTime) + peer -> lowestRoundTripTime = peer -> roundTripTime; + + if (peer -> roundTripTimeVariance > peer -> highestRoundTripTimeVariance) + peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance; + + if (peer -> packetThrottleEpoch == 0 || + ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> packetThrottleEpoch) >= peer -> packetThrottleInterval) + { + peer -> lastRoundTripTime = peer -> lowestRoundTripTime; + peer -> lastRoundTripTimeVariance = peer -> highestRoundTripTimeVariance; + peer -> lowestRoundTripTime = peer -> roundTripTime; + peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance; + peer -> packetThrottleEpoch = host -> serviceTime; + } + + receivedReliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> acknowledge.receivedReliableSequenceNumber); + + commandNumber = enet_protocol_remove_sent_reliable_command (peer, receivedReliableSequenceNumber, command -> header.channelID); + + switch (peer -> state) + { + case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT: + if (commandNumber != ENET_PROTOCOL_COMMAND_VERIFY_CONNECT) + return -1; + + enet_protocol_notify_connect (host, peer, event); + break; + + case ENET_PEER_STATE_DISCONNECTING: + if (commandNumber != ENET_PROTOCOL_COMMAND_DISCONNECT) + return -1; + + enet_protocol_notify_disconnect (host, peer, event); + break; + + case ENET_PEER_STATE_DISCONNECT_LATER: + if (enet_list_empty (& peer -> outgoingReliableCommands) && + enet_list_empty (& peer -> outgoingUnreliableCommands) && + enet_list_empty (& peer -> sentReliableCommands)) + enet_peer_disconnect (peer, peer -> eventData); + break; + + default: + break; + } + + return 0; +} + +static int +enet_protocol_handle_verify_connect (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command) +{ + enet_uint32 mtu, windowSize; + size_t channelCount; + + if (peer -> state != ENET_PEER_STATE_CONNECTING) + return 0; + + channelCount = ENET_NET_TO_HOST_32 (command -> verifyConnect.channelCount); + + if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT || + ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleInterval) != peer -> packetThrottleInterval || + ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleAcceleration) != peer -> packetThrottleAcceleration || + ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleDeceleration) != peer -> packetThrottleDeceleration || + command -> verifyConnect.connectID != peer -> connectID) + { + peer -> eventData = 0; + + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + + return -1; + } + + enet_protocol_remove_sent_reliable_command (peer, 1, 0xFF); + + if (channelCount < peer -> channelCount) + peer -> channelCount = channelCount; + + peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> verifyConnect.outgoingPeerID); + peer -> incomingSessionID = command -> verifyConnect.incomingSessionID; + peer -> outgoingSessionID = command -> verifyConnect.outgoingSessionID; + + mtu = ENET_NET_TO_HOST_32 (command -> verifyConnect.mtu); + + if (mtu < ENET_PROTOCOL_MINIMUM_MTU) + mtu = ENET_PROTOCOL_MINIMUM_MTU; + else + if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) + mtu = ENET_PROTOCOL_MAXIMUM_MTU; + + if (mtu < peer -> mtu) + peer -> mtu = mtu; + + windowSize = ENET_NET_TO_HOST_32 (command -> verifyConnect.windowSize); + + if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + if (windowSize < peer -> windowSize) + peer -> windowSize = windowSize; + + peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.incomingBandwidth); + peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.outgoingBandwidth); + + enet_protocol_notify_connect (host, peer, event); + return 0; +} + +static int +enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) +{ + ENetProtocolHeader * header; + ENetProtocol * command; + ENetPeer * peer; + enet_uint8 * currentData; + size_t headerSize; + enet_uint16 peerID, flags; + enet_uint8 sessionID; + + if (host -> receivedDataLength < (size_t) & ((ENetProtocolHeader *) 0) -> sentTime) + return 0; + + header = (ENetProtocolHeader *) host -> receivedData; + + peerID = ENET_NET_TO_HOST_16 (header -> peerID); + sessionID = (peerID & ENET_PROTOCOL_HEADER_SESSION_MASK) >> ENET_PROTOCOL_HEADER_SESSION_SHIFT; + flags = peerID & ENET_PROTOCOL_HEADER_FLAG_MASK; + peerID &= ~ (ENET_PROTOCOL_HEADER_FLAG_MASK | ENET_PROTOCOL_HEADER_SESSION_MASK); + + headerSize = (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME ? sizeof (ENetProtocolHeader) : (size_t) & ((ENetProtocolHeader *) 0) -> sentTime); + if (host -> checksum != NULL) + headerSize += sizeof (enet_uint32); + + if (peerID == ENET_PROTOCOL_MAXIMUM_PEER_ID) + peer = NULL; + else + if (peerID >= host -> peerCount) + return 0; + else + { + peer = & host -> peers [peerID]; + + if (peer -> state == ENET_PEER_STATE_DISCONNECTED || + peer -> state == ENET_PEER_STATE_ZOMBIE || + ((host -> receivedAddress.host != peer -> address.host || + host -> receivedAddress.port != peer -> address.port) && + peer -> address.host != ENET_HOST_BROADCAST) || + (peer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID && + sessionID != peer -> incomingSessionID)) + return 0; + } + + if (flags & ENET_PROTOCOL_HEADER_FLAG_COMPRESSED) + { + size_t originalSize; + if (host -> compressor.context == NULL || host -> compressor.decompress == NULL) + return 0; + + originalSize = host -> compressor.decompress (host -> compressor.context, + host -> receivedData + headerSize, + host -> receivedDataLength - headerSize, + host -> packetData [1] + headerSize, + sizeof (host -> packetData [1]) - headerSize); + if (originalSize <= 0 || originalSize > sizeof (host -> packetData [1]) - headerSize) + return 0; + + memcpy (host -> packetData [1], header, headerSize); + host -> receivedData = host -> packetData [1]; + host -> receivedDataLength = headerSize + originalSize; + } + + if (host -> checksum != NULL) + { + enet_uint32 * checksum = (enet_uint32 *) & host -> receivedData [headerSize - sizeof (enet_uint32)], + desiredChecksum = * checksum; + ENetBuffer buffer; + + * checksum = peer != NULL ? peer -> connectID : 0; + + buffer.data = host -> receivedData; + buffer.dataLength = host -> receivedDataLength; + + if (host -> checksum (& buffer, 1) != desiredChecksum) + return 0; + } + + if (peer != NULL) + { + peer -> address.host = host -> receivedAddress.host; + peer -> address.port = host -> receivedAddress.port; + peer -> incomingDataTotal += host -> receivedDataLength; + } + + currentData = host -> receivedData + headerSize; + + while (currentData < & host -> receivedData [host -> receivedDataLength]) + { + enet_uint8 commandNumber; + size_t commandSize; + + command = (ENetProtocol *) currentData; + + if (currentData + sizeof (ENetProtocolCommandHeader) > & host -> receivedData [host -> receivedDataLength]) + break; + + commandNumber = command -> header.command & ENET_PROTOCOL_COMMAND_MASK; + if (commandNumber >= ENET_PROTOCOL_COMMAND_COUNT) + break; + + commandSize = commandSizes [commandNumber]; + if (commandSize == 0 || currentData + commandSize > & host -> receivedData [host -> receivedDataLength]) + break; + + currentData += commandSize; + + if (peer == NULL && commandNumber != ENET_PROTOCOL_COMMAND_CONNECT) + break; + + command -> header.reliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> header.reliableSequenceNumber); + + switch (commandNumber) + { + case ENET_PROTOCOL_COMMAND_ACKNOWLEDGE: + if (enet_protocol_handle_acknowledge (host, event, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_CONNECT: + if (peer != NULL) + goto commandError; + peer = enet_protocol_handle_connect (host, header, command); + if (peer == NULL) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_VERIFY_CONNECT: + if (enet_protocol_handle_verify_connect (host, event, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_DISCONNECT: + if (enet_protocol_handle_disconnect (host, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_PING: + if (enet_protocol_handle_ping (host, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: + if (enet_protocol_handle_send_reliable (host, peer, command, & currentData)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: + if (enet_protocol_handle_send_unreliable (host, peer, command, & currentData)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: + if (enet_protocol_handle_send_unsequenced (host, peer, command, & currentData)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: + if (enet_protocol_handle_send_fragment (host, peer, command, & currentData)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT: + if (enet_protocol_handle_bandwidth_limit (host, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE: + if (enet_protocol_handle_throttle_configure (host, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT: + if (enet_protocol_handle_send_unreliable_fragment (host, peer, command, & currentData)) + goto commandError; + break; + + default: + goto commandError; + } + + if (peer != NULL && + (command -> header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) != 0) + { + enet_uint16 sentTime; + + if (! (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME)) + break; + + sentTime = ENET_NET_TO_HOST_16 (header -> sentTime); + + switch (peer -> state) + { + case ENET_PEER_STATE_DISCONNECTING: + case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT: + case ENET_PEER_STATE_DISCONNECTED: + case ENET_PEER_STATE_ZOMBIE: + break; + + case ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT: + if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT) + enet_peer_queue_acknowledgement (peer, command, sentTime); + break; + + default: + enet_peer_queue_acknowledgement (peer, command, sentTime); + break; + } + } + } + +commandError: + if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) + return 1; + + return 0; +} + +static int +enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event) +{ + for (;;) + { + int receivedLength; + ENetBuffer buffer; + + buffer.data = host -> packetData [0]; + buffer.dataLength = sizeof (host -> packetData [0]); + + receivedLength = enet_socket_receive (host -> socket, + & host -> receivedAddress, + & buffer, + 1); + + if (receivedLength < 0) + return -1; + + if (receivedLength == 0) + return 0; + + host -> receivedData = host -> packetData [0]; + host -> receivedDataLength = receivedLength; + + host -> totalReceivedData += receivedLength; + host -> totalReceivedPackets ++; + + if (host -> intercept != NULL) + { + switch (host -> intercept (host, event)) + { + case 1: + if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) + return 1; + + continue; + + case -1: + return -1; + + default: + break; + } + } + + switch (enet_protocol_handle_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: + return -1; + + default: + break; + } + } + + return -1; +} + +static void +enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer) +{ + ENetProtocol * command = & host -> commands [host -> commandCount]; + ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; + ENetAcknowledgement * acknowledgement; + ENetListIterator currentAcknowledgement; + + currentAcknowledgement = enet_list_begin (& peer -> acknowledgements); + + while (currentAcknowledgement != enet_list_end (& peer -> acknowledgements)) + { + if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] || + buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || + peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge)) + { + host -> continueSending = 1; + + break; + } + + acknowledgement = (ENetAcknowledgement *) currentAcknowledgement; + + currentAcknowledgement = enet_list_next (currentAcknowledgement); + + buffer -> data = command; + buffer -> dataLength = sizeof (ENetProtocolAcknowledge); + + host -> packetSize += buffer -> dataLength; + + command -> header.command = ENET_PROTOCOL_COMMAND_ACKNOWLEDGE; + command -> header.channelID = acknowledgement -> command.header.channelID; + command -> acknowledge.receivedReliableSequenceNumber = ENET_HOST_TO_NET_16 (acknowledgement -> command.header.reliableSequenceNumber); + command -> acknowledge.receivedSentTime = ENET_HOST_TO_NET_16 (acknowledgement -> sentTime); + + if ((acknowledgement -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT) + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + + enet_list_remove (& acknowledgement -> acknowledgementList); + enet_free (acknowledgement); + + ++ command; + ++ buffer; + } + + host -> commandCount = command - host -> commands; + host -> bufferCount = buffer - host -> buffers; +} + +static void +enet_protocol_send_unreliable_outgoing_commands (ENetHost * host, ENetPeer * peer) +{ + ENetProtocol * command = & host -> commands [host -> commandCount]; + ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; + ENetOutgoingCommand * outgoingCommand; + ENetListIterator currentCommand; + + currentCommand = enet_list_begin (& peer -> outgoingUnreliableCommands); + + while (currentCommand != enet_list_end (& peer -> outgoingUnreliableCommands)) + { + size_t commandSize; + + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + commandSize = commandSizes [outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK]; + + if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] || + buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || + peer -> mtu - host -> packetSize < commandSize || + (outgoingCommand -> packet != NULL && + peer -> mtu - host -> packetSize < commandSize + outgoingCommand -> fragmentLength)) + { + host -> continueSending = 1; + + break; + } + + currentCommand = enet_list_next (currentCommand); + + if (outgoingCommand -> packet != NULL && outgoingCommand -> fragmentOffset == 0) + { + peer -> packetThrottleCounter += ENET_PEER_PACKET_THROTTLE_COUNTER; + peer -> packetThrottleCounter %= ENET_PEER_PACKET_THROTTLE_SCALE; + + if (peer -> packetThrottleCounter > peer -> packetThrottle) + { + enet_uint16 reliableSequenceNumber = outgoingCommand -> reliableSequenceNumber, + unreliableSequenceNumber = outgoingCommand -> unreliableSequenceNumber; + for (;;) + { + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (outgoingCommand -> packet); + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + enet_free (outgoingCommand); + + if (currentCommand == enet_list_end (& peer -> outgoingUnreliableCommands)) + break; + + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + if (outgoingCommand -> reliableSequenceNumber != reliableSequenceNumber || + outgoingCommand -> unreliableSequenceNumber != unreliableSequenceNumber) + break; + + currentCommand = enet_list_next (currentCommand); + } + + continue; + } + } + + buffer -> data = command; + buffer -> dataLength = commandSize; + + host -> packetSize += buffer -> dataLength; + + * command = outgoingCommand -> command; + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + + if (outgoingCommand -> packet != NULL) + { + ++ buffer; + + buffer -> data = outgoingCommand -> packet -> data + outgoingCommand -> fragmentOffset; + buffer -> dataLength = outgoingCommand -> fragmentLength; + + host -> packetSize += buffer -> dataLength; + + enet_list_insert (enet_list_end (& peer -> sentUnreliableCommands), outgoingCommand); + } + else + enet_free (outgoingCommand); + + ++ command; + ++ buffer; + } + + host -> commandCount = command - host -> commands; + host -> bufferCount = buffer - host -> buffers; + + if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER && + enet_list_empty (& peer -> outgoingReliableCommands) && + enet_list_empty (& peer -> outgoingUnreliableCommands) && + enet_list_empty (& peer -> sentReliableCommands)) + enet_peer_disconnect (peer, peer -> eventData); +} + +static int +enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event) +{ + ENetOutgoingCommand * outgoingCommand; + ENetListIterator currentCommand, insertPosition; + + currentCommand = enet_list_begin (& peer -> sentReliableCommands); + insertPosition = enet_list_begin (& peer -> outgoingReliableCommands); + + while (currentCommand != enet_list_end (& peer -> sentReliableCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + currentCommand = enet_list_next (currentCommand); + + if (ENET_TIME_DIFFERENCE (host -> serviceTime, outgoingCommand -> sentTime) < outgoingCommand -> roundTripTimeout) + continue; + + if (peer -> earliestTimeout == 0 || + ENET_TIME_LESS (outgoingCommand -> sentTime, peer -> earliestTimeout)) + peer -> earliestTimeout = outgoingCommand -> sentTime; + + if (peer -> earliestTimeout != 0 && + (ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMaximum || + (outgoingCommand -> roundTripTimeout >= outgoingCommand -> roundTripTimeoutLimit && + ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMinimum))) + { + enet_protocol_notify_disconnect (host, peer, event); + + return 1; + } + + if (outgoingCommand -> packet != NULL) + peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength; + + ++ peer -> packetsLost; + + outgoingCommand -> roundTripTimeout *= 2; + + enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList)); + + if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) && + ! enet_list_empty (& peer -> sentReliableCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout; + } + } + + return 0; +} + +static int +enet_protocol_send_reliable_outgoing_commands (ENetHost * host, ENetPeer * peer) +{ + ENetProtocol * command = & host -> commands [host -> commandCount]; + ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; + ENetOutgoingCommand * outgoingCommand; + ENetListIterator currentCommand; + ENetChannel *channel; + enet_uint16 reliableWindow; + size_t commandSize; + int windowExceeded = 0, windowWrap = 0, canPing = 1; + + currentCommand = enet_list_begin (& peer -> outgoingReliableCommands); + + while (currentCommand != enet_list_end (& peer -> outgoingReliableCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + channel = outgoingCommand -> command.header.channelID < peer -> channelCount ? & peer -> channels [outgoingCommand -> command.header.channelID] : NULL; + reliableWindow = outgoingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + if (channel != NULL) + { + if (! windowWrap && + outgoingCommand -> sendAttempts < 1 && + ! (outgoingCommand -> reliableSequenceNumber % ENET_PEER_RELIABLE_WINDOW_SIZE) && + (channel -> reliableWindows [(reliableWindow + ENET_PEER_RELIABLE_WINDOWS - 1) % ENET_PEER_RELIABLE_WINDOWS] >= ENET_PEER_RELIABLE_WINDOW_SIZE || + channel -> usedReliableWindows & ((((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) << reliableWindow) | + (((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) >> (ENET_PEER_RELIABLE_WINDOW_SIZE - reliableWindow))))) + windowWrap = 1; + if (windowWrap) + { + currentCommand = enet_list_next (currentCommand); + + continue; + } + } + + if (outgoingCommand -> packet != NULL) + { + if (! windowExceeded) + { + enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE; + + if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu)) + windowExceeded = 1; + } + if (windowExceeded) + { + currentCommand = enet_list_next (currentCommand); + + continue; + } + } + + canPing = 0; + + commandSize = commandSizes [outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK]; + if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] || + buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || + peer -> mtu - host -> packetSize < commandSize || + (outgoingCommand -> packet != NULL && + (enet_uint16) (peer -> mtu - host -> packetSize) < (enet_uint16) (commandSize + outgoingCommand -> fragmentLength))) + { + host -> continueSending = 1; + + break; + } + + currentCommand = enet_list_next (currentCommand); + + if (channel != NULL && outgoingCommand -> sendAttempts < 1) + { + channel -> usedReliableWindows |= 1 << reliableWindow; + ++ channel -> reliableWindows [reliableWindow]; + } + + ++ outgoingCommand -> sendAttempts; + + if (outgoingCommand -> roundTripTimeout == 0) + { + outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance; + outgoingCommand -> roundTripTimeoutLimit = peer -> timeoutLimit * outgoingCommand -> roundTripTimeout; + } + + if (enet_list_empty (& peer -> sentReliableCommands)) + peer -> nextTimeout = host -> serviceTime + outgoingCommand -> roundTripTimeout; + + enet_list_insert (enet_list_end (& peer -> sentReliableCommands), + enet_list_remove (& outgoingCommand -> outgoingCommandList)); + + outgoingCommand -> sentTime = host -> serviceTime; + + buffer -> data = command; + buffer -> dataLength = commandSize; + + host -> packetSize += buffer -> dataLength; + host -> headerFlags |= ENET_PROTOCOL_HEADER_FLAG_SENT_TIME; + + * command = outgoingCommand -> command; + + if (outgoingCommand -> packet != NULL) + { + ++ buffer; + + buffer -> data = outgoingCommand -> packet -> data + outgoingCommand -> fragmentOffset; + buffer -> dataLength = outgoingCommand -> fragmentLength; + + host -> packetSize += outgoingCommand -> fragmentLength; + + peer -> reliableDataInTransit += outgoingCommand -> fragmentLength; + } + + ++ peer -> packetsSent; + + ++ command; + ++ buffer; + } + + host -> commandCount = command - host -> commands; + host -> bufferCount = buffer - host -> buffers; + + return canPing; +} + +static int +enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int checkForTimeouts) +{ + enet_uint8 headerData [sizeof (ENetProtocolHeader) + sizeof (enet_uint32)]; + ENetProtocolHeader * header = (ENetProtocolHeader *) headerData; + ENetPeer * currentPeer; + int sentLength; + size_t shouldCompress = 0; + + host -> continueSending = 1; + + while (host -> continueSending) + for (host -> continueSending = 0, + currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED || + currentPeer -> state == ENET_PEER_STATE_ZOMBIE) + continue; + + host -> headerFlags = 0; + host -> commandCount = 0; + host -> bufferCount = 1; + host -> packetSize = sizeof (ENetProtocolHeader); + + if (! enet_list_empty (& currentPeer -> acknowledgements)) + enet_protocol_send_acknowledgements (host, currentPeer); + + if (checkForTimeouts != 0 && + ! enet_list_empty (& currentPeer -> sentReliableCommands) && + ENET_TIME_GREATER_EQUAL (host -> serviceTime, currentPeer -> nextTimeout) && + enet_protocol_check_timeouts (host, currentPeer, event) == 1) + { + if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) + return 1; + else + continue; + } + + if ((enet_list_empty (& currentPeer -> outgoingReliableCommands) || + enet_protocol_send_reliable_outgoing_commands (host, currentPeer)) && + enet_list_empty (& currentPeer -> sentReliableCommands) && + ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> lastReceiveTime) >= currentPeer -> pingInterval && + currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing)) + { + enet_peer_ping (currentPeer); + enet_protocol_send_reliable_outgoing_commands (host, currentPeer); + } + + if (! enet_list_empty (& currentPeer -> outgoingUnreliableCommands)) + enet_protocol_send_unreliable_outgoing_commands (host, currentPeer); + + if (host -> commandCount == 0) + continue; + + if (currentPeer -> packetLossEpoch == 0) + currentPeer -> packetLossEpoch = host -> serviceTime; + else + if (ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> packetLossEpoch) >= ENET_PEER_PACKET_LOSS_INTERVAL && + currentPeer -> packetsSent > 0) + { + enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent; + +#ifdef ENET_DEBUG +#ifdef WIN32 + printf ( +#else + fprintf (stderr, +#endif + "peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u/%u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingReliableCommands), enet_list_size (& currentPeer -> outgoingUnreliableCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands) : 0); +#endif + + currentPeer -> packetLossVariance -= currentPeer -> packetLossVariance / 4; + + if (packetLoss >= currentPeer -> packetLoss) + { + currentPeer -> packetLoss += (packetLoss - currentPeer -> packetLoss) / 8; + currentPeer -> packetLossVariance += (packetLoss - currentPeer -> packetLoss) / 4; + } + else + { + currentPeer -> packetLoss -= (currentPeer -> packetLoss - packetLoss) / 8; + currentPeer -> packetLossVariance += (currentPeer -> packetLoss - packetLoss) / 4; + } + + currentPeer -> packetLossEpoch = host -> serviceTime; + currentPeer -> packetsSent = 0; + currentPeer -> packetsLost = 0; + } + + host -> buffers -> data = headerData; + if (host -> headerFlags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME) + { + header -> sentTime = ENET_HOST_TO_NET_16 (host -> serviceTime & 0xFFFF); + + host -> buffers -> dataLength = sizeof (ENetProtocolHeader); + } + else + host -> buffers -> dataLength = (size_t) & ((ENetProtocolHeader *) 0) -> sentTime; + + shouldCompress = 0; + if (host -> compressor.context != NULL && host -> compressor.compress != NULL) + { + size_t originalSize = host -> packetSize - sizeof(ENetProtocolHeader), + compressedSize = host -> compressor.compress (host -> compressor.context, + & host -> buffers [1], host -> bufferCount - 1, + originalSize, + host -> packetData [1], + originalSize); + if (compressedSize > 0 && compressedSize < originalSize) + { + host -> headerFlags |= ENET_PROTOCOL_HEADER_FLAG_COMPRESSED; + shouldCompress = compressedSize; +#ifdef ENET_DEBUG_COMPRESS +#ifdef WIN32 + printf ( +#else + fprintf (stderr, +#endif + "peer %u: compressed %u -> %u (%u%%)\n", currentPeer -> incomingPeerID, originalSize, compressedSize, (compressedSize * 100) / originalSize); +#endif + } + } + + if (currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID) + host -> headerFlags |= currentPeer -> outgoingSessionID << ENET_PROTOCOL_HEADER_SESSION_SHIFT; + header -> peerID = ENET_HOST_TO_NET_16 (currentPeer -> outgoingPeerID | host -> headerFlags); + if (host -> checksum != NULL) + { + enet_uint32 * checksum = (enet_uint32 *) & headerData [host -> buffers -> dataLength]; + * checksum = currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID ? currentPeer -> connectID : 0; + host -> buffers -> dataLength += sizeof (enet_uint32); + * checksum = host -> checksum (host -> buffers, host -> bufferCount); + } + + if (shouldCompress > 0) + { + host -> buffers [1].data = host -> packetData [1]; + host -> buffers [1].dataLength = shouldCompress; + host -> bufferCount = 2; + } + + currentPeer -> lastSendTime = host -> serviceTime; + + sentLength = enet_socket_send (host -> socket, & currentPeer -> address, host -> buffers, host -> bufferCount); + + enet_protocol_remove_sent_unreliable_commands (currentPeer); + + if (sentLength < 0) + return -1; + + host -> totalSentData += sentLength; + host -> totalSentPackets ++; + } + + return 0; +} + +/** Sends any queued packets on the host specified to its designated peers. + + @param host host to flush + @remarks this function need only be used in circumstances where one wishes to send queued packets earlier than in a call to enet_host_service(). + @ingroup host +*/ +void +enet_host_flush (ENetHost * host) +{ + host -> serviceTime = enet_time_get (); + + enet_protocol_send_outgoing_commands (host, NULL, 0); +} + +/** Checks for any queued events on the host and dispatches one if available. + + @param host host to check for events + @param event an event structure where event details will be placed if available + @retval > 0 if an event was dispatched + @retval 0 if no events are available + @retval < 0 on failure + @ingroup host +*/ +int +enet_host_check_events (ENetHost * host, ENetEvent * event) +{ + if (event == NULL) return -1; + + event -> type = ENET_EVENT_TYPE_NONE; + event -> peer = NULL; + event -> packet = NULL; + + return enet_protocol_dispatch_incoming_commands (host, event); +} + +/** Waits for events on the host specified and shuttles packets between + the host and its peers. + + @param host host to service + @param event an event structure where event details will be placed if one occurs + if event == NULL then no events will be delivered + @param timeout number of milliseconds that ENet should wait for events + @retval > 0 if an event occurred within the specified time limit + @retval 0 if no event occurred + @retval < 0 on failure + @remarks enet_host_service should be called fairly regularly for adequate performance + @ingroup host +*/ +int +enet_host_service (ENetHost * host, ENetEvent * event, enet_uint32 timeout) +{ + enet_uint32 waitCondition; + + if (event != NULL) + { + event -> type = ENET_EVENT_TYPE_NONE; + event -> peer = NULL; + event -> packet = NULL; + + switch (enet_protocol_dispatch_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: + perror ("Error dispatching incoming packets"); + + return -1; + + default: + break; + } + } + + host -> serviceTime = enet_time_get (); + + timeout += host -> serviceTime; + + do + { + if (ENET_TIME_DIFFERENCE (host -> serviceTime, host -> bandwidthThrottleEpoch) >= ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL) + enet_host_bandwidth_throttle (host); + + switch (enet_protocol_send_outgoing_commands (host, event, 1)) + { + case 1: + return 1; + + case -1: + perror ("Error sending outgoing packets"); + + return -1; + + default: + break; + } + + switch (enet_protocol_receive_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: + perror ("Error receiving incoming packets"); + + return -1; + + default: + break; + } + + switch (enet_protocol_send_outgoing_commands (host, event, 1)) + { + case 1: + return 1; + + case -1: + perror ("Error sending outgoing packets"); + + return -1; + + default: + break; + } + + if (event != NULL) + { + switch (enet_protocol_dispatch_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: + perror ("Error dispatching incoming packets"); + + return -1; + + default: + break; + } + } + + host -> serviceTime = enet_time_get (); + + if (ENET_TIME_GREATER_EQUAL (host -> serviceTime, timeout)) + return 0; + + waitCondition = ENET_SOCKET_WAIT_RECEIVE; + + if (enet_socket_wait (host -> socket, & waitCondition, ENET_TIME_DIFFERENCE (timeout, host -> serviceTime)) != 0) + return -1; + + host -> serviceTime = enet_time_get (); + } while (waitCondition == ENET_SOCKET_WAIT_RECEIVE); + + return 0; +} + diff --git a/examples/ThirdPartyLibs/enet/unix.c b/examples/ThirdPartyLibs/enet/unix.c new file mode 100644 index 000000000..d425b4b8a --- /dev/null +++ b/examples/ThirdPartyLibs/enet/unix.c @@ -0,0 +1,475 @@ +/** + @file unix.c + @brief ENet Unix system specific functions +*/ +#ifndef WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +#ifdef __APPLE__ +#ifdef HAS_POLL +#undef HAS_POLL +#endif +#ifndef HAS_FCNTL +#define HAS_FCNTL 1 +#endif +#ifndef HAS_INET_PTON +#define HAS_INET_PTON 1 +#endif +#ifndef HAS_INET_NTOP +#define HAS_INET_NTOP 1 +#endif +#ifndef HAS_MSGHDR_FLAGS +#define HAS_MSGHDR_FLAGS 1 +#endif +#ifndef HAS_SOCKLEN_T +#define HAS_SOCKLEN_T 1 +#endif +#endif + +#ifdef HAS_FCNTL +#include +#endif + +#ifdef HAS_POLL +#include +#endif + +#ifndef HAS_SOCKLEN_T +typedef int socklen_t; +#endif + +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +static enet_uint32 timeBase = 0; + +int +enet_initialize (void) +{ + return 0; +} + +void +enet_deinitialize (void) +{ +} + +enet_uint32 +enet_time_get (void) +{ + struct timeval timeVal; + + gettimeofday (& timeVal, NULL); + + return timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - timeBase; +} + +void +enet_time_set (enet_uint32 newTimeBase) +{ + struct timeval timeVal; + + gettimeofday (& timeVal, NULL); + + timeBase = timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - newTimeBase; +} + +int +enet_address_set_host (ENetAddress * address, const char * name) +{ + struct hostent * hostEntry = NULL; +#ifdef HAS_GETHOSTBYNAME_R + struct hostent hostData; + char buffer [2048]; + int errnum; + +#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); +#else + hostEntry = gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & errnum); +#endif +#else + hostEntry = gethostbyname (name); +#endif + + if (hostEntry == NULL || + hostEntry -> h_addrtype != AF_INET) + { +#ifdef HAS_INET_PTON + if (! inet_pton (AF_INET, name, & address -> host)) +#else + if (! inet_aton (name, (struct in_addr *) & address -> host)) +#endif + return -1; + return 0; + } + + address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0]; + + return 0; +} + +int +enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength) +{ +#ifdef HAS_INET_NTOP + if (inet_ntop (AF_INET, & address -> host, name, nameLength) == NULL) +#else + char * addr = inet_ntoa (* (struct in_addr *) & address -> host); + if (addr != NULL) + strncpy (name, addr, nameLength); + else +#endif + return -1; + return 0; +} + +int +enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength) +{ + struct in_addr in; + struct hostent * hostEntry = NULL; +#ifdef HAS_GETHOSTBYADDR_R + struct hostent hostData; + char buffer [2048]; + int errnum; + + in.s_addr = address -> host; + +#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); +#else + hostEntry = gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & errnum); +#endif +#else + in.s_addr = address -> host; + + hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET); +#endif + + if (hostEntry == NULL) + return enet_address_get_host_ip (address, name, nameLength); + + strncpy (name, hostEntry -> h_name, nameLength); + + return 0; +} + +int +enet_socket_bind (ENetSocket socket, const ENetAddress * address) +{ + struct sockaddr_in sin; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + + if (address != NULL) + { + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + } + else + { + sin.sin_port = 0; + sin.sin_addr.s_addr = INADDR_ANY; + } + + return bind (socket, + (struct sockaddr *) & sin, + sizeof (struct sockaddr_in)); +} + +int +enet_socket_listen (ENetSocket socket, int backlog) +{ + return listen (socket, backlog < 0 ? SOMAXCONN : backlog); +} + +ENetSocket +enet_socket_create (ENetSocketType type) +{ + return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); +} + +int +enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value) +{ + int result = -1; + switch (option) + { + case ENET_SOCKOPT_NONBLOCK: +#ifdef HAS_FCNTL + result = fcntl (socket, F_SETFL, O_NONBLOCK | fcntl (socket, F_GETFL)); +#else + result = ioctl (socket, FIONBIO, & value); +#endif + break; + + case ENET_SOCKOPT_BROADCAST: + result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_REUSEADDR: + result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_RCVBUF: + result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_SNDBUF: + result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_RCVTIMEO: + result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_SNDTIMEO: + result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & value, sizeof (int)); + break; + + default: + break; + } + return result == -1 ? -1 : 0; +} + +int +enet_socket_connect (ENetSocket socket, const ENetAddress * address) +{ + struct sockaddr_in sin; + int result; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + + result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in)); + if (result == -1 && errno == EINPROGRESS) + return 0; + + return result; +} + +ENetSocket +enet_socket_accept (ENetSocket socket, ENetAddress * address) +{ + int result; + struct sockaddr_in sin; + socklen_t sinLength = sizeof (struct sockaddr_in); + + result = accept (socket, + address != NULL ? (struct sockaddr *) & sin : NULL, + address != NULL ? & sinLength : NULL); + + if (result == -1) + return ENET_SOCKET_NULL; + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return result; +} + +int +enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how) +{ + return shutdown (socket, (int) how); +} + +void +enet_socket_destroy (ENetSocket socket) +{ + if (socket != -1) + close (socket); +} + +int +enet_socket_send (ENetSocket socket, + const ENetAddress * address, + const ENetBuffer * buffers, + size_t bufferCount) +{ + struct msghdr msgHdr; + struct sockaddr_in sin; + int sentLength; + + memset (& msgHdr, 0, sizeof (struct msghdr)); + + if (address != NULL) + { + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + + msgHdr.msg_name = & sin; + msgHdr.msg_namelen = sizeof (struct sockaddr_in); + } + + msgHdr.msg_iov = (struct iovec *) buffers; + msgHdr.msg_iovlen = bufferCount; + + sentLength = sendmsg (socket, & msgHdr, MSG_NOSIGNAL); + + if (sentLength == -1) + { + if (errno == EWOULDBLOCK) + return 0; + + return -1; + } + + return sentLength; +} + +int +enet_socket_receive (ENetSocket socket, + ENetAddress * address, + ENetBuffer * buffers, + size_t bufferCount) +{ + struct msghdr msgHdr; + struct sockaddr_in sin; + int recvLength; + + memset (& msgHdr, 0, sizeof (struct msghdr)); + + if (address != NULL) + { + msgHdr.msg_name = & sin; + msgHdr.msg_namelen = sizeof (struct sockaddr_in); + } + + msgHdr.msg_iov = (struct iovec *) buffers; + msgHdr.msg_iovlen = bufferCount; + + recvLength = recvmsg (socket, & msgHdr, MSG_NOSIGNAL); + + if (recvLength == -1) + { + if (errno == EWOULDBLOCK) + return 0; + + return -1; + } + +#ifdef HAS_MSGHDR_FLAGS + if (msgHdr.msg_flags & MSG_TRUNC) + return -1; +#endif + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return recvLength; +} + +int +enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout) +{ + struct timeval timeVal; + + timeVal.tv_sec = timeout / 1000; + timeVal.tv_usec = (timeout % 1000) * 1000; + + return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal); +} + +int +enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout) +{ +#ifdef HAS_POLL + struct pollfd pollSocket; + int pollCount; + + pollSocket.fd = socket; + pollSocket.events = 0; + + if (* condition & ENET_SOCKET_WAIT_SEND) + pollSocket.events |= POLLOUT; + + if (* condition & ENET_SOCKET_WAIT_RECEIVE) + pollSocket.events |= POLLIN; + + pollCount = poll (& pollSocket, 1, timeout); + + if (pollCount < 0) + return -1; + + * condition = ENET_SOCKET_WAIT_NONE; + + if (pollCount == 0) + return 0; + + if (pollSocket.revents & POLLOUT) + * condition |= ENET_SOCKET_WAIT_SEND; + + if (pollSocket.revents & POLLIN) + * condition |= ENET_SOCKET_WAIT_RECEIVE; + + return 0; +#else + fd_set readSet, writeSet; + struct timeval timeVal; + int selectCount; + + timeVal.tv_sec = timeout / 1000; + timeVal.tv_usec = (timeout % 1000) * 1000; + + FD_ZERO (& readSet); + FD_ZERO (& writeSet); + + if (* condition & ENET_SOCKET_WAIT_SEND) + FD_SET (socket, & writeSet); + + if (* condition & ENET_SOCKET_WAIT_RECEIVE) + FD_SET (socket, & readSet); + + selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal); + + if (selectCount < 0) + return -1; + + * condition = ENET_SOCKET_WAIT_NONE; + + if (selectCount == 0) + return 0; + + if (FD_ISSET (socket, & writeSet)) + * condition |= ENET_SOCKET_WAIT_SEND; + + if (FD_ISSET (socket, & readSet)) + * condition |= ENET_SOCKET_WAIT_RECEIVE; + + return 0; +#endif +} + +#endif + diff --git a/examples/ThirdPartyLibs/enet/win32.c b/examples/ThirdPartyLibs/enet/win32.c new file mode 100644 index 000000000..dcc0791bd --- /dev/null +++ b/examples/ThirdPartyLibs/enet/win32.c @@ -0,0 +1,368 @@ +/** + @file win32.c + @brief ENet Win32 system specific functions +*/ +#ifdef WIN32 + +#include +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +static enet_uint32 timeBase = 0; + +int +enet_initialize (void) +{ + WORD versionRequested = MAKEWORD (1, 1); + WSADATA wsaData; + + if (WSAStartup (versionRequested, & wsaData)) + return -1; + + if (LOBYTE (wsaData.wVersion) != 1|| + HIBYTE (wsaData.wVersion) != 1) + { + WSACleanup (); + + return -1; + } + + timeBeginPeriod (1); + + return 0; +} + +void +enet_deinitialize (void) +{ + timeEndPeriod (1); + + WSACleanup (); +} + +enet_uint32 +enet_time_get (void) +{ + return (enet_uint32) timeGetTime () - timeBase; +} + +void +enet_time_set (enet_uint32 newTimeBase) +{ + timeBase = (enet_uint32) timeGetTime () - newTimeBase; +} + +int +enet_address_set_host (ENetAddress * address, const char * name) +{ + struct hostent * hostEntry; + + hostEntry = gethostbyname (name); + if (hostEntry == NULL || + hostEntry -> h_addrtype != AF_INET) + { + unsigned long host = inet_addr (name); + if (host == INADDR_NONE) + return -1; + address -> host = host; + return 0; + } + + address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0]; + + return 0; +} + +int +enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength) +{ + char * addr = inet_ntoa (* (struct in_addr *) & address -> host); + if (addr == NULL) + return -1; + strncpy (name, addr, nameLength); + return 0; +} + +int +enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength) +{ + struct in_addr in; + struct hostent * hostEntry; + + in.s_addr = address -> host; + + hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET); + if (hostEntry == NULL) + return enet_address_get_host_ip (address, name, nameLength); + + strncpy (name, hostEntry -> h_name, nameLength); + + return 0; +} + +int +enet_socket_bind (ENetSocket socket, const ENetAddress * address) +{ + struct sockaddr_in sin; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + + if (address != NULL) + { + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + } + else + { + sin.sin_port = 0; + sin.sin_addr.s_addr = INADDR_ANY; + } + + return bind (socket, + (struct sockaddr *) & sin, + sizeof (struct sockaddr_in)) == SOCKET_ERROR ? -1 : 0; +} + +int +enet_socket_listen (ENetSocket socket, int backlog) +{ + return listen (socket, backlog < 0 ? SOMAXCONN : backlog) == SOCKET_ERROR ? -1 : 0; +} + +ENetSocket +enet_socket_create (ENetSocketType type) +{ + return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); +} + +int +enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value) +{ + int result = SOCKET_ERROR; + switch (option) + { + case ENET_SOCKOPT_NONBLOCK: + { + u_long nonBlocking = (u_long) value; + result = ioctlsocket (socket, FIONBIO, & nonBlocking); + break; + } + + case ENET_SOCKOPT_BROADCAST: + result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_REUSEADDR: + result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_RCVBUF: + result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_SNDBUF: + result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_RCVTIMEO: + result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_SNDTIMEO: + result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & value, sizeof (int)); + break; + + default: + break; + } + return result == SOCKET_ERROR ? -1 : 0; +} + +int +enet_socket_connect (ENetSocket socket, const ENetAddress * address) +{ + struct sockaddr_in sin; + int result; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + + result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in)); + if (result == SOCKET_ERROR && WSAGetLastError () != WSAEWOULDBLOCK) + return -1; + + return 0; +} + +ENetSocket +enet_socket_accept (ENetSocket socket, ENetAddress * address) +{ + SOCKET result; + struct sockaddr_in sin; + int sinLength = sizeof (struct sockaddr_in); + + result = accept (socket, + address != NULL ? (struct sockaddr *) & sin : NULL, + address != NULL ? & sinLength : NULL); + + if (result == INVALID_SOCKET) + return ENET_SOCKET_NULL; + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return result; +} + +int +enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how) +{ + return shutdown (socket, (int) how) == SOCKET_ERROR ? -1 : 0; +} + +void +enet_socket_destroy (ENetSocket socket) +{ + if (socket != INVALID_SOCKET) + closesocket (socket); +} + +int +enet_socket_send (ENetSocket socket, + const ENetAddress * address, + const ENetBuffer * buffers, + size_t bufferCount) +{ + struct sockaddr_in sin; + DWORD sentLength; + + if (address != NULL) + { + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + } + + if (WSASendTo (socket, + (LPWSABUF) buffers, + (DWORD) bufferCount, + & sentLength, + 0, + address != NULL ? (struct sockaddr *) & sin : NULL, + address != NULL ? sizeof (struct sockaddr_in) : 0, + NULL, + NULL) == SOCKET_ERROR) + { + if (WSAGetLastError () == WSAEWOULDBLOCK) + return 0; + + return -1; + } + + return (int) sentLength; +} + +int +enet_socket_receive (ENetSocket socket, + ENetAddress * address, + ENetBuffer * buffers, + size_t bufferCount) +{ + INT sinLength = sizeof (struct sockaddr_in); + DWORD flags = 0, + recvLength; + struct sockaddr_in sin; + + if (WSARecvFrom (socket, + (LPWSABUF) buffers, + (DWORD) bufferCount, + & recvLength, + & flags, + address != NULL ? (struct sockaddr *) & sin : NULL, + address != NULL ? & sinLength : NULL, + NULL, + NULL) == SOCKET_ERROR) + { + switch (WSAGetLastError ()) + { + case WSAEWOULDBLOCK: + case WSAECONNRESET: + return 0; + } + + return -1; + } + + if (flags & MSG_PARTIAL) + return -1; + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return (int) recvLength; +} + +int +enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout) +{ + struct timeval timeVal; + + timeVal.tv_sec = timeout / 1000; + timeVal.tv_usec = (timeout % 1000) * 1000; + + return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal); +} + +int +enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout) +{ + fd_set readSet, writeSet; + struct timeval timeVal; + int selectCount; + + timeVal.tv_sec = timeout / 1000; + timeVal.tv_usec = (timeout % 1000) * 1000; + + FD_ZERO (& readSet); + FD_ZERO (& writeSet); + + if (* condition & ENET_SOCKET_WAIT_SEND) + FD_SET (socket, & writeSet); + + if (* condition & ENET_SOCKET_WAIT_RECEIVE) + FD_SET (socket, & readSet); + + selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal); + + if (selectCount < 0) + return -1; + + * condition = ENET_SOCKET_WAIT_NONE; + + if (selectCount == 0) + return 0; + + if (FD_ISSET (socket, & writeSet)) + * condition |= ENET_SOCKET_WAIT_SEND; + + if (FD_ISSET (socket, & readSet)) + * condition |= ENET_SOCKET_WAIT_RECEIVE; + + return 0; +} + +#endif + diff --git a/test/enet/client/main.cpp b/test/enet/client/main.cpp new file mode 100644 index 000000000..2c0696869 --- /dev/null +++ b/test/enet/client/main.cpp @@ -0,0 +1,179 @@ + + + + + + +#include +#include +#include + + +int main(int argc, char* argv[]) +{ + printf("starting client (and server)\n"); + + if (enet_initialize () != 0) + { + fprintf (stderr, "An error occurred while initializing ENet.\n"); + return EXIT_FAILURE; + } + atexit (enet_deinitialize); + + ENetAddress selfaddress; + selfaddress.host = ENET_HOST_ANY; + /* Bind the server to port 1111. */ + selfaddress.port = 1111; + + ENetHost * client=0; + while (!client) + { + client = enet_host_create (&selfaddress/* create a client host */, + 32 /* only 32 connections */, + 2 /* allow up 2 channels to be used, 0 and 1 */, + 0/*57600 / 8 56K modem with 56 Kbps downstream bandwidth */, + 0 /* 14400 / 8 56K modem with 14 Kbps upstream bandwidth */); + if (client == NULL) + { + selfaddress.port++; + } + } + if (client == NULL) + { + fprintf (stderr, + "An error occurred while trying to create an ENet client host.\n"); + exit (EXIT_FAILURE); + } + + + + ENetAddress dedicatedserveraddress; + ENetEvent event; + ENetPeer* dedicatedpeer=0; + ENetPeer* natpeer=0; + + /* Connect to some.server.net:1234. */ + enet_address_set_host (& dedicatedserveraddress, "localhost"); + dedicatedserveraddress.port = 1234; + /* Initiate the connection, allocating the two channels 0 and 1. */ + dedicatedpeer = enet_host_connect (client, & dedicatedserveraddress, 2, 0); + if (dedicatedpeer == NULL) + { + fprintf (stderr, "No available peers for initiating an ENet connection.\n"); + exit (EXIT_FAILURE); + } + /* Wait up to 5 seconds for the connection attempt to succeed. */ + if (enet_host_service (client, & event, 5000) > 0 && + event.type == ENET_EVENT_TYPE_CONNECT) + { + char servername[1024]; + enet_address_get_host(&dedicatedserveraddress,servername, 1024); + char serverinfo[1024]; + + sprintf(serverinfo,"Connection to %s:%d succeeded", servername,dedicatedserveraddress.port); + puts (serverinfo); + + /////.... + + /* Wait up to 1000 milliseconds for an event. */ + while (enet_host_service (client, & event, 1000000000) > 0) + { + if (natpeer) + { + /* Create a reliable packet of size 7 containing "packet\0" */ + ENetPacket * packet = enet_packet_create ("packet", + strlen ("packet") + 1, + ENET_PACKET_FLAG_RELIABLE); + /* Extend the packet so and append the string "foo", so it now */ + /* contains "packetfoo\0" */ + enet_packet_resize (packet, strlen ("packetfoo") + 1); + strcpy ((char*)& packet -> data [strlen ("packet")], "foo"); + /* Send the packet to the peer over channel id 0. */ + /* One could also broadcast the packet by */ + /* enet_host_broadcast (host, 0, packet); */ + enet_peer_send (natpeer, 0, packet); + } + switch (event.type) + { + case ENET_EVENT_TYPE_CONNECT: + printf ("A new client connected from %x:%u.\n", + event.peer -> address.host, + event.peer -> address.port); + /* Store any relevant client information here. */ + event.peer -> data = (char*)"Client information"; + break; + case ENET_EVENT_TYPE_RECEIVE: + printf ("A packet of length %u containing %s was received from %s on channel %u.\n", + event.packet -> dataLength, + event.packet -> data, + event.peer -> data, + event.channelID); + /* Clean up the packet now that we're done using it. */ + + if (event.packet->dataLength==sizeof(ENetAddress)) + { + ENetAddress* address = (ENetAddress*)event.packet->data; + printf("received other client's address from server, connecting...\n"); + natpeer = enet_host_connect (client, address, 2, 0); + if (natpeer== NULL) + { + fprintf (stderr, "No available peers for initiating an ENet connection.\n"); + exit (EXIT_FAILURE); + } + /* Wait up to 5 seconds for the connection attempt to succeed. */ + if (enet_host_service (client, & event, 5000) > 0 && + event.type == ENET_EVENT_TYPE_CONNECT) + { + puts ("Connection to natpeer succeeded."); + } else + { + enet_peer_reset (natpeer); + puts ("Connection to natpeer failed."); + natpeer=0; + exit(0); + } + + } + + enet_packet_destroy (event.packet); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + printf ("%s disconected.\n", event.peer -> data); + /* Reset the peer's client information. */ + event.peer -> data = NULL; + } + } + /* One could just use enet_host_service() instead. */ + enet_host_flush (client);//host); + } + else + { + /* Either the 5 seconds are up or a disconnect event was */ + /* received. Reset the peer in the event the 5 seconds */ + /* had run out without any significant event. */ + enet_peer_reset (dedicatedpeer); + puts ("Connection to some.server.net:1234 failed."); + } + + + + + + + enet_host_destroy(client); + + return 0; +} + + + + + + + + + + + + diff --git a/test/enet/client/premake4.lua b/test/enet/client/premake4.lua new file mode 100644 index 000000000..0e38fd6cd --- /dev/null +++ b/test/enet/client/premake4.lua @@ -0,0 +1,25 @@ + + +project ("Test_enet_client") + + language "C++" + + kind "ConsoleApp" + targetdir "../../../bin" + includedirs {"../../../examples/ThirdPartyLibs/enet/include"} + + if os.is("Windows") then + defines { "WIN32" } + links {"Ws2_32","Winmm"} + end + if os.is("Linux") then + end + if os.is("MacOSX") then + end + + links {"enet"} + + files { + "main.cpp", + } + diff --git a/test/enet/server/main.cpp b/test/enet/server/main.cpp new file mode 100644 index 000000000..ded38f2a7 --- /dev/null +++ b/test/enet/server/main.cpp @@ -0,0 +1,112 @@ + +#include +#include +#include + + +ENetPeer* mypeers[2]={0,0}; +ENetAddress clientAddresses[2]; +int numpeers=0; + +int main(int argc, char* argv[]) +{ + fprintf(stderr,"starting enet dedicated server\n"); + + if (enet_initialize () != 0) + { + fprintf (stderr, "An error occurred while initializing ENet.\n"); + return EXIT_FAILURE; + } + atexit (enet_deinitialize); + + ENetAddress address; + ENetHost * server; + /* Bind the server to the default localhost. */ + /* A specific host address can be specified by */ + /* enet_address_set_host (& address, "x.x.x.x"); */ + address.host = ENET_HOST_ANY; + /* Bind the server to port 1234. */ + address.port = 1234; + server = enet_host_create (& address /* the address to bind the server host to */, + 32 /* allow up to 32 clients and/or outgoing connections */, + 2 /* allow up to 2 channels to be used, 0 and 1 */, + 0 /* assume any amount of incoming bandwidth */, + 0 /* assume any amount of outgoing bandwidth */); + if (server == NULL) + { + fprintf (stderr, + "An error occurred while trying to create an ENet server host.\n"); + exit (EXIT_FAILURE); + } + + + ENetEvent event; + + /* Wait up to 10000000 milliseconds for an event. */ + while (enet_host_service (server, & event, 10000000) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_CONNECT: + char clientname[1024]; + enet_address_get_host(&event.peer -> address,clientname, 1024); + printf ("A new client connected from %s:%u.\n", + clientname, + event.peer -> address.port); + /* Store any relevant client information here. */ + event.peer -> data = (char*)"Client information"; + if (numpeers<2) + { + clientAddresses[numpeers] = event.peer->address; + mypeers[numpeers] = event.peer; + } + numpeers++; + if (numpeers==2) + { + printf("exchanging addresses for NAT punchthrough\n"); + //exchange the address info + for (int i=0;i<2;i++) + { + int sz = sizeof(ENetAddress); + /* Create a reliable packet of size 7 containing "packet\0" */ + ENetPacket * packet = enet_packet_create (&clientAddresses[i], + sz, + ENET_PACKET_FLAG_RELIABLE); + enet_peer_send (mypeers[1-i], 0, packet); + + + } + //prepare for the next pair of clients to connect/NAT punchthrough + numpeers=0; + } + + break; + case ENET_EVENT_TYPE_RECEIVE: + printf ("A packet of length %u containing %s was received from %s on channel %u.\n", + event.packet -> dataLength, + event.packet -> data, + event.peer -> data, + event.channelID); + /* Clean up the packet now that we're done using it. */ + enet_packet_destroy (event.packet); + + break; + + case ENET_EVENT_TYPE_DISCONNECT: + printf ("%s disconected.\n", event.peer -> data); + /* Reset the peer's client information. */ + event.peer -> data = NULL; + } + } + + + + + + + enet_host_destroy(server); + printf("server exited, press key\n"); + getchar(); + + return 0; +} diff --git a/test/enet/server/premake4.lua b/test/enet/server/premake4.lua new file mode 100644 index 000000000..84fd143d6 --- /dev/null +++ b/test/enet/server/premake4.lua @@ -0,0 +1,26 @@ + + +project ("Test_enet_server") + + language "C++" + + kind "ConsoleApp" + targetdir "../../../bin" + includedirs {"../../../examples/ThirdPartyLibs/enet/include"} + + if os.is("Windows") then + defines { "WIN32" } + + links {"Ws2_32","Winmm"} + end + if os.is("Linux") then + end + if os.is("MacOSX") then + end + + links {"enet"} + + files { + "main.cpp", + } + From c2006f31e46579ba3460000620f9bf3b7e9366de Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Wed, 6 May 2015 08:27:01 -0700 Subject: [PATCH 2/4] don't build any demos on older Mac OS versions <10.9 use btCompoundCompoundCollisionShape to determine maximum collision algorithm size --- CMakeLists.txt | 2 +- .../CollisionDispatch/btDefaultCollisionConfiguration.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c54b9abfd..7d0bccf49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,7 +187,7 @@ ENDIF (OPENGL_FOUND) OPTION(BUILD_BULLET2_DEMOS "Set when you want to build the Bullet 2 demos" ON) -IF(BUILD_BULLET2_DEMOS) +IF(BUILD_BULLET2_DEMOS AND BUILD_OPENGL3_DEMOS) IF(EXISTS ${BULLET_PHYSICS_SOURCE_DIR}/examples AND IS_DIRECTORY ${BULLET_PHYSICS_SOURCE_DIR}/examples) diff --git a/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp b/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp index dac2711e4..d42f00a63 100644 --- a/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp +++ b/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp @@ -105,11 +105,12 @@ btDefaultCollisionConfiguration::btDefaultCollisionConfiguration(const btDefault int maxSize = sizeof(btConvexConvexAlgorithm); int maxSize2 = sizeof(btConvexConcaveCollisionAlgorithm); int maxSize3 = sizeof(btCompoundCollisionAlgorithm); + int maxSize4 = sizeof(btCompoundCompoundCollisionAlgorithm); int collisionAlgorithmMaxElementSize = btMax(maxSize,constructionInfo.m_customCollisionAlgorithmMaxElementSize); collisionAlgorithmMaxElementSize = btMax(collisionAlgorithmMaxElementSize,maxSize2); collisionAlgorithmMaxElementSize = btMax(collisionAlgorithmMaxElementSize,maxSize3); - + collisionAlgorithmMaxElementSize = btMax(collisionAlgorithmMaxElementSize,maxSize4); if (constructionInfo.m_persistentManifoldPool) { From 0da584a42d1dc8792f2efd6e169cea6b5dc0caf8 Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Wed, 6 May 2015 10:35:14 -0700 Subject: [PATCH 3/4] allow to compile and run the ExampleBrowser on older Mac OSX versions (without OpenGL3) using the 'NO_OPENGL3' preprocessor definition both premake/cmake has support for this now needs some testing --- CMakeLists.txt | 22 +++++++------ build3/findOpenGLGlewGlut.lua | 4 +++ build3/premake4_osx32 | Bin 0 -> 384184 bytes examples/ExampleBrowser/ExampleEntries.cpp | 7 ++++- .../ExampleBrowser/OpenGLExampleBrowser.cpp | 29 +++++++++++++----- examples/ExampleBrowser/premake4.lua | 2 +- .../OpenGLWindow/GLInstancingRenderer.cpp | 2 ++ examples/OpenGLWindow/GLPrimitiveRenderer.cpp | 4 ++- examples/OpenGLWindow/GLRenderToTexture.cpp | 2 ++ examples/OpenGLWindow/MacOpenGLWindow.mm | 20 +++++++----- examples/OpenGLWindow/OpenGLInclude.h | 2 +- .../opengl_fontstashcallbacks.cpp | 3 ++ examples/Vehicles/Hinge2Vehicle.cpp | 2 -- test/GwenOpenGLTest/OpenGLSample.cpp | 19 +++++++++--- 14 files changed, 82 insertions(+), 36 deletions(-) create mode 100755 build3/premake4_osx32 diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d0bccf49..72374090a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,20 +180,11 @@ ELSE (OPENGL_FOUND) SET(OPENGL_glu_LIBRARY glu32) ENDIF (OPENGL_FOUND) -# ADD_DEFINITIONS(-DBT_USE_FREEGLUT) #FIND_PACKAGE(GLU) -OPTION(BUILD_BULLET2_DEMOS "Set when you want to build the Bullet 2 demos" ON) -IF(BUILD_BULLET2_DEMOS AND BUILD_OPENGL3_DEMOS) - - - IF(EXISTS ${BULLET_PHYSICS_SOURCE_DIR}/examples AND IS_DIRECTORY ${BULLET_PHYSICS_SOURCE_DIR}/examples) - SUBDIRS(examples) - ENDIF() -ENDIF(BUILD_BULLET2_DEMOS) IF (APPLE) FIND_LIBRARY(COCOA_LIBRARY Cocoa) @@ -207,6 +198,7 @@ IF(BUILD_BULLET3) IF(_CURRENT_OSX_VERSION VERSION_LESS 10.9) MESSAGE("Mac OSX below 10.9 has no OpenGL 3 support so please disable the BUILD_OPENGL3_DEMOS option") #unset(BUILD_OPENGL3_DEMOS CACHE) + OPTION(BUILD_OPENGL3_DEMOS "Set when you want to build the OpenGL3+ demos" OFF) ELSE() OPTION(BUILD_OPENGL3_DEMOS "Set when you want to build the OpenGL3+ demos" ON) @@ -222,8 +214,20 @@ IF(BUILD_OPENGL3_DEMOS) IF(EXISTS ${BULLET_PHYSICS_SOURCE_DIR}/Demos3 AND IS_DIRECTORY ${BULLET_PHYSICS_SOURCE_DIR}/Demos3) SUBDIRS(Demos3) ENDIF() +ELSE() + ADD_DEFINITIONS(-DNO_OPENGL3) ENDIF(BUILD_OPENGL3_DEMOS) +OPTION(BUILD_BULLET2_DEMOS "Set when you want to build the Bullet 2 demos" ON) +IF(BUILD_BULLET2_DEMOS) + + IF(EXISTS ${BULLET_PHYSICS_SOURCE_DIR}/examples AND IS_DIRECTORY ${BULLET_PHYSICS_SOURCE_DIR}/examples) + SUBDIRS(examples) + ENDIF() +ENDIF(BUILD_BULLET2_DEMOS) + + + OPTION(BUILD_EXTRAS "Set when you want to build the extras" ON) IF(BUILD_EXTRAS) SUBDIRS(Extras) diff --git a/build3/findOpenGLGlewGlut.lua b/build3/findOpenGLGlewGlut.lua index 886b6a561..6ed26e65e 100644 --- a/build3/findOpenGLGlewGlut.lua +++ b/build3/findOpenGLGlewGlut.lua @@ -29,6 +29,10 @@ configuration {"Windows"} links {"opengl32","glu32"} configuration {"MacOSX"} + if (not findOpenGL3()) then + defines {"NO_OPENGL3"} + end + links { "OpenGL.framework"} configuration {"not Windows", "not MacOSX"} if os.is("Linux") then diff --git a/build3/premake4_osx32 b/build3/premake4_osx32 new file mode 100755 index 0000000000000000000000000000000000000000..5f5156216e6c5e677174425da394adb919920b45 GIT binary patch literal 384184 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 f4p>HKe8sBfWM#z1?flq- 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 gAdditionalRegisteredExamples; @@ -223,11 +226,13 @@ ExampleEntries::~ExampleEntries() void ExampleEntries::initOpenCLExampleEntries() { #ifdef B3_USE_CLEW +#ifndef NO_OPENGL3 int numDefaultEntries = sizeof(gOpenCLExamples)/sizeof(ExampleEntry); for (int i=0;im_allExamples.push_back(gOpenCLExamples[i]); } +#endif #endif //B3_USE_CLEW } diff --git a/examples/ExampleBrowser/OpenGLExampleBrowser.cpp b/examples/ExampleBrowser/OpenGLExampleBrowser.cpp index ce75cf208..773813a73 100644 --- a/examples/ExampleBrowser/OpenGLExampleBrowser.cpp +++ b/examples/ExampleBrowser/OpenGLExampleBrowser.cpp @@ -2,7 +2,9 @@ #include "LinearMath/btQuickprof.h" #include "../OpenGLWindow/OpenGLInclude.h" #include "../OpenGLWindow/SimpleOpenGL2App.h" +#ifndef NO_OPENGL3 #include "../OpenGLWindow/SimpleOpenGL3App.h" +#endif #include "../CommonInterfaces/CommonRenderInterface.h" #ifdef __APPLE__ #include "../OpenGLWindow/MacOpenGLWindow.h" @@ -60,7 +62,10 @@ static b3AlignedObjectArray allNames; static class ExampleEntries* gAllExamples=0; bool sUseOpenGL2 = false; bool drawGUI=true; +#ifndef USE_OPENGL3 extern bool useShadowMap; +#endif + static bool visualWireframe=false; static bool renderVisualGeometry=true; static bool renderGrid = true; @@ -150,12 +155,12 @@ void MyKeyboardCallback(int key, int state) { pauseSimulation = !pauseSimulation; } - +#ifndef NO_OPENGL3 if (key=='s' && state) { useShadowMap=!useShadowMap; } - +#endif if (key==B3G_ESCAPE && s_window) { s_window->setRequestExit(); @@ -598,10 +603,12 @@ bool OpenGLExampleBrowser::init(int argc, char* argv[]) int width = 1024; int height=768; - +#ifndef NO_OPENGL3 SimpleOpenGL3App* simpleApp=0; sUseOpenGL2 =args.CheckCmdLineFlag("opengl2"); - +#else + sUseOpenGL2 = true; +#endif const char* appTitle = "Bullet Physics ExampleBrowser"; #if defined (_DEBUG) || defined (DEBUG) const char* optMode = "Debug build (slow)"; @@ -615,19 +622,22 @@ bool OpenGLExampleBrowser::init(int argc, char* argv[]) sprintf(title,"%s using limited OpenGL2 fallback. %s", appTitle,optMode); s_app = new SimpleOpenGL2App(title,width,height); s_app->m_renderer = new SimpleOpenGL2Renderer(width,height); - } else + } +#ifndef NO_OPENGL3 + else { char title[1024]; sprintf(title,"%s using OpenGL3+. %s", appTitle,optMode); simpleApp = new SimpleOpenGL3App(title,width,height); s_app = simpleApp; } +#endif char* gVideoFileName = 0; args.GetCmdLineArgument("mp4",gVideoFileName); - + #ifndef NO_OPENGL3 if (gVideoFileName) simpleApp->dumpFramesToVideo(gVideoFileName); - + #endif s_instancingRenderer = s_app->m_renderer; s_window = s_app->m_window; @@ -658,11 +668,14 @@ bool OpenGLExampleBrowser::init(int argc, char* argv[]) if (sUseOpenGL2 ) { gwenRenderer = new Gwen::Renderer::OpenGL_DebugFont(); - } else + } +#ifndef NO_OPENGL3 + else { sth_stash* fontstash=simpleApp->getFontStash(); gwenRenderer = new GwenOpenGL3CoreRenderer(simpleApp->m_primRenderer,fontstash,width,height,s_window->getRetinaScale(),myTexLoader); } +#endif // gui->init(width,height,gwenRenderer,s_window->getRetinaScale()); diff --git a/examples/ExampleBrowser/premake4.lua b/examples/ExampleBrowser/premake4.lua index de359e4ff..101a4c6e0 100644 --- a/examples/ExampleBrowser/premake4.lua +++ b/examples/ExampleBrowser/premake4.lua @@ -93,7 +93,7 @@ } - if (hasCL) then + if (hasCL and findOpenGL3()) then files { "../OpenCL/broadphase/*", "../OpenCL/CommonOpenCL/*", diff --git a/examples/OpenGLWindow/GLInstancingRenderer.cpp b/examples/OpenGLWindow/GLInstancingRenderer.cpp index faae50d41..1ac543995 100644 --- a/examples/OpenGLWindow/GLInstancingRenderer.cpp +++ b/examples/OpenGLWindow/GLInstancingRenderer.cpp @@ -1,3 +1,4 @@ +#ifndef NO_OPENGL3 /* Copyright (c) 2012 Advanced Micro Devices, Inc. @@ -1641,3 +1642,4 @@ int GLInstancingRenderer::getInstanceCapacity() const { return m_data->m_maxNumObjectCapacity; } +#endif //NO_OPENGL3 diff --git a/examples/OpenGLWindow/GLPrimitiveRenderer.cpp b/examples/OpenGLWindow/GLPrimitiveRenderer.cpp index cff4bd52e..60328514e 100644 --- a/examples/OpenGLWindow/GLPrimitiveRenderer.cpp +++ b/examples/OpenGLWindow/GLPrimitiveRenderer.cpp @@ -1,3 +1,4 @@ +#ifndef NO_OPENGL3 #include "GLPrimitiveRenderer.h" #include "GLPrimInternalData.h" @@ -319,4 +320,5 @@ void GLPrimitiveRenderer::setScreenSize(int width, int height) m_screenWidth = width; m_screenHeight = height; -} \ No newline at end of file +} +#endif diff --git a/examples/OpenGLWindow/GLRenderToTexture.cpp b/examples/OpenGLWindow/GLRenderToTexture.cpp index 3ca338eba..89e044ce3 100644 --- a/examples/OpenGLWindow/GLRenderToTexture.cpp +++ b/examples/OpenGLWindow/GLRenderToTexture.cpp @@ -1,3 +1,4 @@ +#ifndef NO_OPENGL3 ///See http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-14-render-to-texture/ @@ -132,4 +133,5 @@ GLRenderToTexture::~GLRenderToTexture() glDeleteFramebuffers(1, &m_framebufferName); } } +#endif diff --git a/examples/OpenGLWindow/MacOpenGLWindow.mm b/examples/OpenGLWindow/MacOpenGLWindow.mm index 66c672ab1..4f2019570 100644 --- a/examples/OpenGLWindow/MacOpenGLWindow.mm +++ b/examples/OpenGLWindow/MacOpenGLWindow.mm @@ -2,8 +2,8 @@ #define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED #import -#include -#include +#include "OpenGLInclude.h" + #include @@ -107,16 +107,16 @@ float loop; { (*m_resizeCallback)(width,height); } - + #ifndef NO_OPENGL3 NSRect backingBounds = [self convertRectToBacking:[self bounds]]; GLsizei backingPixelWidth = (GLsizei)(backingBounds.size.width), backingPixelHeight = (GLsizei)(backingBounds.size.height); // Set viewport glViewport(0, 0, backingPixelWidth, backingPixelHeight); - - // glViewport(0,0,(GLsizei)width,(GLsizei)height); - + #else + glViewport(0,0,(GLsizei)width,(GLsizei)height); +#endif } [m_context setView: self]; @@ -140,7 +140,7 @@ float loop; - +#ifndef NO_OPENGL3 if (openglVersion==3) { NSOpenGLPixelFormatAttribute attrs[] = @@ -156,6 +156,7 @@ float loop; // Init GL context fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes: (NSOpenGLPixelFormatAttribute*)attrs]; } else +#endif { NSOpenGLPixelFormatAttribute attrs[] = { @@ -437,8 +438,11 @@ void MacOpenGLWindow::createWindow(const b3gWindowConstructionInfo& ci) TransformProcessType(&psn, kProcessTransformToForegroundApplication); SetFrontProcess(&psn); */ - +#ifndef NO_OPENGL3 m_retinaScaleFactor = [m_internalData->m_myview convertSizeToBacking:sz].width; +#else + m_retinaScaleFactor=1.f; +#endif [m_internalData->m_myApp finishLaunching]; [pool release]; diff --git a/examples/OpenGLWindow/OpenGLInclude.h b/examples/OpenGLWindow/OpenGLInclude.h index fee6a3095..19219a188 100644 --- a/examples/OpenGLWindow/OpenGLInclude.h +++ b/examples/OpenGLWindow/OpenGLInclude.h @@ -24,7 +24,7 @@ subject to the following restrictions: //#include //#include //#import -#ifdef USE_OPENGL2 +#if defined (USE_OPENGL2) || defined (NO_OPENGL3) #include #else #include diff --git a/examples/OpenGLWindow/opengl_fontstashcallbacks.cpp b/examples/OpenGLWindow/opengl_fontstashcallbacks.cpp index 35250ce2e..18db74692 100644 --- a/examples/OpenGLWindow/opengl_fontstashcallbacks.cpp +++ b/examples/OpenGLWindow/opengl_fontstashcallbacks.cpp @@ -1,3 +1,4 @@ +#ifndef NO_OPENGL3 #include "opengl_fontstashcallbacks.h" #include "../OpenGLWindow/GLPrimitiveRenderer.h" #include "../OpenGLWindow/GLPrimInternalData.h" @@ -250,3 +251,5 @@ void dumpTextureToPng(int textureWidth, int textureHeight, const char* fileName) free(pixels); } +#endif + diff --git a/examples/Vehicles/Hinge2Vehicle.cpp b/examples/Vehicles/Hinge2Vehicle.cpp index 1ed665190..9d72ad57c 100644 --- a/examples/Vehicles/Hinge2Vehicle.cpp +++ b/examples/Vehicles/Hinge2Vehicle.cpp @@ -301,13 +301,11 @@ Hinge2Vehicle::~Hinge2Vehicle() } -extern float shadowMapWorldSize; void Hinge2Vehicle::initPhysics() { m_guiHelper->setUpAxis(1); - shadowMapWorldSize = 10; btCollisionShape* groundShape = new btBoxShape(btVector3(50,3,50)); m_collisionShapes.push_back(groundShape); diff --git a/test/GwenOpenGLTest/OpenGLSample.cpp b/test/GwenOpenGLTest/OpenGLSample.cpp index 3f1452f34..af7781fdf 100644 --- a/test/GwenOpenGLTest/OpenGLSample.cpp +++ b/test/GwenOpenGLTest/OpenGLSample.cpp @@ -2,6 +2,7 @@ #include "Gwen/Gwen.h" #include "Gwen/Skins/Simple.h" +#include "../OpenGLWindow/OpenGLInclude.h" #include "UnitTest.h" @@ -22,9 +23,10 @@ extern unsigned char OpenSansData[]; #endif//__APPLE__ #include "OpenGLWindow/opengl_fontstashcallbacks.h" - +#ifndef NO_OPENGL3 #include "OpenGLWindow/GwenOpenGL3CoreRenderer.h" #include "OpenGLWindow/GLPrimitiveRenderer.h" +#endif #include Gwen::Controls::Canvas* pCanvas = NULL; @@ -79,10 +81,12 @@ static void MyResizeCallback( float width, float height) sWidth = width; sHeight = height; // printf("resize(%d,%d)\n",sWidth,sHeight); +#ifndef NO_OPENGL3 if (primRenderer) { primRenderer->setScreenSize(width,height); } +#endif if (gwenRenderer) { gwenRenderer->Resize(width,height); @@ -96,7 +100,7 @@ static void MyResizeCallback( float width, float height) int droidRegular;//, droidItalic, droidBold, droidJapanese, dejavu; - +#ifndef NO_OPENGL3 sth_stash* initFont(GLPrimitiveRenderer* primRenderer) { GLint err; @@ -217,7 +221,7 @@ sth_stash* initFont(GLPrimitiveRenderer* primRenderer) return stash; } - +#endif void keyCallback(int key, int value) { printf("key = %d, value = %d\n", key,value); @@ -308,7 +312,11 @@ int main() b3gDefaultOpenGLWindow* window = new b3gDefaultOpenGLWindow(); window->setKeyboardCallback(keyCallback); b3gWindowConstructionInfo wci; - wci.m_openglVersion = 2; +#ifndef NO_OPENGL3 + wci.m_openglVersion = 3; +#else + wci.m_openglVersion = 2; +#endif wci.m_width = sWidth; wci.m_height = sHeight; // wci.m_resizeCallback = MyResizeCallback; @@ -333,7 +341,7 @@ int main() sprintf(title,"Gwen with OpenGL %d\n",wci.m_openglVersion); } window->setWindowTitle(title); - +#ifndef NO_OPENGL3 if (majorGlVersion>=3 && wci.m_openglVersion>=3) { float retinaScale = 1.f; @@ -363,6 +371,7 @@ int main() gwenRenderer = new GwenOpenGL3CoreRenderer(primRenderer,font,sWidth,sHeight,retinaScale); } else +#endif { //OpenGL 2.x gwenRenderer = new Gwen::Renderer::OpenGL_DebugFont(); From 5a0b744e73367e63b64e5b5d8ef6384734b8a566 Mon Sep 17 00:00:00 2001 From: erwin coumans Date: Wed, 6 May 2015 12:05:25 -0700 Subject: [PATCH 4/4] fixed premake4 issue causing the build to fail on non-Mac platforms --- build3/findOpenGLGlewGlut.lua | 4 ---- build3/premake4.lua | 19 +++++++++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/build3/findOpenGLGlewGlut.lua b/build3/findOpenGLGlewGlut.lua index 6ed26e65e..886b6a561 100644 --- a/build3/findOpenGLGlewGlut.lua +++ b/build3/findOpenGLGlewGlut.lua @@ -29,10 +29,6 @@ configuration {"Windows"} links {"opengl32","glu32"} configuration {"MacOSX"} - if (not findOpenGL3()) then - defines {"NO_OPENGL3"} - end - links { "OpenGL.framework"} configuration {"not Windows", "not MacOSX"} if os.is("Linux") then diff --git a/build3/premake4.lua b/build3/premake4.lua index 9a6965110..53c087357 100644 --- a/build3/premake4.lua +++ b/build3/premake4.lua @@ -30,6 +30,11 @@ description = "Dynamically load OpenGL (instead of static/dynamic linking)" } + newoption + { + trigger = "noopengl3", + description = "Don't compile any OpenGL3+ code" + } newoption { @@ -37,7 +42,7 @@ description = "Use Midi controller to control parameters" } --- _OPTIONS["midi"] = "1"; +-- --_OPTIONS["midi"] = "1"; newoption { @@ -111,6 +116,10 @@ dofile ("findOpenCL.lua") dofile ("findDirectX11.lua") dofile ("findOpenGLGlewGlut.lua") + + if (not findOpenGL3()) then + defines {"NO_OPENGL3"} + end language "C++" @@ -122,9 +131,11 @@ include "../examples/HelloWorld" include "../examples/BasicDemo" - include "../examples/ThirdPartyLibs/enet" - include "../test/enet/client" - include "../test/enet/server" + if _OPTIONS["enet"] then + include "../examples/ThirdPartyLibs/enet" + include "../test/enet/client" + include "../test/enet/server" + end if not _OPTIONS["without-gtest"] then include "../test/gtest-1.7.0"