From f4eb3b724e6c6a44c0146814adead2b0fb5133ba Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Thu, 20 May 2010 21:03:41 +0000 Subject: [PATCH] enet 1.3 testing git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@4955 e016b1c5-ed68-44aa-a2f7-31cac88cffde --- source/enet/LICENSE | 2 +- source/enet/Makefile.am | 2 +- source/enet/Makefile.in | 12 +- source/enet/callbacks.c | 21 +- source/enet/compress.c | 474 +++++++++++++++++++++++++++ source/enet/configure | 69 ++-- source/enet/configure.ac | 13 +- source/enet/design.txt | 117 ------- source/enet/docs/mainpage.dox | 11 +- source/enet/docs/tutorial.dox | 4 +- source/enet/host.c | 90 +++-- source/enet/include/enet/callbacks.h | 2 - source/enet/include/enet/enet.h | 97 ++++-- source/enet/include/enet/protocol.h | 23 +- source/enet/peer.c | 6 +- source/enet/protocol.c | 199 +++++++---- source/enet/tutorial.txt | 325 ------------------ source/lib/enet.lib | Bin 33854 -> 31760 bytes source/src/client.cpp | 7 +- source/src/server.cpp | 3 +- 20 files changed, 810 insertions(+), 667 deletions(-) create mode 100644 source/enet/compress.c delete mode 100755 source/enet/design.txt delete mode 100755 source/enet/tutorial.txt diff --git a/source/enet/LICENSE b/source/enet/LICENSE index 44b273a1..df0f91e9 100755 --- a/source/enet/LICENSE +++ b/source/enet/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2002-2009 Lee Salzman +Copyright (c) 2002-2010 Lee Salzman Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/source/enet/Makefile.am b/source/enet/Makefile.am index a96f3501..d8fb923f 100755 --- a/source/enet/Makefile.am +++ b/source/enet/Makefile.am @@ -14,7 +14,7 @@ enetinclude_HEADERS = \ include/enet/win32.h lib_LTLIBRARIES = libenet.la -libenet_la_SOURCES = host.c list.c callbacks.c packet.c peer.c protocol.c unix.c win32.c +libenet_la_SOURCES = callbacks.c compress.c host.c list.c packet.c peer.c protocol.c unix.c win32.c # see info '(libtool) Updating version info' before making a release libenet_la_LDFLAGS = $(AM_LDFLAGS) -version-info 0:0:0 INCLUDES = -Iinclude diff --git a/source/enet/Makefile.in b/source/enet/Makefile.in index bf9946fe..0dbe2b42 100755 --- a/source/enet/Makefile.in +++ b/source/enet/Makefile.in @@ -39,9 +39,8 @@ host_triplet = @host@ subdir = . DIST_COMMON = README $(am__configure_deps) $(enetinclude_HEADERS) \ $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ - $(srcdir)/libenet.pc.in $(top_srcdir)/configure ChangeLog \ - config.guess config.sub depcomp install-sh ltmain.sh missing \ - mkinstalldirs + $(srcdir)/libenet.pc.in $(top_srcdir)/configure config.guess \ + config.sub depcomp install-sh ltmain.sh missing mkinstalldirs ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ @@ -79,8 +78,8 @@ am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" \ "$(DESTDIR)$(enetincludedir)" LTLIBRARIES = $(lib_LTLIBRARIES) libenet_la_LIBADD = -am_libenet_la_OBJECTS = host.lo list.lo callbacks.lo packet.lo peer.lo \ - protocol.lo unix.lo win32.lo +am_libenet_la_OBJECTS = callbacks.lo compress.lo host.lo list.lo \ + packet.lo peer.lo protocol.lo unix.lo win32.lo libenet_la_OBJECTS = $(am_libenet_la_OBJECTS) libenet_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ @@ -242,7 +241,7 @@ enetinclude_HEADERS = \ include/enet/win32.h lib_LTLIBRARIES = libenet.la -libenet_la_SOURCES = host.c list.c callbacks.c packet.c peer.c protocol.c unix.c win32.c +libenet_la_SOURCES = callbacks.c compress.c host.c list.c packet.c peer.c protocol.c unix.c win32.c # see info '(libtool) Updating version info' before making a release libenet_la_LDFLAGS = $(AM_LDFLAGS) -version-info 0:0:0 INCLUDES = -Iinclude @@ -328,6 +327,7 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callbacks.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compress.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/host.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet.Plo@am__quote@ diff --git a/source/enet/callbacks.c b/source/enet/callbacks.c index 250b9670..f9412825 100644 --- a/source/enet/callbacks.c +++ b/source/enet/callbacks.c @@ -5,11 +5,14 @@ #define ENET_BUILDING_LIB 1 #include "enet/enet.h" -static ENetCallbacks callbacks = { malloc, free, rand, abort }; +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) @@ -19,14 +22,8 @@ enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits callbacks.free = inits -> free; } - if (inits -> rand != NULL) - callbacks.rand = inits -> rand; - - if (version >= ENET_VERSION_CREATE(1, 2, 2)) - { - if (inits -> no_memory != NULL) - callbacks.no_memory = inits -> no_memory; - } + if (inits -> no_memory != NULL) + callbacks.no_memory = inits -> no_memory; return enet_initialize (); } @@ -48,9 +45,3 @@ enet_free (void * memory) callbacks.free (memory); } -int -enet_rand (void) -{ - return callbacks.rand (); -} - diff --git a/source/enet/compress.c b/source/enet/compress.c new file mode 100644 index 00000000..f21dddd2 --- /dev/null +++ b/source/enet/compress.c @@ -0,0 +1,474 @@ +/** + @file compress.c + @brief An order-1 adaptive range coder +*/ +#define ENET_BUILDING_LIB 1 +#include +#include "enet/enet.h" + +typedef struct _ENetSymbol +{ + enet_uint8 value; + enet_uint8 count; + enet_uint16 total; + enet_uint16 left, right; +} ENetSymbol; + +typedef struct _ENetPredictor +{ + enet_uint16 escapes; + enet_uint16 total; + ENetSymbol *symbols; +} ENetPredictor; + +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_SYMBOL_DELTA = 2, + ENET_SUBCONTEXT_ESCAPE_DELTA = 3 +}; + +typedef struct _ENetRangeCoder +{ + ENetPredictor context, subcontexts[256]; + ENetSymbol symbols[256 + ENET_PROTOCOL_MAXIMUM_MTU + 1]; +} 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_PREDICTOR_CREATE(predictor, escapes_, minimum) \ +{ \ + (predictor).escapes = escapes_; \ + (predictor).total = escapes_ + 256*minimum; \ + (predictor).symbols = NULL; \ +} + +#define ENET_SYMBOL_CREATE(symbol, value_, count_) \ +{ \ + symbol = & rangeCoder -> symbols [nextSymbol ++]; \ + symbol -> value = value_; \ + symbol -> count = count_; \ + symbol -> total = count_; \ + symbol -> left = 0; \ + symbol -> right = 0; \ +} + +static enet_uint16 +enet_symbol_rescale (ENetSymbol * symbol) +{ + enet_uint16 total = 0; + for (;;) + { + symbol -> count -= symbol->count >> 1; + symbol -> total = symbol -> count; + if (symbol -> left) + symbol -> total += enet_symbol_rescale (symbol + symbol -> left); + total += symbol -> total; + if (! symbol -> right) break; + symbol += symbol -> right; + } + return total; +} + +#define ENET_PREDICTOR_RESCALE(predictor, minimum) \ +{ \ + (predictor).total = (predictor).symbols != NULL ? enet_symbol_rescale ((predictor).symbols) : 0; \ + (predictor).escapes -= (predictor).escapes >> 1; \ + (predictor).total += (predictor).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 + 1 >= sizeof (rangeCoder -> symbols) / sizeof (ENetSymbol)) \ + { \ + ENET_PREDICTOR_CREATE (rangeCoder -> context, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); \ + subcontext = NULL; \ + nextSymbol = 0; \ + } \ +} + +#define ENET_PREDICTOR_ENCODE(predictor, value_, under_, count_, update) \ +{ \ + under_ = 0; \ + count_ = 0; \ + if ((predictor).symbols == NULL) \ + { \ + ENET_SYMBOL_CREATE ((predictor).symbols, value_, update); \ + } \ + else \ + { \ + ENetSymbol * symbol = (predictor).symbols, * child; \ + for (;;) \ + { \ + if (value_ < symbol -> value) \ + { \ + symbol -> total += update; \ + if (symbol -> left) { symbol += symbol -> left; continue; } \ + ENET_SYMBOL_CREATE (child, value_, update); \ + symbol -> left = child - symbol; \ + } \ + else \ + if (value_ > symbol -> value) \ + { \ + under_ += symbol -> total; \ + if (symbol -> right) { symbol += symbol -> right; continue; } \ + ENET_SYMBOL_CREATE (child, value_, update); \ + symbol -> right = child - symbol; \ + } \ + else \ + { \ + count_ = symbol -> count; \ + under_ += symbol -> total - count_; \ + symbol -> total += update; \ + symbol -> count += update; \ + } \ + break; \ + } \ + } \ +} + +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; + ENetPredictor * subcontext = NULL; + size_t nextSymbol = 0; + + if (rangeCoder == NULL || inBufferCount <= 0 || inLimit <= 0) + return 0; + + inData = (const enet_uint8 *) inBuffers -> data; + inEnd = & inData [inBuffers -> dataLength]; + inBuffers ++; + inBufferCount --; + + ENET_PREDICTOR_CREATE (rangeCoder -> context, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); + + for (;;) + { + enet_uint8 value; + enet_uint16 under, count; + if (inData >= inEnd) + { + if (inBufferCount <= 0) + break; + inData = (const enet_uint8 *) inBuffers -> data; + inEnd = & inData [inBuffers -> dataLength]; + inBuffers ++; + inBufferCount --; + } + value = * inData ++; + + if (subcontext != NULL) + { + ENET_PREDICTOR_ENCODE (* subcontext, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA); + if (count > 0) + { + ENET_RANGE_CODER_ENCODE (subcontext -> escapes + under, count, subcontext -> total); + } + else + { + if (subcontext -> escapes > 0) + ENET_RANGE_CODER_ENCODE (0, subcontext -> escapes, subcontext -> 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_PREDICTOR_RESCALE (* subcontext, 0); + if (count <= 0) + goto parentContext; + } + else + { + parentContext: + ENET_PREDICTOR_ENCODE (rangeCoder -> context, value, under, count, ENET_CONTEXT_SYMBOL_DELTA); + if (count <= 0) + ENET_PREDICTOR_CREATE (rangeCoder -> subcontexts [value], 0, 0); + ENET_RANGE_CODER_ENCODE (rangeCoder -> context.escapes + under + value*ENET_CONTEXT_SYMBOL_MINIMUM, count + ENET_CONTEXT_SYMBOL_MINIMUM, rangeCoder -> context.total); + rangeCoder -> context.total += ENET_CONTEXT_SYMBOL_DELTA; + if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA || rangeCoder -> context.total > ENET_RANGE_CODER_BOTTOM - 0x100) + ENET_PREDICTOR_RESCALE (rangeCoder -> context, ENET_CONTEXT_SYMBOL_MINIMUM); + } + + subcontext = & rangeCoder -> subcontexts [value]; + 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_PREDICTOR_TRY_DECODE(predictor, code, value_, under_, count_, update) \ +{ \ + under_ = 0; \ + count_ = 0; \ + if ((predictor).symbols == NULL) \ + return 0; \ + else \ + { \ + ENetSymbol * symbol = (predictor).symbols, * child; \ + for (;;) \ + { \ + if (code >= under_ + symbol -> total) \ + { \ + under_ += symbol -> total; \ + if (! symbol -> right) return 0; \ + symbol += symbol -> right; \ + } \ + else \ + if (code < under_ + symbol -> total - symbol -> count) \ + { \ + symbol -> total += update; \ + if (! symbol -> left) return 0; \ + symbol += symbol -> left; \ + } \ + else \ + { \ + value_ = symbol -> value; \ + count_ = symbol -> count; \ + under_ += symbol -> total - count_; \ + symbol -> total += update; \ + symbol -> count += update; \ + break; \ + } \ + } \ + } \ +} + +#define ENET_PREDICTOR_DECODE(predictor, code, value_, under_, count_, update, minimum) \ +{ \ + int lowValue = -1; \ + under_ = 0; \ + count_ = 0; \ + if ((predictor).symbols == NULL) \ + { \ + value_ = code / minimum; \ + ENET_SYMBOL_CREATE ((predictor).symbols, value_, update); \ + } \ + else \ + { \ + ENetSymbol * symbol = (predictor).symbols, * child; \ + for (;;) \ + { \ + enet_uint16 total = under_ + symbol -> total + (symbol -> value + 1)*minimum; \ + if (code >= total) \ + { \ + under_ += symbol -> total; \ + lowValue = symbol -> value; \ + if (symbol -> right) { symbol += symbol -> right; continue; } \ + value_ = lowValue + 1 + (code - under_ - (lowValue + 1)*minimum) / minimum; \ + ENET_SYMBOL_CREATE (child, value_, update); \ + symbol -> right = child - symbol; \ + } \ + else \ + if (code < total - symbol -> count - minimum) \ + { \ + symbol -> total += update; \ + if (symbol -> left) { symbol += symbol -> left; continue; } \ + value_ = lowValue + 1 + (code - under_ - (lowValue + 1)*minimum) / minimum; \ + ENET_SYMBOL_CREATE (child, value_, update); \ + symbol -> left = child - symbol; \ + } \ + else \ + { \ + value_ = symbol -> value; \ + count_ = symbol -> count; \ + under_ += symbol -> total - count_; \ + symbol -> total += update; \ + symbol -> count += update; \ + } \ + break; \ + } \ + } \ +} + +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; + ENetPredictor * subcontext = NULL; + size_t nextSymbol = 0; + + if (rangeCoder == NULL || inLimit <= 0) + return 0; + + ENET_PREDICTOR_CREATE (rangeCoder -> context, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); + + ENET_RANGE_CODER_SEED; + + for (;;) + { + enet_uint8 value = 0; + enet_uint16 code, under, count; + if (subcontext != NULL && subcontext -> escapes > 0) + { + code = ENET_RANGE_CODER_READ (subcontext -> total); + if (code < subcontext -> escapes) + { + ENET_RANGE_CODER_DECODE (0, subcontext -> escapes, subcontext -> total); + goto parentContext; + } + code -= subcontext -> escapes; + ENET_PREDICTOR_TRY_DECODE (* subcontext, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA); + ENET_RANGE_CODER_DECODE (subcontext -> escapes + under, count, subcontext -> total); + subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA; + if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100) + ENET_PREDICTOR_RESCALE (* subcontext, 0); + } + else + { + parentContext: + code = ENET_RANGE_CODER_READ (rangeCoder -> context.total); + if (code < rangeCoder -> context.escapes) + { + ENET_RANGE_CODER_DECODE (0, rangeCoder -> context.escapes, rangeCoder -> context.total); + break; + } + code -= rangeCoder -> context.escapes; + ENET_PREDICTOR_DECODE (rangeCoder -> context, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM); + if (count <= 0) + ENET_PREDICTOR_CREATE (rangeCoder -> subcontexts [value], 0, 0); + ENET_RANGE_CODER_DECODE (rangeCoder -> context.escapes + under + value*ENET_CONTEXT_SYMBOL_MINIMUM, count + ENET_CONTEXT_SYMBOL_MINIMUM, rangeCoder -> context.total); + rangeCoder -> context.total += ENET_CONTEXT_SYMBOL_DELTA; + if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA || rangeCoder -> context.total > ENET_RANGE_CODER_BOTTOM - 0x100) + ENET_PREDICTOR_RESCALE (rangeCoder -> context, ENET_CONTEXT_SYMBOL_MINIMUM); + if (subcontext != NULL) + { + enet_uint16 under, count; + ENET_PREDICTOR_ENCODE (* subcontext, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA); + if (count <= 0) + { + 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_PREDICTOR_RESCALE (* subcontext, 0); + } + } + + ENET_RANGE_CODER_OUTPUT (value); + + subcontext = & rangeCoder -> subcontexts [value]; + ENET_RANGE_CODER_FREE_SYMBOLS; + } + + return (size_t) (outData - outStart); +} + +/** Sets the packet compressor the host should use to the default range compressor. + @param host host to enable or disable compression 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/source/enet/configure b/source/enet/configure index 8adafabc..471e57a0 100755 --- a/source/enet/configure +++ b/source/enet/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.65 for libenet 1.2.2. +# Generated by GNU Autoconf 2.65 for libenet 1.3.0. # # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, @@ -698,8 +698,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='libenet' PACKAGE_TARNAME='libenet' -PACKAGE_VERSION='1.2.2' -PACKAGE_STRING='libenet 1.2.2' +PACKAGE_VERSION='1.3.0' +PACKAGE_STRING='libenet 1.3.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -863,7 +863,6 @@ with_pic enable_fast_install with_gnu_ld enable_libtool_lock -enable_crc32 ' ac_precious_vars='build_alias host_alias @@ -1415,7 +1414,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures libenet 1.2.2 to adapt to many kinds of systems. +\`configure' configures libenet 1.3.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1485,7 +1484,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libenet 1.2.2:";; + short | recursive ) echo "Configuration of libenet 1.3.0:";; esac cat <<\_ACEOF @@ -1502,7 +1501,6 @@ Optional Features: --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) - --enable-crc32 enable CRC32 packet verification Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -1587,7 +1585,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libenet configure 1.2.2 +libenet configure 1.3.0 generated by GNU Autoconf 2.65 Copyright (C) 2009 Free Software Foundation, Inc. @@ -1976,7 +1974,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libenet $as_me 1.2.2, which was +It was created by libenet $as_me 1.3.0, which was generated by GNU Autoconf 2.65. Invocation command line was $ $0 $@ @@ -2785,7 +2783,7 @@ fi # Define the identity of the package. PACKAGE='libenet' - VERSION='1.2.2' + VERSION='1.3.0' cat >>confdefs.h <<_ACEOF @@ -4511,13 +4509,13 @@ if test "${lt_cv_nm_interface+set}" = set; then : else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:4514: $ac_compile\"" >&5) + (eval echo "\"\$as_me:4512: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 - (eval echo "\"\$as_me:4517: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval echo "\"\$as_me:4515: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 - (eval echo "\"\$as_me:4520: output\"" >&5) + (eval echo "\"\$as_me:4518: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" @@ -5723,7 +5721,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 5726 "configure"' > conftest.$ac_ext + echo '#line 5724 "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -7253,11 +7251,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7256: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7254: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7260: \$? = $ac_status" >&5 + echo "$as_me:7258: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7592,11 +7590,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7595: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7593: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7599: \$? = $ac_status" >&5 + echo "$as_me:7597: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7697,11 +7695,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7700: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7698: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7704: \$? = $ac_status" >&5 + echo "$as_me:7702: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -7752,11 +7750,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7755: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7753: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7759: \$? = $ac_status" >&5 + echo "$as_me:7757: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -10136,7 +10134,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10139 "configure" +#line 10137 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -10232,7 +10230,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10235 "configure" +#line 10233 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -10540,25 +10538,6 @@ fi rm -f conftest* -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use CRC32" >&5 -$as_echo_n "checking whether to use CRC32... " >&6; } -# Check whether --enable-crc32 was given. -if test "${enable_crc32+set}" = set; then : - enableval=$enable_crc32; if test "$enableval" = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - $as_echo "#define USE_CRC32 1" >>confdefs.h - - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - fi -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - ac_config_files="$ac_config_files Makefile libenet.pc" cat >confcache <<\_ACEOF @@ -11122,7 +11101,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by libenet $as_me 1.2.2, which was +This file was extended by libenet $as_me 1.3.0, which was generated by GNU Autoconf 2.65. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -11179,7 +11158,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -libenet config.status 1.2.2 +libenet config.status 1.3.0 configured by $0, generated by GNU Autoconf 2.65, with options \\"\$ac_cs_config\\" diff --git a/source/enet/configure.ac b/source/enet/configure.ac index 0cc2906c..d625727d 100644 --- a/source/enet/configure.ac +++ b/source/enet/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([libenet], [1.2.2]) +AC_INIT([libenet], [1.3.0]) AC_CONFIG_SRCDIR([include/enet/enet.h]) AM_INIT_AUTOMAKE([foreign]) AM_MAINTAINER_MODE([enable]) @@ -25,17 +25,6 @@ AC_CHECK_TYPE(socklen_t, [AC_DEFINE(HAS_SOCKLEN_T)], , AC_EGREP_HEADER(MSG_MAXIOVLEN, /usr/include/sys/socket.h, AC_DEFINE(ENET_BUFFER_MAXIMUM, [MSG_MAXIOVLEN])) AC_EGREP_HEADER(MSG_MAXIOVLEN, socket.h, AC_DEFINE(ENET_BUFFER_MAXIMUM, [MSG_MAXIOVLEN])) -AC_MSG_CHECKING(whether to use CRC32) -AC_ARG_ENABLE(crc32, - [ --enable-crc32 enable CRC32 packet verification ], - [if test "$enableval" = yes; then - AC_MSG_RESULT(yes) - AC_DEFINE(USE_CRC32) - else - AC_MSG_RESULT(no) - fi], - [AC_MSG_RESULT(no)]) - AC_CONFIG_FILES([Makefile libenet.pc]) AC_OUTPUT diff --git a/source/enet/design.txt b/source/enet/design.txt deleted file mode 100755 index 9a88dba9..00000000 --- a/source/enet/design.txt +++ /dev/null @@ -1,117 +0,0 @@ -* Why ENet? - - ENet evolved specifically as a UDP networking layer for the multiplayer -first person shooter Cube. Cube necessitated low latency communcation with -data sent out very frequently, so TCP was an unsuitable choice due to its -high latency and stream orientation. UDP, however, lacks many sometimes -necessary features from TCP such as reliability, sequencing, unrestricted -packet sizes, and connection management. So UDP by itself was not suitable -as a network protocol either. No suitable freely available networking -libraries existed at the time of ENet's creation to fill this niche. - - UDP and TCP could have been used together in Cube to benefit somewhat -from both of their features, however, the resulting combinations of protocols -still leaves much to be desired. TCP lacks multiple streams of communication -without resorting to opening many sockets and complicates delineation of -packets due to its buffering behavior. UDP lacks sequencing, connection -management, management of bandwidth resources, and imposes limitations on -the size of packets. A significant investment is required to integrate these -two protocols, and the end result is worse off in features and performance -than the uniform protocol presented by ENet. - - ENet thus attempts to address these issues and provide a single, uniform -protocol layered over UDP to the developer with the best features of UDP and -TCP as well as some useful features neither provide, with a much cleaner -integration than any resulting from a mixture of UDP and TCP. - -* Connection management - - ENet provides a simple connection interface over which to communicate -with a foreign host. The liveness of the connection is actively monitored -by pinging the foreign host at frequent intervals, and also monitors the -network conditions from the local host to the foreign host such as the -mean round trip time and packet loss in this fashion. - -* Sequencing - - Rather than a single byte stream that complicates the delineation -of packets, ENet presents connections as multiple, properly sequenced packet -streams that simplify the transfer of various types of data. - - ENet provides sequencing for all packets by assigning to each sent -packet a sequence number that is incremented as packets are sent. ENet -guarentees that no packet with a higher sequence number will be delivered -before a packet with a lower sequence number, thus ensuring packets are -delivered exactly in the order they are sent. - - For unreliable packets, ENet will simply discard the lower sequence -number packet if a packet with a higher sequence number has already been -delivered. This allows the packets to be dispatched immediately as they -arrive, and reduce latency of unreliable packets to an absolute minimum. -For reliable packets, if a higher sequence number packet arrives, but the -preceding packets in the sequence have not yet arrived, ENet will stall -delivery of the higher sequence number packets until its predecessors -have arrived. - -* Channels - - Since ENet will stall delivery of reliable packets to ensure proper -sequencing, and consequently any packets of higher sequence number whether -reliable or unreliable, in the event the reliable packet's predecessors -have not yet arrived, this can introduce latency into the delivery of other -packets which may not need to be as strictly ordered with respect to the -packet that stalled their delivery. - - To combat this latency and reduce the ordering restrictions on packets, -ENet provides multiple channels of communication over a given connection. -Each channel is independently sequenced, and so the delivery status of -a packet in one channel will not stall the delivery of other packets -in another channel. - -* Reliability - - ENet provides optional reliability of packet delivery by ensuring the -foreign host acknowledges receipt of all reliable packets. ENet will attempt -to resend the packet up to a reasonable amount of times, if no acknowledgement -of the packet's receipt happens within a specified timeout. Retry timeouts -are progressive and become more lenient with every failed attempt to allow -for temporary turbulence in network conditions. - -* Fragmentation and reassembly - - ENet will send and deliver packets regardless of size. Large packets are -fragmented into many smaller packets of suitable size, and reassembled on -the foreign host to recover the original packet for delivery. The process -is entirely transparent to the developer. - -* Aggregation - - ENet aggregates all protocol commands, including acknowledgements and -packet transfer, into larger protocol packets to ensure the proper utilization -of the connection and to limit the opportunities for packet loss that might -otherwise result in further delivery latency. - -* Adaptability - - ENet provides an in-flight data window for reliable packets to ensure -connections are not overwhelmed by volumes of packets. It also provides a -static bandwidth allocation mechanism to ensure the total volume of packets -sent and received to a host don't exceed the host's capabilities. Further, -ENet also provides a dynamic throttle that responds to deviations from normal -network connections to rectify various types of network congestion by further -limiting the volume of packets sent. - -* Portability - - ENet works on Windows and any other Unix or Unix-like platform providing -a BSD sockets interface. The library has a small and stable code base that -can easily be extended to support other platforms and integrates easily. - -* Freedom - - ENet demands no royalties and doesn't carry a viral license that would -restrict you in how you might use it in your programs. ENet is licensed under -a short-and-sweet MIT-style license, which gives you the freedom to do anything -you want with it (well, almost anything). - - diff --git a/source/enet/docs/mainpage.dox b/source/enet/docs/mainpage.dox index b8876ba6..8c32b498 100755 --- a/source/enet/docs/mainpage.dox +++ b/source/enet/docs/mainpage.dox @@ -7,10 +7,10 @@ network communication layer on top of UDP (User Datagram Protocol). The primary feature it provides is optional reliable, in-order delivery of packets. -ENet is NOT intended to be a general purpose high level networking -library that handles authentication, lobbying, server discovery, -compression, encryption and other high level, often application level -or dependent tasks. +ENet omits certain higher level networking features such as authentication, +lobbying, server discovery, encryption, or other similar tasks that are +particularly application specific so that the library remains flexible, +portable, and easily embeddable. @ref Features @@ -36,7 +36,8 @@ or dependent tasks. You can retrieve the source to ENet by downloading it in either .tar.gz form or accessing the cvs distribution directly. -The most recent stable release (1.2.1) can be downloaded here. +The most recent stable release (1.3.0) can be downloaded here. +The last release that is protocol compatible with the 1.2 series or earlier (1.2.2) can be downloaded here To access ENet via anonymous CVS, you must use the CVSROOT :pserver:anonymous\@bespin.org:/var/lib/cvs/enet with an empty diff --git a/source/enet/docs/tutorial.dox b/source/enet/docs/tutorial.dox index 62e615fc..dd911d30 100755 --- a/source/enet/docs/tutorial.dox +++ b/source/enet/docs/tutorial.dox @@ -73,6 +73,7 @@ and the resources used by the host will be freed. 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) @@ -100,6 +101,7 @@ may be simultaneously open. client = enet_host_create (NULL /* create a client host */, 1 /* only allow 1 outgoing connection */, + 2 /* allow up 2 channels to be used, 0 and 1 */, 57600 / 8 /* 56K modem with 56 Kbps downstream bandwidth */, 14400 / 8 /* 56K modem with 14 Kbps upstream bandwidth */); @@ -321,7 +323,7 @@ ENET_EVENT_TYPE_DISCONNECT will be generated. address.port = 1234; /* Initiate the connection, allocating the two channels 0 and 1. */ - peer = enet_host_connect (client, & address, 2); + peer = enet_host_connect (client, & address, 2, 0); if (peer == NULL) { diff --git a/source/enet/host.c b/source/enet/host.c index 67accd93..22d7dfa6 100755 --- a/source/enet/host.c +++ b/source/enet/host.c @@ -4,6 +4,7 @@ */ #define ENET_BUILDING_LIB 1 #include +#include #include "enet/enet.h" /** @defgroup host ENet host functions @@ -14,6 +15,7 @@ @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. @@ -25,7 +27,7 @@ at any given time. */ ENetHost * -enet_host_create (const ENetAddress * address, size_t peerCount, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) +enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) { ENetHost * host; ENetPeer * currentPeer; @@ -66,7 +68,15 @@ enet_host_create (const ENetAddress * address, size_t peerCount, enet_uint32 inc if (address != NULL) host -> address = * address; - host -> channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; + 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) time(NULL) + (enet_uint32) (size_t) host; + host -> randomSeed = (host -> randomSeed << 16) | (host -> randomSeed >> 16); + host -> channelLimit = channelLimit; host -> incomingBandwidth = incomingBandwidth; host -> outgoingBandwidth = outgoingBandwidth; host -> bandwidthThrottleEpoch = 0; @@ -75,8 +85,10 @@ enet_host_create (const ENetAddress * address, size_t peerCount, enet_uint32 inc 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; @@ -84,6 +96,11 @@ enet_host_create (const ENetAddress * address, size_t peerCount, enet_uint32 inc host -> totalReceivedData = 0; host -> totalReceivedPackets = 0; + host -> compressor.context = NULL; + host -> compressor.compress = NULL; + host -> compressor.decompress = NULL; + host -> compressor.destroy = NULL; + enet_list_clear (& host -> dispatchQueue); for (currentPeer = host -> peers; @@ -92,6 +109,7 @@ enet_host_create (const ENetAddress * address, size_t peerCount, enet_uint32 inc { currentPeer -> host = host; currentPeer -> incomingPeerID = currentPeer - host -> peers; + currentPeer -> outgoingSessionID = currentPeer -> incomingSessionID = 0xFF; currentPeer -> data = NULL; enet_list_clear (& currentPeer -> acknowledgements); @@ -103,7 +121,7 @@ enet_host_create (const ENetAddress * address, size_t peerCount, enet_uint32 inc enet_peer_reset (currentPeer); } - + return host; } @@ -124,6 +142,9 @@ enet_host_destroy (ENetHost * host) enet_peer_reset (currentPeer); } + if (host -> compressor.context && host -> compressor.destroy) + (* host -> compressor.destroy) (host -> compressor.context); + enet_free (host -> peers); enet_free (host); } @@ -132,12 +153,13 @@ enet_host_destroy (ENetHost * 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_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount, enet_uint32 data) { ENetPeer * currentPeer; ENetChannel * channel; @@ -166,7 +188,7 @@ enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelC currentPeer -> channelCount = channelCount; currentPeer -> state = ENET_PEER_STATE_CONNECTING; currentPeer -> address = * address; - currentPeer -> sessionID = (enet_uint32) enet_rand (); + currentPeer -> connectID = ++ host -> randomSeed; if (host -> outgoingBandwidth == 0) currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; @@ -199,7 +221,9 @@ enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelC 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.mtu = ENET_HOST_TO_NET_16 (currentPeer -> mtu); + 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); @@ -207,29 +231,14 @@ enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelC 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.sessionID = currentPeer -> sessionID; - + 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; } -/** 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; -} - /** 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 @@ -254,6 +263,39 @@ enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet) 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 && 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 diff --git a/source/enet/include/enet/callbacks.h b/source/enet/include/enet/callbacks.h index b488ac72..340a4a98 100644 --- a/source/enet/include/enet/callbacks.h +++ b/source/enet/include/enet/callbacks.h @@ -11,7 +11,6 @@ typedef struct _ENetCallbacks { void * (ENET_CALLBACK * malloc) (size_t size); void (ENET_CALLBACK * free) (void * memory); - int (ENET_CALLBACK * rand) (void); void (ENET_CALLBACK * no_memory) (void); } ENetCallbacks; @@ -21,7 +20,6 @@ typedef struct _ENetCallbacks */ extern void * enet_malloc (size_t); extern void enet_free (void *); -extern int enet_rand (void); /** @} */ diff --git a/source/enet/include/enet/enet.h b/source/enet/include/enet/enet.h index 7d4ac334..3c3ef948 100755 --- a/source/enet/include/enet/enet.h +++ b/source/enet/include/enet/enet.h @@ -24,8 +24,8 @@ extern "C" #include "enet/callbacks.h" #define ENET_VERSION_MAJOR 1 -#define ENET_VERSION_MINOR 2 -#define ENET_VERSION_PATCH 2 +#define ENET_VERSION_MINOR 3 +#define ENET_VERSION_PATCH 0 #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) @@ -233,7 +233,9 @@ typedef struct _ENetPeer struct _ENetHost * host; enet_uint16 outgoingPeerID; enet_uint16 incomingPeerID; - enet_uint32 sessionID; + 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; @@ -267,7 +269,7 @@ typedef struct _ENetPeer 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_uint16 mtu; + enet_uint32 mtu; enet_uint32 windowSize; enet_uint32 reliableDataInTransit; enet_uint16 outgoingReliableSequenceNumber; @@ -281,9 +283,26 @@ typedef struct _ENetPeer enet_uint16 incomingUnsequencedGroup; enet_uint16 outgoingUnsequencedGroup; enet_uint32 unsequencedWindow [ENET_PEER_UNSEQUENCED_WINDOW_SIZE / 32]; - enet_uint32 disconnectData; + 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); + /** An ENet host for communicating with peers. * * No fields should be modified. @@ -294,38 +313,43 @@ typedef struct _ENetPeer @sa enet_host_service() @sa enet_host_flush() @sa enet_host_broadcast() + @sa enet_host_compress() @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; - 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; - ENetAddress receivedAddress; - enet_uint8 receivedData [ENET_PROTOCOL_MAXIMUM_MTU]; - 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 */ + 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; + 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 */ } ENetHost; /** @@ -470,13 +494,15 @@ ENET_API void enet_packet_destroy (ENetPacket *); ENET_API int enet_packet_resize (ENetPacket *, size_t); extern enet_uint32 enet_crc32 (const ENetBuffer *, size_t); -ENET_API ENetHost * enet_host_create (const ENetAddress *, size_t, enet_uint32, enet_uint32); +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_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 *); @@ -496,6 +522,11 @@ extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32); extern ENetAcknowledgement * enet_peer_queue_acknowledgement (ENetPeer *, const ENetProtocol *, enet_uint16); +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 diff --git a/source/enet/include/enet/protocol.h b/source/enet/include/enet/protocol.h index 4e61cad3..19f7e45d 100755 --- a/source/enet/include/enet/protocol.h +++ b/source/enet/include/enet/protocol.h @@ -16,7 +16,7 @@ enum ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE = 32768, ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1, ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255, - ENET_PROTOCOL_MAXIMUM_PEER_ID = 0x7FFF + ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xFFF }; typedef enum _ENetProtocolCommand @@ -43,8 +43,12 @@ typedef enum _ENetProtocolFlag ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 << 7), ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 << 6), - ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 << 15), - ENET_PROTOCOL_HEADER_FLAG_MASK = 0x8000 + 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_ @@ -58,7 +62,6 @@ typedef enum _ENetProtocolFlag typedef struct _ENetProtocolHeader { - enet_uint32 checksum; enet_uint16 peerID; enet_uint16 sentTime; } ENET_PACKED ENetProtocolHeader; @@ -81,7 +84,9 @@ typedef struct _ENetProtocolConnect { ENetProtocolCommandHeader header; enet_uint16 outgoingPeerID; - enet_uint16 mtu; + enet_uint8 incomingSessionID; + enet_uint8 outgoingSessionID; + enet_uint32 mtu; enet_uint32 windowSize; enet_uint32 channelCount; enet_uint32 incomingBandwidth; @@ -89,14 +94,17 @@ typedef struct _ENetProtocolConnect enet_uint32 packetThrottleInterval; enet_uint32 packetThrottleAcceleration; enet_uint32 packetThrottleDeceleration; - enet_uint32 sessionID; + enet_uint32 connectID; + enet_uint32 data; } ENET_PACKED ENetProtocolConnect; typedef struct _ENetProtocolVerifyConnect { ENetProtocolCommandHeader header; enet_uint16 outgoingPeerID; - enet_uint16 mtu; + enet_uint8 incomingSessionID; + enet_uint8 outgoingSessionID; + enet_uint32 mtu; enet_uint32 windowSize; enet_uint32 channelCount; enet_uint32 incomingBandwidth; @@ -104,6 +112,7 @@ typedef struct _ENetProtocolVerifyConnect enet_uint32 packetThrottleInterval; enet_uint32 packetThrottleAcceleration; enet_uint32 packetThrottleDeceleration; + enet_uint32 connectID; } ENET_PACKED ENetProtocolVerifyConnect; typedef struct _ENetProtocolBandwidthLimit diff --git a/source/enet/peer.c b/source/enet/peer.c index 395a9027..d73625e7 100755 --- a/source/enet/peer.c +++ b/source/enet/peer.c @@ -325,7 +325,7 @@ void enet_peer_reset (ENetPeer * peer) { peer -> outgoingPeerID = ENET_PROTOCOL_MAXIMUM_PEER_ID; - peer -> sessionID = 0; + peer -> connectID = 0; peer -> state = ENET_PEER_STATE_DISCONNECTED; @@ -363,7 +363,7 @@ enet_peer_reset (ENetPeer * peer) peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; peer -> incomingUnsequencedGroup = 0; peer -> outgoingUnsequencedGroup = 0; - peer -> disconnectData = 0; + peer -> eventData = 0; memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow)); @@ -477,7 +477,7 @@ enet_peer_disconnect_later (ENetPeer * peer, enet_uint32 data) enet_list_empty (& peer -> sentReliableCommands))) { peer -> state = ENET_PEER_STATE_DISCONNECT_LATER; - peer -> disconnectData = data; + peer -> eventData = data; } else enet_peer_disconnect (peer, data); diff --git a/source/enet/protocol.c b/source/enet/protocol.c index 21460b1b..b4d13b8b 100755 --- a/source/enet/protocol.c +++ b/source/enet/protocol.c @@ -48,6 +48,7 @@ enet_protocol_dispatch_incoming_commands (ENetHost * host, ENetEvent * event) event -> type = ENET_EVENT_TYPE_CONNECT; event -> peer = peer; + event -> data = peer -> eventData; return 1; @@ -56,7 +57,7 @@ enet_protocol_dispatch_incoming_commands (ENetHost * host, ENetEvent * event) event -> type = ENET_EVENT_TYPE_DISCONNECT; event -> peer = peer; - event -> data = peer -> disconnectData; + event -> data = peer -> eventData; enet_peer_reset (peer); @@ -111,6 +112,7 @@ enet_protocol_notify_connect (ENetHost * host, ENetPeer * peer, ENetEvent * even 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); @@ -134,7 +136,11 @@ enet_protocol_notify_disconnect (ENetHost * host, ENetPeer * peer, ENetEvent * e enet_peer_reset (peer); } else + { + peer -> eventData = 0; + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + } } static void @@ -238,32 +244,13 @@ enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliabl static ENetPeer * enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENetProtocol * command) { - enet_uint16 mtu; - enet_uint32 windowSize; + enet_uint8 incomingSessionID, outgoingSessionID; + enet_uint32 mtu, windowSize; ENetChannel * channel; size_t channelCount; ENetPeer * currentPeer; ENetProtocol verifyCommand; -#ifdef USE_CRC32 - { - enet_uint32 crc = header -> checksum; - ENetBuffer buffer; - - command -> header.reliableSequenceNumber = ENET_HOST_TO_NET_16 (command -> header.reliableSequenceNumber); - - header -> checksum = command -> connect.sessionID; - - buffer.data = host -> receivedData; - buffer.dataLength = host -> receivedDataLength; - - if (enet_crc32 (& buffer, 1) != crc) - return NULL; - - command -> header.reliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> header.reliableSequenceNumber); - } -#endif - channelCount = ENET_NET_TO_HOST_32 (command -> connect.channelCount); if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || @@ -277,7 +264,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet if (currentPeer -> state != ENET_PEER_STATE_DISCONNECTED && currentPeer -> address.host == host -> receivedAddress.host && currentPeer -> address.port == host -> receivedAddress.port && - currentPeer -> sessionID == command -> connect.sessionID) + currentPeer -> connectID == command -> connect.connectID) return NULL; } @@ -299,7 +286,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet return NULL; currentPeer -> channelCount = channelCount; currentPeer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT; - currentPeer -> sessionID = command -> connect.sessionID; + 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); @@ -307,6 +294,19 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet 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]; @@ -323,7 +323,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows)); } - mtu = ENET_NET_TO_HOST_16 (command -> connect.mtu); + mtu = ENET_NET_TO_HOST_32 (command -> connect.mtu); if (mtu < ENET_PROTOCOL_MINIMUM_MTU) mtu = ENET_PROTOCOL_MINIMUM_MTU; @@ -371,6 +371,8 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet 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_16 (currentPeer -> mtu); verifyCommand.verifyConnect.windowSize = ENET_HOST_TO_NET_32 (windowSize); verifyCommand.verifyConnect.channelCount = ENET_HOST_TO_NET_32 (channelCount); @@ -379,6 +381,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet 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); @@ -655,7 +658,9 @@ enet_protocol_handle_disconnect (ENetHost * host, ENetPeer * peer, const ENetPro else enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); - peer -> disconnectData = ENET_NET_TO_HOST_32 (command -> disconnect.data); + if (peer -> state != ENET_PEER_STATE_DISCONNECTED) + peer -> eventData = ENET_NET_TO_HOST_32 (command -> disconnect.data); + return 0; } @@ -735,7 +740,7 @@ enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer * if (enet_list_empty (& peer -> outgoingReliableCommands) && enet_list_empty (& peer -> outgoingUnreliableCommands) && enet_list_empty (& peer -> sentReliableCommands)) - enet_peer_disconnect (peer, peer -> disconnectData); + enet_peer_disconnect (peer, peer -> eventData); break; } @@ -745,8 +750,7 @@ enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer * static int enet_protocol_handle_verify_connect (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command) { - enet_uint16 mtu; - enet_uint32 windowSize; + enet_uint32 mtu, windowSize; size_t channelCount; if (peer -> state != ENET_PEER_STATE_CONNECTING) @@ -757,8 +761,11 @@ enet_protocol_handle_verify_connect (ENetHost * host, ENetEvent * event, ENetPee 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) + 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; @@ -770,8 +777,10 @@ enet_protocol_handle_verify_connect (ENetHost * host, ENetEvent * event, ENetPee 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_16 (command -> verifyConnect.mtu); + mtu = ENET_NET_TO_HOST_32 (command -> verifyConnect.mtu); if (mtu < ENET_PROTOCOL_MINIMUM_MTU) mtu = ENET_PROTOCOL_MINIMUM_MTU; @@ -809,15 +818,21 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) enet_uint8 * currentData; size_t headerSize; enet_uint16 peerID, flags; + enet_uint8 sessionID; - if (host -> receivedDataLength < sizeof (ENetProtocolHeader)) + 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; + 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; @@ -829,35 +844,55 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) peer = & host -> peers [peerID]; if (peer -> state == ENET_PEER_STATE_DISCONNECTED || - peer -> state == ENET_PEER_STATE_ZOMBIE || + peer -> state == ENET_PEER_STATE_ZOMBIE || (host -> receivedAddress.host != peer -> address.host && - peer -> address.host != ENET_HOST_BROADCAST)) + 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; -#ifdef USE_CRC32 - { - enet_uint32 crc = header -> checksum; - ENetBuffer buffer; + 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; - header -> checksum = peer -> sessionID; + memcpy (host -> packetData [1], header, headerSize); + host -> receivedData = host -> packetData [1]; + host -> receivedDataLength = headerSize + originalSize; + } - buffer.data = host -> receivedData; - buffer.dataLength = host -> receivedDataLength; + if (host -> checksum != NULL) + { + enet_uint32 * checksum = (enet_uint32 *) & host -> receivedData [headerSize - sizeof (enet_uint32)], + desiredChecksum = * checksum; + ENetBuffer buffer; - if (enet_crc32 (& buffer, 1) != crc) - return 0; - } -#else - if (header -> checksum != peer -> sessionID) - return 0; -#endif + * 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; } - headerSize = (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME ? sizeof (ENetProtocolHeader) : (size_t) & ((ENetProtocolHeader *) 0) -> sentTime); currentData = host -> receivedData + headerSize; while (currentData < & host -> receivedData [host -> receivedDataLength]) @@ -990,8 +1025,8 @@ enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event) int receivedLength; ENetBuffer buffer; - buffer.data = host -> receivedData; - buffer.dataLength = sizeof (host -> receivedData); + buffer.data = host -> packetData [0]; + buffer.dataLength = sizeof (host -> packetData [0]); receivedLength = enet_socket_receive (host -> socket, & host -> receivedAddress, @@ -1004,6 +1039,7 @@ enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event) if (receivedLength == 0) return 0; + host -> receivedData = host -> packetData [0]; host -> receivedDataLength = receivedLength; host -> totalReceivedData += receivedLength; @@ -1157,7 +1193,7 @@ enet_protocol_send_unreliable_outgoing_commands (ENetHost * host, ENetPeer * pee enet_list_empty (& peer -> outgoingReliableCommands) && enet_list_empty (& peer -> outgoingUnreliableCommands) && enet_list_empty (& peer -> sentReliableCommands)) - enet_peer_disconnect (peer, peer -> disconnectData); + enet_peer_disconnect (peer, peer -> eventData); } static int @@ -1320,10 +1356,12 @@ enet_protocol_send_reliable_outgoing_commands (ENetHost * host, ENetPeer * peer) static int enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int checkForTimeouts) { - ENetProtocolHeader header; + 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) @@ -1402,22 +1440,57 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch currentPeer -> packetsLost = 0; } - header.checksum = currentPeer -> sessionID; - header.peerID = ENET_HOST_TO_NET_16 (currentPeer -> outgoingPeerID | host -> headerFlags); - - host -> buffers -> data = & header; + host -> buffers -> data = headerData; if (host -> headerFlags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME) { - header.sentTime = ENET_HOST_TO_NET_16 (host -> serviceTime & 0xFFFF); + header -> sentTime = ENET_HOST_TO_NET_16 (host -> serviceTime & 0xFFFF); host -> buffers -> dataLength = sizeof (ENetProtocolHeader); } else host -> buffers -> dataLength = (size_t) & ((ENetProtocolHeader *) 0) -> sentTime; - -#ifdef USE_CRC32 - header.checksum = enet_crc32 (host -> buffers, host -> bufferCount); + + 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; diff --git a/source/enet/tutorial.txt b/source/enet/tutorial.txt deleted file mode 100755 index 9341d6c2..00000000 --- a/source/enet/tutorial.txt +++ /dev/null @@ -1,325 +0,0 @@ -* Using ENet - - Before using ENet, you must call enet_initialize() to initialize the -library. Upon program exit, you should call enet_deinitialize() so that -the library may clean up any used resources. - -i.e. - -int -main (int argc, char ** argv) -{ - if (enet_initialize () != 0) - { - fprintf (stderror, "An error occurred while initializing ENet.\n"); - return EXIT_FAILURE; - } - atexit (enet_deinitialize); - ... - ... - ... -} - -* Creating an ENet server - - Servers in ENet are constructed with enet_host_create(). You must specify -an address on which to receive data and new connections, as well as the maximum -allowable numbers of connected peers. You may optionally specify the incoming -and outgoing bandwidth of the server in bytes per second so that ENet may try -to statically manage bandwidth resources among connected peers in addition to -its dynamic throttling algorithm; specifying 0 for these two options will cause -ENet to rely entirely upon its dynamic throttling algorithm to manage -bandwidth. - - When done with a host, the host may be destroyed with enet_host_destroy(). -All connected peers to the host will be reset, and the resources used by -the host will be freed. - -i.e. - - 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 */, - 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); - } - ... - ... - ... - enet_host_destroy(server); - -* Creating an ENet client - - Clients in ENet are similarly constructed with enet_host_create() when no -address is specified to bind the host to. Bandwidth may be specified for the -client host as in the above example. The peer count controls the maximum number -of connections to other server hosts that may be simultaneously open. - -i.e. - - ENetHost * client; - - clienet = enet_host_create (NULL /* create a client host */, - 1 /* only allow 1 outgoing connection */, - 57600 / 8 /* 56K modem with 56 Kbps downstream bandwidth */, - 14400 / 8 /* 56K modem with 14 Kbps upstream bandwidth */); - - if (client == NULL) - { - fprintf (stderr, - "An error occurred while trying to create an ENet client host.\n"); - exit (EXIT_FAILURE); - } - ... - ... - ... - enet_host_destroy(client); - -* Managing an ENet host - - ENet uses a polled event model to notify the programmer of significant -events. ENet hosts are polled for events with enet_host_service(), where an -optional timeout value in milliseconds may be specified to control how long -ENet will poll; if a timeout of 0 is specified, enet_host_service() will -return immediately if there are no events to dispatch. enet_host_service() -will return 1 if an event was dispatched within the specified timeout. - - Currently there are only four types of significant events in ENet: - -An event of type ENET_EVENT_TYPE_NONE is returned if no event occurred -within the specified time limit. enet_host_service() will return 0 -with this event. - -An event of type ENET_EVENT_TYPE_CONNECT is returned when either a new client -host has connected to the server host or when an attempt to establish a -connection with a foreign host has succeeded. Only the "peer" field of the -event structure is valid for this event and contains the newly connected peer. - -An event of type ENET_EVENT_TYPE_RECEIVE is returned when a packet is received -from a connected peer. The "peer" field contains the peer the packet was -received from, "channelID" is the channel on which the packet was sent, and -"packet" is the packet that was sent. The packet contained in the "packet" -field must be destroyed with enet_packet_destroy() when you are done -inspecting its contents. - -An event of type ENET_EVENT_TYPE_DISCONNECT is returned when a connected peer -has either explicitly disconnected or timed out. Only the "peer" field of the -event structure is valid for this event and contains the peer that -disconnected. Only the "data" field of the peer is still valid on a -disconnect event and must be explicitly reset. - -i.e. - - ENetEvent event; - - /* Wait up to 1000 milliseconds for an event. */ - while (enet_host_service (client, & event, 1000) > 0) - { - 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 = "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. */ - 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; - } - } - ... - ... - ... - -* Sending a packet to an ENet peer - - Packets in ENet are created with enet_packet_create(), where the size of -the packet must be specified. Optionally, initial data may be specified to -copy into the packet. - - Certain flags may also be supplied to enet_packet_create() to control -various packet features: - -ENET_PACKET_FLAG_RELIABLE specifies that the packet must use reliable delivery. -A reliable packet is guarenteed to be delivered, and a number of retry attempts -will be made until an acknowledgement is received from the foreign host the -packet is sent to. If a certain number of retry attempts is reached without -any acknowledgement, ENet will assume the peer has disconnected and forcefully -reset the connection. If this flag is not specified, the packet is assumed -an unreliable packet, and no retry attempts will be made nor acknowledgements -generated. - - A packet may be resized (extended or truncated) with enet_packet_resize(). - - A packet is sent to a foreign host with enet_peer_send(). enet_peer_send() -accepts a channel id over which to send the packet to a given peer. Once the -packet is handed over to ENet with enet_peer_send(), ENet will handle its -deallocation and enet_packet_destroy() should not be used upon it. - - One may also use enet_host_broadcast() to send a packet to all connected -peers on a given host over a specified channel id, as with enet_peer_send(). - - Queued packets will be sent on a call to enet_host_service(). -Alternatively, enet_host_flush() will send out queued packets without -dispatching any events. - -i.e. - - /* 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 (& packet -> data [strlen ("packet")], "foo"); - - /* Send the packet to the peer over channel id 3. - * One could also broadcast the packet by - * enet_host_broadcast (host, 3, packet); - */ - enet_peer_send (peer, 3, packet); - ... - ... - ... - /* One could just use enet_host_service() instead. */ - enet_host_flush (host); - -* Disconnecting an ENet peer - - Peers may be gently disconnected with enet_peer_disconnect(). A disconnect -request will be sent to the foreign host, and ENet will wait for an -acknowledgement from the foreign host before finally disconnecting. An -event of type ENET_EVENT_TYPE_DISCONNECT will be generated once the -disconnection succeeds. Normally timeouts apply to the disconnect -acknowledgement, and so if no acknowledgement is received after a length -of time the peer will be forcefully disconnected. - - enet_peer_reset() will forcefully disconnect a peer. The foreign host -will get no notification of a disconnect and will time out on the foreign -host. No event is generated. - -i.e. - ENetEvent event; - - enet_peer_disconnect (& client -> peers [0], 0); - - /* Allow up to 3 seconds for the disconnect to succeed - * and drop any packets received packets. - */ - while (enet_host_service (client, & event, 3000) > 0) - { - switch (event.type) - { - case ENET_EVENT_TYPE_RECEIVE: - enet_packet_destroy (event.packet); - break; - - case ENET_EVENT_TYPE_DISCONNECT: - puts ("Disconnection succeeded."); - return; - ... - ... - ... - } - } - - /* We've arrived here, so the disconnect attempt didn't succeed yet. - * Force the connection down. - */ - enet_peer_reset (& client -> peers [0]); - ... - ... - ... - -* Connecting to an ENet host - - A connection to a foregin host is initiated with enet_host_connect(). -It accepts the address of a foreign host to connect to, and the number of -channels that should be allocated for communication. If N channels are -allocated for use, their channel ids will be numbered 0 through N-1. -A peer representing the connection attempt is returned, or NULL if there -were no available peers over which to initiate the connection. When the -connection attempt succeeds, an event of type ENET_EVENT_TYPE_CONNECT will -be generated. If the connection attempt times out or otherwise fails, an -event of type ENET_EVENT_TYPE_DISCONNECT will be generated. - -i.e. - ENetAddress address; - ENetEvent event; - ENetPeer *peer; - - /* Connect to some.server.net:1234. */ - enet_address_set_host (& address, "some.server.net"); - address.port = 1234; - - /* Initiate the connection, allocating the two channels 0 and 1. */ - peer = enet_host_connect (client, & address, 2); - - if (peer == 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 some.server.net:1234 succeeded."); - ... - ... - ... - } - 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 (peer); - - puts ("Connection to some.server.net:1234 failed."); - } - ... - ... - ... - diff --git a/source/lib/enet.lib b/source/lib/enet.lib index 879f50284a2814cf78141b281a928e2284d7dc11..cbd1748b40a32aed2ea634d078afc42d589fc4d6 100644 GIT binary patch literal 31760 zcmeHw4R~9{mG+gbI7$#)K@BxvK!6ft>QXB)tc9Icwrt0c07~NgLBI))g&mUEwIwHz z0CptVnM|~w%g;i$d|UbyzGVwv%d&w2Wx=u?%Y;9Z6bP`;KwGd7KM5&`6Da=PGjs2i zWCdF2?(;p*K6?}2d+wRPGv}N+GjnF<>gzvCR41Ot|-cdiZUgT`8N&dWBr|4rzq)vr|(dd^uM$F6y@vwU0kLp>3`SX zuPA@}UqjRSra*(gy{So&VEd}|t2J=lsE{MBufn z8n5rr!kb$=0u9$9@rIVw2wvN=t|f5npg?nbYap;zFHk0IZ(X&zaaBh~lE&8c>zf)g zP^+6d0`09gq-WgN-n1&9XSTKlR5Y$_TGg&a5r~%c9Zl_eJ_6I;w63)?BT)xxrlo0Y zZ=5|#gWFI=FsQvLEnLH8(xM?VsA+3zYHyfSU4i>yuqoKofUMWIZdlv2`nsldP3v{~ z(jv61Z){!Hvi`aTJl3V;k`|*i7`U$WOJcMEuh$}-5ahPr-*R2BU9Ydmuf3_Usl{N4 zKy@@3s-Q*b04oITy3|wn^paM$bZD|`Kn0H-a%^nFTF6qDT^sbIwXt=rUj2qN83?Uu zY;J12zM-iT%+m=Hcu2dmrBSb*K>61OJDRmJJ6cJmRF{sXwJ8OY=!Ole48_$jnXJ)} zY0S|unfjn1S2Z>^wdtbOaG82-ebNK zj)v<{Ls9vbHZ4y*gz8Qq8amLjt_i%zctv6@bahjz?Q5tkgr2cpq+*zd3tENQ)om?V zR98s}46Sj$sI=5>FDv~!X=?2&r_VR#!mcXHKbREdTLLnhE(kPjgz+^l7*J(9{+x(I zmj!=z#kcZ;)vE%l1V-MhcoZVeMlBxY>`_YkFSxd&BQ4r7!7jw($D0v0@pfNy$4rXE zkEfDS@1YM0c(tR@H*qqBhK6jB{(w0&WE~u%cYhxOEuqa0vnenoG%_*R$1Ra(0uJso zM|KB3V#iY?b}|*gJr*RL@^i{QL1+;txGVW?U;0HM`jrEKI;K43Bm+9%fG!3^_(c;HIU*IFiZTO#cKoF>aadG+NQ){z47?D+1Xkt`FnY4$>1PIlX(J z9ys^Wj&pfcL1=7BXxy|Ri8gKZAWti=Dw7dRJKaay-z0pbUk$IJGEhFU$)CW*tH>&x z%r`qM>aWHh+jH5qw!`nm-p^z8j(kdktZax|U`LVVYaRLRDzN#k-LZXB4u6Wkf;2+S zJ?{N#&kay!q@V;!@^l1u$B0h7Hi$H$_9PL=D|M=0=amjnu?K{i%NLXcc4d--5)0U? zv5)ikf-?121F^9w?s&)lVdHEMsBrB*e2`VHj`Rno$17K(AmqE+UAeL|msPGrN;6-9 znh?h;ONu@N!dAzCDf%-wtyRuWsS*o`8|`+)Xd^n<0p`oktlV(N_mEjzys{MWm3Sov zy^11h$GwiIxl5}fUu2J(x41_(yc8N*an&^qR}b>}4qN0I^^Sfu@(R+3GBy@QCIUa= zKGGG+2po4tcB>H|sG)KXvjMPC1W7#r=Z4kL3XmBMI7*W!@F(5zG}cljWBYRWrFPSZ zdtB}L9#v?r&f}Mcs~x4=aJz6K^nn%i*v!2)pU_9Jp)NFJC1#b+Emp&uL4l0pT%<>+ zMg-v2DD-d1Qh^Q)*{Mu0GSNn5)jRBw-N9fOYIYh?iAo$u6Z>%c1&JKs`=WuFRKpCt z(1~ATH;s{ky!Hh$G!*0|1Pjd=0pSH~`D*v_m7Ql38`<)ee3>Kp5Ic}q{AVPAe4*xd z^d~wcMMPclR(61|by!`y5$uH8no077M4#5gq~@AXCRpwuX(UfgqmUgoBrwuhnqhwXq- ziDe+0c;D(=oX+}07lNfFi9SvANqb`D0f!xp#i>5J*5TlnJNPwFJ!VuZE6z7hc*>V3GQ34bg>jSMheI(`653n@fiGaScELR` zcO|Pu6>QSZ{UA1Gi|w)9cZNaTW~vh!q02-kn@ZQ5F)ekgQE`Kb34*c!p`KP zB1V&Tf)V-h4P0D-bj7^VY6O=ccm?;QOh74LztPaSGJbQLY~)B+!LJDzhQPyXEx=Vv zOAtk_z9ph&um&mS^-*tUgLW43Gi6dwoIcLH>mjS0z;{b4&% ze+Nk|zl)8=_L-yRoM>qTY*l1+K8Nk9vBwfQEbi(*_(0t3ph23Eac*dQLJdCzjf9TD z?5W{6;H67ZW#OgvY|Tk)Ec_S#G%(>1xgT(_%Mi81rpk{AfE z1ljQuq5Xd5wXj7r*nql)d{c>Jw#bSo#c{6~D6@ICxL0%>ac{xKDWcgq;-LyaBC;1R zbs`;lvCw=2st4!jB9p|ub{1#e;;8vn!}>qrwpA%^}*m10kumCo|rL(%x8wY(VH~Wv`c^ z99ow zS`CQiDf!Bc2E+l${C;dezc8TR7|@6Ty=*}57|<7hBzoQjap{UHpMK4rb`(Yl8WoAW}9ar zh8=&YndBV2wIOK=Bj#z4dWzRoTVLJ3z%|Dw1 zt?So%O7%3DNfM?b9YbN;^a!XgnNmghr!34vS(uS5%$r%5kFqdFvM_nD!t&BfKQRk) zP8McX7G`c1W=RHyd^vK?EWBI)-<{67TQA)at>MzVa(}_km+3vs@~ftK=ri2%Y5svM z-*=W1SNgIuOA@}nl(SEyk?!d%-!c{Ob^HhyUI+(+xKcj7&!Hg43niJ7<u zm;D$%z4UM${;g_13jn$c1m6@3a(E)4YZiJb6otfL%iTJzJ zJeWK_qu;@>!0hVhC%Sh9j-zo8Kgr|X6+AMiN6NNkf|K9KmXY11{wHOX&cj47@q;L;o7HP*!i{_#c@?E)hyAH9@&_2`Q zy)(Ly=FmV+AU8DPIW%CVr~S}?g`T!U12%eE4h`7pX^tJuJ2YS-FemhmNMgFbON~5? zEMogkr2+9z0Gj#*il+pYvriJ$7z3L6FZ}fxF>~^19e79wex?Jr>Oe>X5kwaR=2O`o)1dqHj)HADRj!d(!ZHKXHD$In zLUg%b502HkKZ1)rZb`nxMnd~(ey}k$4taj;39;#fM!kk?hb25-XX8q9GJ#B ztge1U8$>cW8(?t^G2yX?|L8p*_ge91@yV&fGCt2D2ez($8q#jEAbBz-=ct&HSwsO| z^7&0eev2k(&PnA2RWUBs5>rx8vh2#M5=59b)z{`&(2`QnSPYFRS(86T5h(uP;hm&s z0Xu86&@>ps`s>YXlQr%oHXw?X?G?2}LHaz$hPez#IVo}|@Z7?ssd-cKDI!i}?Ro>5 zCu3xyxZNRD}T0HV_Kl@|fYQRr)cP7=6N(9R@O3rNP> zY(TdGk|};>#G7G2*>+R23X?3S-t>e`M9Igx1b+}fOp{Ii96*FkVT$qAhx94i>H%hd z24*oZC7@8o$+oQzB8+TF>iTB3HQSzu5nsj_kjiK=3KHuJ>zdXzw%wrN^if!6Mx1WE zNgJ)LbElRHR(ltEY2_OM^{cKmR_L{*?QgaQo-tYxqnPP5#z@n8 z>k${ty3l18SIV~6V%k9xJeF z%IJm@;1+l7hEJT0UNz|NE3bA~JI~~BkuPX7jq>HI5%L7INpbJ(d=pMpiWN2cT)W*A z@mUcu;2!OqUNjooHG#;?k|P_Q&40cK=?WP*L~q zI&wn;HotqIgFKh;BC8SVW613329w5ZqHdBGq5i4vftz-Rj!add=wA;DGyg zfu}H9w6IaWra08^bngtD=l)&$>4{$ARg8Z3sw zd2?wfR>IzLKOeLvOkf0Y>iIyetAAUKHMCC+jZY1JfHW3A_swLZA}e>i-NkFb$$wU`LSB4Qc8u~2<9s5!Cx2susWWqRE-fCMGX zR|c2ndjJ2JuG;zM)qelJ(5&aWYIXp+`0@MP} z>DpK!?zJLV(K>(4F-dfgRVaq*8w<9~OZyX?XsmU9wx}?=$u5=YBghK+LJqB-uhh&wDrq8>69 z@_-kYZ@CA-9zPRLQK`6>BGF3P!iBO3@=zY52>(QmKVF3n7TgetD0$)#N&8WxfPb4B zhbMGn!4%qqQ873S#f#u}GAVbOGkxLM*+?Xv>1p#kv3y(s4W17c)MMzi3^npG??%?q z#x?~gH!gCMv_TyTw80`)x(rXg-6+WJTQv3<6fQL6qp_v%*JGl%4f(KG?Cm^F^eDtu z^b+?KnqJ}yN}`n|?t#DpS3ldctx9VS7zgV^HQwRj-U_rge!?Y@es)5Z-&I>g*pDbz zUUkhNud?{dD`&!NEpSFFo$i6)G}oakY58TakGGexJ&{AfkNmuHCSPdrpm_z~=H602 zTQCzt#%xr`eA?B>mD?92&4QO zd&gMREIdiE-{%z#rY)$LUW+#GL$#aBo2}{=>Z-TaJ7yryW?p5!KR0-sKXgM5n#tqT z3QTHb8$EN>@DK5X034Tq9e&N2P!)>!qtLJweK?$`>NFbgR~hf;?6BR33M z3e;X|vabHbd1xj?+El^t%A7=0L-b5$rm%2=`CvxkN7TGVOyEVL0$|46#tM6C=VHtd z{ogclaPJMAEA$I_YSe2Bc`_C>*}XUT4v##D$k9jW?%p3PNLC}hww@(|DBk|S1*u#H zshN1{pvf=uvMX2_?Y#=_GQts>e3D0Yh+OHOl`A2?GFRKB%*M+i6WO_v@S~5=9k~`J z>&X74(MthenX}~*X5lt0^=Hvb_BZ+n-LvQ=ar6VSf+VzJUjyP%@x zmyw|odnd1GAZk8^c1F;KE?(=5-t2Ub2c5}!*dnlZ^4gCkm0BkeGoCFH57O-9f_Ws8 zCo)9xAg?Wn-dyt6_GB$^mmx+(|9MJ7MUk*5j`ID#-gFey$(td_d4aHSUyVRqf|c1DQ7<3Me6!&=OZo=V;hwh?B z7ePK0OjmOkZfmqY9`?r`8Y;Ld@gFcx+5~|{_r%{a@E09k0oha94jLMt7O2GlF;FQQ z(=U)>7&fsQx3ecicWv0eR6p!QKU{K%`r&+joFQ7CYe?+BpQuPIQYnQteu-!dd8TA! zNY)T*N@@!;#fDYg^N$i&G2bhr`EHuLAR;yV5(GwqdqxP(qK5mW1l91b2_Wm8I3fjT zK9&HfTlH#ttJI!~9mh&S4kny;LMBz6H^0ZS)w zzXT+4djOp+LJu3E=fLeJ*Q=+Q^>z2}0Fr5+10>hjKL;dpX@F~5LZ1PmW|gl@0mTwh z4X6!}+*3gN0_3{(od&cW9FsZ!+<^XIKz{}#^IK)n<81^aam;}31azvP;CMJ}WqykR z$#Q9>UX~j%pr06_zX2q<@rDt4zz97Nb1@luAt0H{JU}w%>y1!ug#OG3jTxcpaeCU* z4d?H>K9ss5Unv;t26fpZUFx2&yKzgY-?*gM((=msESP34b;*fj62EL>)=K*7f z-&A6<{n|GojOvxTzL{S;m0pJ$)(2XfGj=j$?`_ETeT!6C-f%B!Aurwcy*LZAG7Ce~ z%FI;J3`_&O>U26LGY<7fnV1JNFfqJIKf72{|DR)EY0O9+5KCuK3Y&2ZOspJd9N0_S z3?X)aq#y43szYNLYZ2KC(%L$QzCU8Ro|V2w z_9@gPdkxxiuA=Nf*l!(DkI0JH{|ojzT+p6*C~67X5eXg)sectig@Y!4fN zv&YK2Xg4Y6L__r7#NFHlVw~8OTj)LztsbaAG4Xv}iw) zm8!jWQ8@U*E6La6;+GWi-`q`t8xhK28ZQ@9P__3)MD!PZ0?JCGW*5Aj>Xu!UmKM}N zZR8Lq208;XWwp=pBiC6Vs8}wO3Aydr=bu((_D@t_n&+RWIg?)o^$Qm8P#=+Z+jiV> zE``-#3;YyTlOO#z(L%&s4ZndKn`}6jVH#)N>colEAfU)`PYJk8Y^EL5_E3KX z$+0LFJCYkUU!rY&&fL>XS`amNvtCkQHa>-PyJ&#OM=oWt@m#7jfvlI58_s^=n4x^* zb46*%K7G9u+t8O4BPqDy;FoFJ-lOI&tzZ=69&FEH&(a)nJh$V7(D;-Z68^-849@MLvl-yuz8JhJQeK%Hmh|B6)Y5?*6!^ z48#fhOPR*INXba7aZf3xUJAHX>i)ADz8yF=2y-2SQft;WCampn_h22VE>?y>j~#^D zk`AasXxJGVv}lV(+ID*g$Bq3yYM3_AqSe{eo&zYvjom?N&wGFb-7rIb-Ycx&rKPf$ z8zDDK&rYH%SCBQz7nZV*$#iy@nXiq=D5mF_W+;g=yhkR~t=~q0psSc)fpbO-HUm7DM~lF9sA>sz zJb+|6EO({dv=<#`iQ2n~tVC4_1Q4kqSr4rc+pex26zvDbp*UVCS}(ef&5pvNBgoeZ zeJeT``|L|?1sX7=WZlA6{FvAe;X5L4s9_qTlOnJ;`E6u~yLSq6{Jt9Qq7Wi~UNzs~ zWc!Oo=1g2QbBfw~9R-9$zYp`05Wq;$UM-Xy;%El1bsrJ2dp@N1y+!-yd@qRoP`&Tt z6{n!etMc1V)1%x66)!s2{aG)3uWIiI#b~d?`s!&Qx`bCr1I>R-9ahmG6Hg);Qsdrw z5A)XZcAM!CYqu>NLlgzw2AEQG6vL3B5q`0# zK&P3F6zwmsvbDd07$tmJ9k>)JBoU*?$TMtSof^KHUMc;vNS{{O-ftnG`-rG%j~MJg zEy%Yso}>2CsR{DEg_puIz>KQBmlJ?WtaVh#4i98%22UQ?>?5@zCUg%7E@SD|{Ud_Z z@XfFeY!?wR_8uI!L>zx$2tnP0bce^d+Wn>){;e)VWYj%CLuoEM3|KrE_-J*UMo1N0 z^bye=aq2~)B8E05!pH+nxgdF=)IV>XT$I3yj?5hNOB>2kM_6z|w^|J!hoZ6PceA5n zTa6uycKcK}ZSJ&2?g15QPbDf4t)<#Si6y)`5+QssI_1v`N) zF=J>1!c8w|Lxv5d?A2(8{os9Ccd)3AdIOxMqBS1yzYc4}l%C<#UzL@a_|j6-09#rr z7yR}0#(-lPl7)uJf>&Vc3$cNit42PhMvb~4D+E;hrGC9ww$~E2MGS9Y!Hb4tU*xif zMMr)EcK@6sa(jq^u__gb5Sew3#2vJHxlT#gq`G>_e@w~}+zr~R%O5Ab2oAH6DWZjU z(>?g>#f$mlJ|tgic>R+<2WwKp@zco)=|V=kqBWL?Hv%chNqE=Tg4R)<$(C3+?6ttM+RWc7z`DO1#~H_N<>*-P;) z)I{On5>}fs_g1jI{BfZg{Z=~pXItXq)1)}X)-Mkm2Fv(e)cU)J>Fz!lJU6i%3kxa1 z(ykCh`bQb8hRZWTy`n+%2VRt|-x>KZIE1u@mLwnTKB`GKaR$j@1u8x-T?cx@RQgf% zJVON%QeGu|sgIi4>-0Leo=(%-#A*5%pSuDBw)b<2M)}fZZ0-s)|Ip8n3$~WQNED64 z#&R*>9c4?G!Fn%U5gW^4N2pn@fE0>(l`~%HLu`dr&W~5t6O`yg8PG$G2zfYL^r&J$ znz#mQH6$pZ7#Pe_Z>}o`pX)@0(_|IzB|)Hz$Esen!w*@Jv=e?5!A5w)ilR}_XBx$L zaPBf}12a8~;1brbB5@7c4u)`v2vnHnv)vz1OXPPusD0w7vzvznh=cb)(16M;A7TWE zz<#`rRbfd-9R8dKy+)T*iiha^uw{WFV2WJjteX5n~f+HE_K1rInLlgq)^K~5B8!2rib z{g*W!cHDyuXyVImcianTjVZca01MFd6)Z;8p93~^?$j0c)z%>@s{!1Oq^=Vs!n zR3@rvE4469nmL4^4{HClo6t}B>Ut5c6vj^t z{{d;xl7|o&uaOgZnr$g;y&30BrgfF0A8N+>6)a>mwd=$zP5xf9ls&82$Z-T@3~3Zr z)5bR$2P~|tCXjpr+UYfw*Vxt2bwo#vzHZ&SOlAayPFE43EoenP@P-u3G>k@246{Ph z`vSgA*n}Y~zA}Qgq4s{upe9|-hIxe2vY2$s8QR7^ale2uBdQbamMW%(XuvLc$_`_< zGzRBvv}oKNQ^TvUxQ0_m@1Er^!e%s)V>B{~d?&5<9~G^M9f-X@1zNUeAK!il#ezah zz=!kpwBsy_O_@hvU{f4fOoNUTM{4nUj7BpoYk}_1V9H_YeJ%%A$Vpg$p}H@NGe!T$ddj~!Cx@lq3s#p zrEYlx%x5E#c_LWs4p*atx={3OYv_SeIs&=zP5H*QP!FGH(}pcHfF8138#@!lio4%Eyy30S!CZ^n0Q}a$`|!mC6kX<9W7tEooA=W=0Rtv-epW$4 zvG2Lwv5;={Wb2ne8Z-tWCoLUODnY<9;8z2`av|+BT;?-AaUuOqYPg>AM75uyCP#b8 z^F=pBD4NLNu?>@lJ?|n%;oG(e4 zq_zXpC?qAYaKc`^DhQ$$@vpkAAa^)y#e3MO5%uORU1M4(4%>-}TExbf6Hzppiq>Vo zoT3XnYF5MD==`HqpNTk^Sjed6s^RYvphu>d!c6Oxvoi4&rNmPZZRn-~?9$yq5aM)b zN9e#JvWJn)G9Ny#K%a#Hzfp~>*6@o8 zVeKDfff>gQFk&%J666yeSR>@(pYwy9&g1=a6dN1pobm*X+5F{q+>XpTruvxxL>)CN zFCrkB_=XMAJ)v}oPt3x!PE;pMK*J*m7cQa@sPE1p*bG|@sYH#jgD6rPoRXTW!9iu>f2sl5Y zxSz)Y4IC%zed7~myE-a)djb4TA8P|z+Uwgx1H#~&IBsI|m-o%iQ#1zWup%rvqY2nAl4*4c?61Yy3CjhvPt-Jm4Bh)TJdE7_dezdz zqbU|&KrWZaE$DZ#3_;TWB|!Lvwh&7DWKx8bNvNCJ8wNTq5TvKN%xbMqQHPFkvjv^q zN4D6zVq5zni_wIzTG0Cx(mqYeFjU0CU8jmJ{|idjIpt}33|BighH$$9A3v(e`uI^; z>K}@^fU1RIZ)Ef6eA_mnloSQK>InyCmBw#$KNvD$zC&9no5aKvTPT;ISaP_A z7XiaYm&gQZy-T@9NVP!-oz1W%4t+8)ajYrtomkNoQ|<3#fQTvYGl<6@6a!@pjL(Jq zhLDrSud#pmgBJT?L__SK;32B3@eD*`C9~)#t90t_u}-u)jH>G}Q@$TLk>!A&dvEt= zViuEwI{2bt$3sB-G-IiM&I)9sp+xGCM3$jVh^m~SCuLr0ge;LZhfU<{N5}UMG?Pl0 z!3ANg)m}OjmMDNjfsH0gX_6?LUpEE>5N{7X5pts(ALNSJC6+z@M1r~-(Pb_@n-HF+ zCTm5iqW!{4qs_ggyR<5gA! zD0+jjB`hd3Dt0%x#a)e%encabk6YZ;$R7meL4l#W8rcmD)*nU3uMP4@wSGep1NiQR zlux`$I-L5^N2yQc!bcG5pT=qm62Ae02CQmt0OG-DOgrBMr^9TBj#qhTJXzjmRU_xp zJI1EvRah1qKmocIsm@a#z`Y26`N|)0Cuc~$vI3R~9u7dQXp=JD8puM%>i|UM<}24t(RY9D z0YqiyD{C=iknsY5WPV9Ns1*KaSFD77HcgNBOF%Net1#6$Tg3Y|W~v`Ijc`p*H$v;p`G&KIH20Fq@~1&^JiZ3YwrBuo3P0Ud|-BXg-Vpvw&CE(2NtR>-t1fMklG0d*Tt)PR0$K#u}K z1yJ)30HJ6=hYjd^sJ6`cDL^u36Br=%t;fUYy34F=R}KzAC@!+=mJ z_ zecjo6zX_AEuRDA1H|2OTYAP>%r?>t~0N{WaWa0-~vf|WcV2-`7n_TaiId9IuP^#>2 ztk85IGu5M682Wl?W}G*&FrQ~(PDF2-nQCSh#+8Mcn}u1Pg}FWpvndM`&A>FYHPIMn z5mbW6_&Lv^j#YL z2SFz1O2evDBz>n91h!X{VSvey0U-&5!aWo+F_eHWd zsi*&*%~yS&Pvf!t%3sFshNPvMTvwfk^b0Zg!p~p#1213qFoX8(!_10K+HNnYPutor zh)$ge&)HxLDS(#@`vWc{wmq$uLm^EH0gB)iWOHET@IH@p8x50&tL zR{ueL#)g*lvuC9-_b>6b)K!X;7<8r1TilCz!QTnevd>%CF^|}Run+L0i^lBszu~+E zy56j-3*Y=p?+Ymc&2fHr=FcvzTgESFO`(Awa2ZgY{vY73T zGQj*v2W_WdBXG#rwBAOP9j|tX{u=uj@+sa92Yu-5E2s%Og!g6Y{ic8wzF7JV3oB)K ze@@^ew!@JRSSy3D@JGPUV@H-CKZ-No(OT1@=!01Hf@4B@{qVIb;qX#6Pmc>31y$GX z?jfoHzLiBk@_;>Q(&Kw&q2crJbSofTAeYdBIKx=(gjPKGNYJnSxSsH2UP{K@dYQc_ndL+*WAR4p}{ca&qL^ zm5sI7+hRuqS|wq*sZV9esMx-M57N+9<-*XA6JPy$l*${U+nm|w;e&o>%&DcvDezB- z2x31XJ`dTSXn;!s`_MWmVe-q&+m`z-MHL%H(jWE~@5~ zsZ0KD(sxqnx02>N3iVZ>g30HL(&`sj1mW07*HHyKQMbS+)m;?C2A73Cn+Q0toIy;_ zC#L5S)6Yz+A$Btp)5$|abo%=QrN06rwe~^fSX-~%Av)n66Fhd!Yeh4djU+VWmNtE`2-=9~TIE})9_*vpc; z({i&XA4&(fqo>$DpvcB;Nv?!QKLmy~kuLxGL0^*lWq5-C_9D!!g)*Utl$sAT?jDwf>yz&iPhR~b#ZO^M_L;!rJO=5#)YCr+F}#z z2bPdVQDVA1)zrl<#fw0sor;rSmYjeCypd?yf@39l9i70(-=ILu=IXz4Iei}iiy|n< z#uXeh!fqx)PU62+FqA&p$;9l5T@G#%kYyu>rUI{iV$0A4pXth?Df);!l(DKDd;6#}bzC$?LJ#;r>)3;Hz z?1??r`Fy~CU6c2_&4N4?`#5Yqz_}quoC++tdbgtoO$nVL{ z7W)<^pFJOq(uKc8_*;d)cKl7j-({#7+!pX-91prAR0as{2;eR^px*$p3*5Va&K8gf zeaSfjqTf)$bQmcf2ILUX^F}CbV3{Fs7h%vwBZYkBDnK%q7$BMU4M4Em2sME+vbuRn zAt0x~T?(j3K>rL#`Vxl#Nxz|eik^!HP_c-&3D9>1^bde863`QXq)+7~K+;!|k3mF< z2t5lB`IYjNQUiCffm;H|CE|4hnk}H80-7bDM*x)y=ruqW3g`eJS?*Ny!IGA1j1-6> zd^z$r;!eR^o*2tYh#H-Q$ZkrAnz)3r$DDG+*N7PD7Z@Zi6QuWLr|!v*wGw|Kj)+Z^ zwn5G*OcgMxZ?#gSdi?E&WK(fkfU!Y_Da=j4bY);}rBoT1UjWkwGE=FZ0_LF%%x+-z zWnlgQOdBMaO7#V$%D|9c!v=1r;)tI#LOc*4{-6tpllfhMIl%ZbQqivWwhYWFVEPSA z_Sl|A$1_nz>iTA5dy;(Sl|CK@L+}fGG$IBmldg@RP`@A~T%jz?Z5f#CZwFASpJ&E7 z<}W3ryfcdCEgH&5MfvQ>!n~D*c|QwtBnvZ5R)(_F%A)bLe&M$q=vM+fE^3nEl7VMt z#GwE>xvO6itjNM#l7(58g=xvcY{fi{yz31&7dEAv8XA4BaHO6u&J@L zw!L+oXZA(neQr}=K7MCetV4Q`1W^{jZC2kZA~ddT?P!wn!*7e=7mDfEYe_ZX8WTUp zEMSP1;^60MMX4>Vje)gVB8r*@l5eJIJ(1EvzcUTz?kdU`zc)c1m}_rXziM3*Aw($s zh%CJjS3qVx9z-F%)~sLcYie(4T}|O2*}JxB)%swYUV*@>_5j{c3`MSLx~^q?D!Nga I$R7m$KTahp^#A|> literal 33854 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 Ya(J8Dnj3iz^_1FGd7-zi*$3YKFFg(8r~m)} diff --git a/source/src/client.cpp b/source/src/client.cpp index ef02f353..ac033cfb 100644 --- a/source/src/client.cpp +++ b/source/src/client.cpp @@ -96,14 +96,11 @@ void connectserv_(const char *servername, const char *serverport = NULL, const c } if(!clienthost) - { - clienthost = enet_host_create(NULL, 2, 0, 0); - if(clienthost) enet_host_channel_limit(clienthost, 3); - } + clienthost = enet_host_create(NULL, 2, 3, 0, 0); if(clienthost) { - connpeer = enet_host_connect(clienthost, &address, 3); + connpeer = enet_host_connect(clienthost, &address, 3, 0); enet_host_flush(clienthost); connmillis = totalmillis; connattempts = 0; diff --git a/source/src/server.cpp b/source/src/server.cpp index bd09e947..424675f2 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -3461,9 +3461,8 @@ void initserver(bool dedicated) { ENetAddress address = { ENET_HOST_ANY, scl.serverport }; if(scl.ip[0] && enet_address_set_host(&address, scl.ip)<0) logline(ACLOG_WARNING, "server ip not resolved!"); - serverhost = enet_host_create(&address, scl.maxclients+1, 0, scl.uprate); + serverhost = enet_host_create(&address, scl.maxclients+1, 3, 0, scl.uprate); if(!serverhost) fatal("could not create server host"); - enet_host_channel_limit(serverhost, 3); loopi(scl.maxclients) serverhost->peers[i].data = (void *)-1; maprot.init(scl.maprot);