Browse Source

Initial revision

eihrul 19 years ago
commit
3010e46b47
36 changed files with 7227 additions and 0 deletions
  1. 993 0
      Doxyfile
  2. 7 0
      LICENSE
  3. 5 0
      Makefile.am
  4. 16 0
      README
  5. 290 0
      api.txt
  6. 53 0
      callbacks.c
  7. 22 0
      configure.in
  8. 117 0
      design.txt
  9. 24 0
      docs/FAQ.dox
  10. 126 0
      docs/design.dox
  11. 37 0
      docs/install.dox
  12. 26 0
      docs/license.dox
  13. 84 0
      docs/mainpage.dox
  14. 349 0
      docs/tutorial.dox
  15. 164 0
      enet.dsp
  16. 391 0
      host.c
  17. 1 0
      include/Makefile.am
  18. 12 0
      include/enet/Makefile.am
  19. 28 0
      include/enet/callbacks.h
  20. 421 0
      include/enet/enet.h
  21. 42 0
      include/enet/list.h
  22. 166 0
      include/enet/protocol.h
  23. 18 0
      include/enet/time.h
  24. 13 0
      include/enet/types.h
  25. 36 0
      include/enet/unix.h
  26. 12 0
      include/enet/utility.h
  27. 42 0
      include/enet/win32.h
  28. 57 0
      list.c
  29. 74 0
      packet.c
  30. 644 0
      peer.c
  31. 1296 0
      protocol.c
  32. 620 0
      pyenet/enet.pyx
  33. 50 0
      pyenet/setup.py
  34. 325 0
      tutorial.txt
  35. 371 0
      unix.c
  36. 295 0
      win32.c

+ 993 - 0
Doxyfile

@@ -0,0 +1,993 @@
+# Doxyfile 1.2.18
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = enet
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = docs
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, 
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en 
+# (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, 
+# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these class will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = NO
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited 
+# members of a class in the documentation of that class as if those members were 
+# ordinary class members. Constructors, destructors and assignment operators of 
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH        = 
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower case letters. If set to YES upper case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# users are adviced to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments  will behave just like the Qt-style comments (thus requiring an 
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# reimplements.
+
+INHERIT_DOCS           = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consist of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 
+# only. Doxygen will then generate output that is more tailored for C. 
+# For instance some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
+# only. Doxygen will then generate output that is more tailored for Java. 
+# For instance namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = . include/enet docs
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp 
+# *.h++ *.idl *.odl
+
+FILE_PATTERNS          = *.c *.h *.dox
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = Tests
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.
+
+INPUT_FILTER           = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output dir.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non empty doxygen will try to run 
+# the html help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the Html help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+, 
+# or Internet explorer 4.0+). Note that for large projects the tree generation 
+# can take a very long time. In such cases it is better to disable this feature. 
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimised for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assigments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_XML           = NO
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed.
+
+PREDEFINED             = FORCE_DOXYGEN
+
+# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse the 
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or 
+# super classes. Setting the tag to NO turns the diagrams off. Note that this 
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is 
+# recommended to install and use dot, since it yield more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermedate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
+
+# The CGI_NAME tag should be the name of the CGI script that 
+# starts the search engine (doxysearch) with the correct parameters. 
+# A script with this name will be generated by doxygen.
+
+CGI_NAME               = search.cgi
+
+# The CGI_URL tag should be the absolute URL to the directory where the 
+# cgi binaries are located. See the documentation of your http daemon for 
+# details.
+
+CGI_URL                = 
+
+# The DOC_URL tag should be the absolute URL to the directory where the 
+# documentation is located. If left blank the absolute path to the 
+# documentation, with file:// prepended to it, will be used.
+
+DOC_URL                = 
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the 
+# documentation is located. If left blank the directory on the local machine 
+# will be used.
+
+DOC_ABSPATH            = 
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary 
+# is installed.
+
+BIN_ABSPATH            = /usr/local/bin/
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to 
+# documentation generated for other projects. This allows doxysearch to search 
+# the documentation for these projects as well.
+
+EXT_DOC_PATHS          = 

+ 7 - 0
LICENSE

@@ -0,0 +1,7 @@
+Copyright (c) 2002 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:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 5 - 0
Makefile.am

@@ -0,0 +1,5 @@
+lib_LIBRARIES = libenet.a
+libenet_a_SOURCES = host.c list.c callbacks.c packet.c peer.c protocol.c unix.c win32.c
+INCLUDES = -Iinclude/
+
+SUBDIRS = include

+ 16 - 0
README

@@ -0,0 +1,16 @@
+Please visit the ENet homepage at http://enet.cubik.org for installation
+and usage instructions.
+
+If you obtained this package from CVS, the quick description on how to build
+is:
+
+# Generate the build system.
+
+aclocal && automake -a -c --foreign && autoconf
+
+# Compile and install the library.
+
+./configure && make && make install
+
+See pyenet/readme.txt for further information on Ling Lo's Python wrapper for
+ENet.

+ 290 - 0
api.txt

@@ -0,0 +1,290 @@
+enet.h - The file that should be included to use the ENet API.
+
+enet_uint8 - unsigned 8 bit integer
+enet_uint16 - unsigned 16 bit integer
+enet_uint32 - unsigned 32 bit integer
+
+typedef struct
+{  
+  enet_uint32 host; 
+  enet_uint16 port; 
+} ENetAddress;
+      
+  Portable internet address structure. The host must be specified in network 
+byte-order, and the port must be in host byte-order. The constant ENET_HOST_ANY
+may be used to specify the default server host.
+
+typedef struct
+{
+  enet_uint32 flags;
+  enet_uint8 * data;
+  size_t dataLength;
+} ENetPacket;
+
+  An ENet data packet that may be sent to or received from a peer. The shown fields
+should only be read and never modified. The data field contains the allocated data
+for the packet. The dataLength fields specifies the length of the allocated data.
+The flags field is either 0 (specifying no flags), or a bitwise-or of any
+combination of the following flags:
+  
+  ENET_PACKET_FLAG_RELIABLE -   
+  
+      Specifies that the packet must be received by the target peer and that resend
+    attempts should be made should delivery of the packet fail.
+
+typedef struct
+{
+  ENetAddress address;
+  void * data;
+  size_t channelCount;
+  enet_uint32 incomingBandwidth;
+  enet_uint32 outgoingBandwidth;
+  enet_uint32 roundTripTime;
+  enet_uint32 packetLoss;
+} ENetPeer;
+
+  An ENet peer which data packets may be sent or received from. No fields should be
+modified unless otherwise specified. The address field contains the internet address 
+of the peer. The data fields may be used to associate any desired data with the peer 
+and may be freely modified. The channelCount field tells the number of channels that
+have been allocated for use to communnicate with the peer. The incomingBandwidth field
+specifies the downstream bandwidth of the client in bytes per second. The 
+outgoingBandwidth field specifies the upstream bandwidth of the client in bytes per
+second. The roundTripTime field tells the mean round trip time from the sending of
+a reliable packet until the receipt of its acknowledgement in milliseconds. The
+packetLoss field tells the mean packet loss of reliable packets as a ratio with
+respect to the constant ENET_PEER_PACKET_LOSS_SCALE.
+
+typedef enum
+{
+   ENET_EVENT_TYPE_NONE,
+   ENET_EVENT_TYPE_CONNECT,
+   ENET_EVENT_TYPE_DISCONNECT,
+   ENET_EVENT_TYPE_RECEIVE
+} ENetEventType;
+
+typedef struct _ENetEvent
+{
+   ENetEventType        type;
+   ENetPeer *           peer;
+   enet_uint8                channelID;
+   ENetPacket *         packet;
+} ENetEvent;
+
+  An ENet event as returned by enet_host_service. The type field contains the type
+of the event, which may be any one of the following:
+  
+  ENET_EVENT_TYPE_NONE - No event occurred within the specified time limit.
+  ENET_EVENT_TYPE_CONNECT - 
+    
+    A connection request initiated by enet_host_connect has completed. The peer field
+  contains the peer which successfully connected.
+
+  ENET_EVENT_TYPE_DISCONNECT -
+    
+    A peer has disconnected. This event is generated on successful completion of a
+  disconnect iniated by enet_peer_disconnect, if a peer has timed out, or if a
+  connection request initialized by enet_host_connect has timed out. The peer field
+  contains the peer which disconnected.
+
+  ENET_EVENT_TYPE_RECEIVE -
+    
+    A packet has been received from a peer. The peer field specifies the peer which
+  send the packet. The channelID field specifies the channel number upon which the
+  packet was received. The packet field contains the packet that was destroyed; this
+  packet must be destroyed with enet_packet_destroy after use.
+
+typedef struct
+{
+  ENetAddress address;
+  enet_uint32 incomingBandwidth;
+  enet_uint32 outgoingBandwidth;
+  ENetPeer * peers;
+  size_t peerCount;
+} ENetHost;
+
+  An ENet host for communicating with peers. No fields should be modified. The address 
+field tells the internet address of the host. The incomingBandwidth field tells the downstream
+bandwidth of the host. The outgoingBandwidth field specifies the upstream bandwidth of the host.
+The peers field contains an array of the peers that have been allocated for this host. The 
+peerCount field specifies the number of peers that have been allocated for this host.
+
+unsigned ENET_HOST_TO_NET_8 (unsigned);
+unsigned ENET_HOST_TO_NET_16 (unsigned);
+unsigned ENET_HOST_TO_NET_32 (unsigned);
+
+  Macros that convert from host byte-order to network byte-order (big
+endian) for unsigned integers of 8 bits, 16 bits, and 32 bits repectively. 
+
+unsigned ENET_NET_TO_HOST_8 (unsigned);
+unsigned ENET_NET_TO_HOST_16 (unsigned);
+unsigned ENET_NET_TO_HOST_32 (unsigned);
+
+  Macros that convert from network byte-order (big endian) to host 
+byte-order for unsigned integers of 8 bits, 16 bits, and 32 bits repectively.
+
+enet_uint32 enet_time_get (void);
+
+  Returns the wall-time in milliseconds. Its initial value is unspecified unless
+otherwise set.
+
+void enet_time_set (enet_uint32);
+
+  Sets the current wall-time in milliseconds.
+
+int enet_initialize (void);
+
+  Initialize ENet for use. Must be called prior to using any functions in ENet.
+Returns 0 on success and -1 on failure.
+
+void enet_deinitialize (void);
+
+  Clean-up ENet after use. Should be called when a program that has initialized
+and used ENet exits.
+
+int enet_address_set_host (ENetAddress * address, const char * hostName);
+
+  Attempts to resolve the host named by the parameter hostName and sets the host
+field in the address parameter if successful. Returns 0 on success and -1 on
+failure.
+
+int enet_address_get_host (const ENetAddress * address, char * hostName, size_t nameLength);
+
+  Attempts to do a reverse lookup of the host field in the address parameter.
+If successful, the name of the host is placed in the string described by
+hostName and nameLength. The host name is always null-delimited and will
+not exceed nameLength in length. Returns 0 on success and -1 on failure.
+
+ENetPacket * enet_packet_create (const void * dataContents, size_t dataLength, enet_uint32 flags);
+
+  Creates a packet that may be sent to a peer. The dataContents parameter
+specifies the initial contents of the packet's data; the packet's data will
+remain uninitialized if dataContents is NULL. The dataLength parameter specifies
+the size of the data that is allocated for this packet. The flags parameter
+specifies flags for this packet as described for the ENetPacket structure.
+Returns the packet on success and NULL on failure.
+
+void enet_packet_destroy (ENetPacket * packet);
+
+  Destroys the packet and deallocates its data.
+
+int enet_packet_resize (ENetPacket * packet, size_t dataLength);
+
+  Attempts to resize the data in the packet to the length specified in the
+dataLength parameter. Returns 0 on success and -1 on failure.
+
+ENetHost * enet_host_create (const ENetAddress * address, size_t peerCount, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth);
+
+  Creates a host for communicating with peers. The address parameter specifies 
+the address at which other peers may connect to this host; if the address parameter
+is NULL, then no peers may connect to the host. The peerCount parameter specifies
+the numbers of peers that should be allocated for the host; this limits the maximum
+number of peers that may connect to this host to peerCount. The incomingBandwidth
+parameter specifies the downstream bandwidth of the host in bytes per second; if
+the incomingBandwidth parameter is 0, ENet will assume the host has unlimited
+downstream bandwidth. The outgoingBandwidth parameter specifies the upstream bandwidth
+of the host in bytes per second; if the outgoingBandwidth parameter is 0, ENet will
+assume the host has unlimited upstream bandwidth. ENet will strategically drop packets
+on specific sides of a connection between hosts to ensure the host's bandwidth is not
+overwhelmed; the bandwidth parameters also determine the window size of a connection
+which limits the amount of reliable packets that may be in transit at any given time.
+Returns the host on success and NULL on failure.
+
+void enet_host_destroy (ENetHost * host);
+
+  Destroys the host and all resources associated with it.
+
+ENetPeer * enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount);
+  
+  Initiates a connection from the host specified in the host parameter to a foreign 
+host whose internet address is specified by the address parameter. The channelCount 
+parameter specifies the number of channels that should be allocated for communicating 
+with the foreign host. Returns a peer representing the foreign host on success and NULL
+on failure. The peer returned will have not completed the connection until enet_host_service
+notifies of an ENET_EVENT_TYPE_CONNECT event for the peer.
+
+int enet_host_service (ENetHost * host, ENetEvent * event, enet_uint32 timeout);
+
+  Waits for events on the host specified by the host parameters and shuttles packets 
+between the host and its peers. The event parameter specifies an event structure
+where event details will be placed if one occurs. The timeout field specifies an
+amount of time in milliseconds that ENet should wait for events. Returns 1 if an
+event occured within the specified time limit, 0 if no event occurred within the 
+time limit, and -1 on failure. This function must be called frequently for adequate
+performance.
+
+void enet_host_flush (ENetHost * host);
+
+  Sends out any queued packets on the host specified in the host parameters to
+the designated peers. This function need only be used in circumstances where one
+wishes to send queued packets earlier than in a call to enet_host_service.
+
+void enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet);
+
+  Queues a packet to be sent to all peers on the host specified in the host parameter
+over the channel number identified by the channelID parameter.
+
+void enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth);
+
+  Adjusts the bandwidth limits of the host specified in the host parameter. The
+incomingBandwidth and outgoingBandwidth parameters are as specified in a call to 
+enet_host_create.
+
+int enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet);
+
+  Queues a packet to be sent to the peer specified by the peer parameter over the
+channel number identified by the channelID parameter. Returns 0 on success and -1
+on failure.
+
+ENetPacket * enet_peer_receive (ENetPeer * peer, enet_uint8 channelID);
+
+  Attempts to dequeue any incoming queued packets on the peer specified by the peer
+parameter on the channel number identified by the channelID parameter. Returns a packet
+if one is available and NULL if there are no available incoming queued packets.
+
+void enet_peer_ping (ENetPeer * peer);
+  
+  Sends a ping request to the peer specified by the peer parameter. Ping requests factor
+into the mean round trip time as designated by the roundTripTime field in the ENetPeer
+structure. ENet automatically pings all connected peer at an interval, however, this 
+function may be called to ensure more frequent ping requests.
+
+void enet_peer_reset (ENetPeer * peer);
+
+  Forcefully disconnects the peer specified by the peer parameter. The foreign host 
+represented by the peer is not notified of the disconnection and so will timeout on its
+connection to the local host.
+
+void enet_peer_disconnect (ENetPeer * peer);
+
+  Request a disconnection from the peer specified by the peer parameter. An
+ENET_EVENT_DISCONNECT event will be generated by enet_host_service once the
+disconnection is complete.
+
+void enet_peer_throttle_configure (ENetPeer * peer, enet_uint32 interval, enet_uint32 acceleration, enet_uint32 deceleration);
+
+  Configures throttle parameter for the peer specified by the peer parameter.
+Unreliable packets are dropped by ENet in response to the varying conditions of
+the internet connection to the peer. The throttle represents a probability that
+an unreliable packet should not be dropped and thus sent by ENet to the peer.
+The lowest mean round trip time from the sending of a reliable packet to the 
+receipt of its acknowledgement is measured over an amount of time specified 
+by the interval parameter in milliseconds; the constant ENET_PEER_PACKET_THROTTLE_INTERVAL
+is the default value for this parameter. If a measured round trip time happens 
+to be signifigantly less than the mean round trip time measured over the interval, 
+then the throttle probability is increased to allow more traffic by an amount
+specified in the acceleration parameter which is in ratio to the
+ENET_PEER_PACKET_THROTTLE_SCALE constant. If a measured round trip time happens
+to be signifigantly greater than the mean round trip time measured over the interval,
+then the throttle probability is decreased to limit traffic by an amount specified
+in the deceleration parameter which is in ratio to the ENET_PEER_PACKET_THROTTLE_SCALE
+constant. When the throttle has a value of ENET_PEER_PACKET_THROTTLE_SCALE, no unreliable
+packets are dropped by ENET, and so 100% of all unreliable packets will be sent. When the 
+throttle has a value of 0, all unreliable packets are dropped by ENet, and so 0% of all
+unreliable packets will be sent. Intermediate values for the throttle represent intermediate
+probabilities between 0% and 100% of unreliable packets being sent. The bandwidth limits
+of the local and foreign host are taken into account to determine a sensible limit for
+the throttle probability above which it should not raise even in the best of conditions.
+
+
+

+ 53 - 0
callbacks.c

@@ -0,0 +1,53 @@
+/** 
+ @file callbacks.c
+ @brief ENet callback functions
+*/
+#define ENET_BUILDING_LIB 1
+#include "enet/enet.h"
+
+static ENetCallbacks callbacks = { malloc, free, rand };
+
+int
+enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits)
+{
+   if (version != ENET_VERSION)
+     return -1;
+
+   if (inits -> malloc != NULL || inits -> free != NULL)
+   {
+      if (inits -> malloc == NULL || inits -> free == NULL)
+        return -1;
+
+      callbacks.malloc = inits -> malloc;
+      callbacks.free = inits -> free;
+   }
+      
+   if (inits -> rand != NULL)
+     callbacks.rand = inits -> rand;
+
+   return enet_initialize ();
+}
+           
+void *
+enet_malloc (size_t size)
+{
+   void * memory = callbacks.malloc (size);
+
+   if (memory == NULL)
+     abort ();
+
+   return memory;
+}
+
+void
+enet_free (void * memory)
+{
+   callbacks.free (memory);
+}
+
+int
+enet_rand (void)
+{
+   return callbacks.rand ();
+}
+

+ 22 - 0
configure.in

@@ -0,0 +1,22 @@
+AC_INIT(libenet, 11-3-2003)
+AM_INIT_AUTOMAKE(libenet.a, 11-3-2003)
+
+AC_PROG_CC
+AC_PROG_RANLIB
+
+AC_CHECK_FUNC(gethostbyaddr_r, [AC_DEFINE(HAS_GETHOSTBYADDR_R)])
+AC_CHECK_FUNC(gethostbyname_r, [AC_DEFINE(HAS_GETHOSTBYNAME_R)])
+AC_CHECK_FUNC(poll, [AC_DEFINE(HAS_POLL)])
+AC_CHECK_FUNC(fcntl, [AC_DEFINE(HAS_FCNTL)])
+
+AC_CHECK_MEMBER(struct msghdr.msg_flags, [AC_DEFINE(HAS_MSGHDR_FLAGS)], , [#include <sys/socket.h>])
+
+AC_CHECK_TYPE(socklen_t, [AC_DEFINE(HAS_SOCKLEN_T)], , 
+              #include <sys/types.h>
+              #include <sys/socket.h>
+)
+
+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_OUTPUT([Makefile include/Makefile include/enet/Makefile])

+ 117 - 0
design.txt

@@ -0,0 +1,117 @@
+* 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).
+
+

+ 24 - 0
docs/FAQ.dox

@@ -0,0 +1,24 @@
+/**
+ @page FAQ Frequently Answered Questions
+
+@section Q1 Is ENet thread safe?
+
+ENet does not use any significant global variables, the vast majority
+of state is encapsulated in the ENetHost structure.  As such, as long
+as the application guards access to this structure, then ENet should
+operate fine in a multithreaded environment.
+   
+@section Q2 Isn't ENet just re-inventing TCP?!  What's the point?
+
+In a perfect world, that would be true.  But as many have found, using
+TCP either in lieu of or in conjunction with UDP can lead to all kinds
+of nightmares.  TCP is a good, solid protocol, however it simply isn't
+up to the task of real-time games.  Too much of TCP's implementation
+dictates a policy that isn't practical for games.  If you want to use
+TCP, then do so -- this library is for people that either don't want
+to use TCP or have tried and ended up being discouraged with the
+performance.
+
+*/
+
+

+ 126 - 0
docs/design.dox

@@ -0,0 +1,126 @@
+/**
+@page Features Features and Architecture
+
+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.
+
+@section CM 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.
+
+@section Sequencing 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.
+
+@section Channels 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.
+
+@section Reliability 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.
+
+@section FaR 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.
+
+@section Aggregation 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.
+
+@section Adaptability 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.
+
+@section Portability 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.  ENet makes no assumptions about the underlying
+platform's endianess or word size.
+
+@section Freedom 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).
+
+*/
+

+ 37 - 0
docs/install.dox

@@ -0,0 +1,37 @@
+/**
+@page Installation Installation
+
+ENet should be trivially simple to integrate with most applications.
+First, make sure you download the latest source distribution here @ref
+SourceDistro.
+
+@section Unix Unix-like Operating Systems
+
+[to be completed]
+
+@subsection SolarisBSD Solaris and BSD
+
+When building ENet under Solaris, you must specify the -lsocket and
+-lnsl parameters to your compiler to ensure that the sockets library
+is linked in.
+
+@section Windows Microsoft Windows
+
+Using MSVC 6 under Windows simply drag all the ENet source files into
+your main project or, better yet, create a new static library project
+and make your executable dependent (Project|Dependencies) on ENet.
+There is also an enet.dsp provided.
+
+You will have to link to the Winsock2 libraries, so make sure to add
+ws2_32.lib to your library list (Project Settings | Link |
+Object/library modules).
+
+@subsection DLL DLL
+
+If you wish to build ENet as a DLL you must first define ENET_DLL
+within the project (Project Settings | C/C++ | Preprocessor |
+Preprocessor definitions) or, more invasively, simply define ENET_DLL
+at the top of enet.h.
+
+*/
+

+ 26 - 0
docs/license.dox

@@ -0,0 +1,26 @@
+/**
+ @page License License
+ 
+Copyright (c) 2002 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:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+

+ 84 - 0
docs/mainpage.dox

@@ -0,0 +1,84 @@
+/** @mainpage enet
+ <center>http://enet.cubik.org</center>
+ <hr>
+
+ENet's purpose is to provide a relatively thin, simple and robust
+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.
+
+@ref Features
+
+@ref SourceDistro
+
+@ref Installation
+
+@ref Tutorial
+
+@ref MailingList
+
+@ref Extending
+
+@ref FAQ
+
+@ref License
+
+ */
+
+/**
+@page SourceDistro Source Distribution
+
+You can retrieve the source to ENet by downloading it in either .zip
+form, .tar.gz form, or accessing the cvs distribution directly.
+
+The most recent CVS can be downloaded <a href="http://enet.cubik.org/download/enet.tar.gz">here</a>
+
+To access ENet via anonymous CVS, you must use the CVSROOT
+:pserver:anoncvs\@sferik.cubik.org:/home/enet/cvsroot with an empty
+password.
+
+@code
+	$ cvs -z3 -d :pserver:anoncvs@sferik.cubik.org:/home/enet/cvsroot login
+@endcode
+	Hit the return key when prompted for a password.
+@code
+	$ cvs -z3 -d :pserver:anoncvs@sferik.cubik.org:/home/enet/cvsroot co -l .
+	$ cvs -z3 co enet
+@endcode
+
+This will create a CVS directory in the current directory, and with
+the second command will proceed to check the enet module out of CVS.
+Any problems with CVS access or request for write access should be
+sent via email to @ref MailingList.
+
+*/
+
+/**
+@page Extending Extending and Modifying ENet
+
+Since ENet is distributed in source form, you can easily extend and
+modify it to suit your needs.  For example, some users prefer to use
+their own memory management routines and can thus replace the
+appropriate functions in memory.c
+
+*/
+
+/**
+@page MailingList ENet Related Mailing Lists
+
+The <a
+href="http://lists.cubik.org/mailman/listinfo/enet-discuss">
+enet-discuss</a> list is for discussion of ENet, including bug reports
+or feature requests.
+
+The CVS commits are also sent to <a
+href="http://lists.cubik.org/mailman/listinfo/enet-cvs">enet-cvs</a>,
+so feel free to subscribe if you want to keep up with the latest
+developments.
+
+*/

+ 349 - 0
docs/tutorial.dox

@@ -0,0 +1,349 @@
+/**
+@page Tutorial Tutorial
+
+@ref Initialization
+
+@ref CreateServer
+
+@ref CreateClient
+
+@ref ManageHost
+
+@ref SendingPacket
+
+@ref Disconnecting
+
+@ref Connecting
+
+@section Initialization Initialization
+
+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.
+
+@code
+int 
+main (int argc, char ** argv) 
+{
+    if (enet_initialize () != 0)
+    {
+        fprintf (stderr, "An error occurred while initializing ENet.\n");
+        return EXIT_FAILURE;
+    }
+    atexit (enet_deinitialize);
+    ...
+    ...
+    ...
+}
+@endcode
+        
+@section CreateServer 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.
+
+@code
+    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);
+@endcode
+
+@section CreateClient 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.
+
+@code
+    ENetHost * client;
+
+    client = 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);
+@endcode
+
+@section ManageHost 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.
+
+@code
+    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;
+        }
+    }
+    ...
+    ...
+    ...
+@endcode
+
+@section SendingPacket 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.
+
+@code
+    /* 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);
+@endcode
+
+@section Disconnecting 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.
+
+@code
+    ENetEvent event;
+    
+    enet_peer_disconnect (& client -> peers [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]);
+    ...
+    ...
+    ...
+@endcode
+
+@section Connecting Connecting to an ENet host
+
+A connection to a foreign 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.
+
+@code
+    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.");
+    }
+    ...
+    ...
+    ...
+@endcode
+*/

+ 164 - 0
enet.dsp

@@ -0,0 +1,164 @@
+# Microsoft Developer Studio Project File - Name="enet" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=enet - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "enet.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "enet.mak" CFG="enet - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "enet - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "enet - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "enet - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+MTL=midl.exe
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /W3 /O2 /I "include/enet" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "enet - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "\temp\enet\Debug"
+# PROP Intermediate_Dir "\temp\enet\Debug"
+# PROP Target_Dir ""
+MTL=midl.exe
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /G6 /MTd /W3 /ZI /Od /I "include/enet" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF 
+
+# Begin Target
+
+# Name "enet - Win32 Release"
+# Name "enet - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\host.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\list.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\callbacks.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\packet.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\peer.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\protocol.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\unix.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\win32.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\include\enet\enet.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\enet\list.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\enet\callbacks.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\enet\protocol.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\enet\time.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\enet\types.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\enet\unix.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\enet\utility.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\enet\win32.h
+# End Source File
+# End Group
+# End Target
+# End Project

+ 391 - 0
host.c

@@ -0,0 +1,391 @@
+/** 
+ @file host.c
+ @brief ENet host management functions
+*/
+#define ENET_BUILDING_LIB 1
+#include <string.h>
+#include "enet/enet.h"
+
+/** @defgroup host ENet host functions
+    @{
+*/
+
+/** Creates a host for communicating to peers.  
+
+    @param address   the address at which other peers may connect to this host.  If NULL, then no peers may connect to the host.
+    @param peerCount the maximum number of peers that should be allocated for the host.
+    @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
+    @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
+
+    @returns the host on success and NULL on failure
+
+    @remarks ENet will strategically drop packets on specific sides of a connection between hosts
+    to ensure the host's bandwidth is not overwhelmed.  The bandwidth parameters also determine
+    the window size of a connection which limits the amount of reliable packets that may be in transit
+    at any given time.
+*/
+ENetHost *
+enet_host_create (const ENetAddress * address, size_t peerCount, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
+{
+    ENetHost * host = (ENetHost *) enet_malloc (sizeof (ENetHost));
+    ENetPeer * currentPeer;
+
+    host -> peers = (ENetPeer *) enet_malloc (peerCount * sizeof (ENetPeer));
+    memset (host -> peers, 0, peerCount * sizeof (ENetPeer));
+
+    host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM, address);
+    if (host -> socket == ENET_SOCKET_NULL)
+    {
+       enet_free (host -> peers);
+       enet_free (host);
+
+       return NULL;
+    }
+
+    if (address != NULL)
+      host -> address = * address;
+
+    host -> incomingBandwidth = incomingBandwidth;
+    host -> outgoingBandwidth = outgoingBandwidth;
+    host -> bandwidthThrottleEpoch = 0;
+    host -> recalculateBandwidthLimits = 0;
+    host -> mtu = ENET_HOST_DEFAULT_MTU;
+    host -> peerCount = peerCount;
+    host -> lastServicedPeer = host -> peers;
+    host -> commandCount = 0;
+    host -> bufferCount = 0;
+    host -> receivedAddress.host = ENET_HOST_ANY;
+    host -> receivedAddress.port = 0;
+    host -> receivedDataLength = 0;
+     
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+       currentPeer -> host = host;
+       currentPeer -> incomingPeerID = currentPeer - host -> peers;
+       currentPeer -> data = NULL;
+
+       enet_list_clear (& currentPeer -> acknowledgements);
+       enet_list_clear (& currentPeer -> sentReliableCommands);
+       enet_list_clear (& currentPeer -> sentUnreliableCommands);
+       enet_list_clear (& currentPeer -> outgoingReliableCommands);
+       enet_list_clear (& currentPeer -> outgoingUnreliableCommands);
+
+       enet_peer_reset (currentPeer);
+    }
+ 
+    return host;
+}
+
+/** Destroys the host and all resources associated with it.
+    @param host pointer to the host to destroy
+*/
+void
+enet_host_destroy (ENetHost * host)
+{
+    ENetPeer * currentPeer;
+
+    enet_socket_destroy (host -> socket);
+
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+       enet_peer_reset (currentPeer);
+    }
+
+    enet_free (host -> peers);
+    enet_free (host);
+}
+
+/** Initiates a connection to a foreign host.
+    @param host host seeking the connection
+    @param address destination for the connection
+    @param channelCount number of channels to allocate
+    @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)
+{
+    ENetPeer * currentPeer;
+    ENetChannel * channel;
+    ENetProtocol command;
+
+    if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
+      channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
+    else
+    if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
+      channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
+
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+       if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED)
+         break;
+    }
+
+    if (currentPeer >= & host -> peers [host -> peerCount])
+      return NULL;
+
+    currentPeer -> state = ENET_PEER_STATE_CONNECTING;
+    currentPeer -> address = * address;
+    currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel));
+    currentPeer -> channelCount = channelCount;
+    currentPeer -> challenge = (enet_uint32) enet_rand ();
+
+    if (host -> outgoingBandwidth == 0)
+      currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+    else
+      currentPeer -> windowSize = (host -> outgoingBandwidth /
+                                    ENET_PEER_WINDOW_SIZE_SCALE) * 
+                                      ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+    if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+      currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+    else
+    if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+      currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+         
+    for (channel = currentPeer -> channels;
+         channel < & currentPeer -> channels [channelCount];
+         ++ channel)
+    {
+        channel -> outgoingReliableSequenceNumber = 0;
+        channel -> outgoingUnreliableSequenceNumber = 0;
+        channel -> incomingReliableSequenceNumber = 0;
+        channel -> incomingUnreliableSequenceNumber = 0;
+
+        enet_list_clear (& channel -> incomingReliableCommands);
+        enet_list_clear (& channel -> incomingUnreliableCommands);
+    }
+        
+    command.header.command = ENET_PROTOCOL_COMMAND_CONNECT;
+    command.header.channelID = 0xFF;
+    command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
+    command.header.commandLength = sizeof (ENetProtocolConnect);
+    command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID);
+    command.connect.mtu = ENET_HOST_TO_NET_16 (currentPeer -> mtu);
+    command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize);
+    command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount);
+    command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth);
+    command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
+    command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval);
+    command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration);
+    command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration);
+    
+    enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0);
+
+    return currentPeer;
+}
+
+/** Queues a packet to be sent to all peers associated with the host.
+    @param host host on which to broadcast the packet
+    @param channelID channel on which to broadcast
+    @param packet packet to broadcast
+*/
+void
+enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet)
+{
+    ENetPeer * currentPeer;
+
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+       if (currentPeer -> state != ENET_PEER_STATE_CONNECTED)
+         continue;
+
+       enet_peer_send (currentPeer, channelID, packet);
+    }
+
+    if (packet -> referenceCount == 0)
+      enet_packet_destroy (packet);
+}
+
+/** Adjusts the bandwidth limits of a host.
+    @param host host to adjust
+    @param incomingBandwidth new incoming bandwidth
+    @param outgoingBandwidth new outgoing bandwidth
+    @remarks the incoming and outgoing bandwidth parameters are identical in function to those
+    specified in enet_host_create().
+*/
+void
+enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
+{
+    host -> incomingBandwidth = incomingBandwidth;
+    host -> outgoingBandwidth = outgoingBandwidth;
+    host -> recalculateBandwidthLimits = 1;
+}
+
+void
+enet_host_bandwidth_throttle (ENetHost * host)
+{
+    enet_uint32 timeCurrent = enet_time_get (),
+           elapsedTime = timeCurrent - host -> bandwidthThrottleEpoch,
+           peersTotal = 0,
+           dataTotal = 0,
+           peersRemaining,
+           bandwidth,
+           throttle = 0,
+           bandwidthLimit = 0;
+    int needsAdjustment;
+    ENetPeer * peer;
+    ENetProtocol command;
+
+    if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
+      return;
+
+    for (peer = host -> peers;
+         peer < & host -> peers [host -> peerCount];
+         ++ peer)
+    {
+        if (peer -> state != ENET_PEER_STATE_CONNECTED)
+          continue;
+
+        ++ peersTotal;
+        dataTotal += peer -> outgoingDataTotal;
+    }
+
+    if (peersTotal == 0)
+      return;
+
+    peersRemaining = peersTotal;
+    needsAdjustment = 1;
+
+    if (host -> outgoingBandwidth == 0)
+      bandwidth = ~0;
+    else
+      bandwidth = (host -> outgoingBandwidth * elapsedTime) / 1000;
+
+    while (peersRemaining > 0 && needsAdjustment != 0)
+    {
+        needsAdjustment = 0;
+        
+        if (dataTotal < bandwidth)
+          throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
+        else
+          throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
+
+        for (peer = host -> peers;
+             peer < & host -> peers [host -> peerCount];
+             ++ peer)
+        {
+            enet_uint32 peerBandwidth;
+            
+            if (peer -> state != ENET_PEER_STATE_CONNECTED ||
+                peer -> incomingBandwidth == 0 ||
+                peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
+              continue;
+
+            peerBandwidth = (peer -> incomingBandwidth * elapsedTime) / 1000;
+            if ((throttle * peer -> outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth)
+              continue;
+
+            peer -> packetThrottleLimit = (peerBandwidth * 
+                                            ENET_PEER_PACKET_THROTTLE_SCALE) / peer -> outgoingDataTotal;
+            
+            if (peer -> packetThrottleLimit == 0)
+              peer -> packetThrottleLimit = 1;
+            
+            if (peer -> packetThrottle > peer -> packetThrottleLimit)
+              peer -> packetThrottle = peer -> packetThrottleLimit;
+
+            peer -> outgoingBandwidthThrottleEpoch = timeCurrent;
+
+            
+            needsAdjustment = 1;
+            -- peersRemaining;
+            bandwidth -= peerBandwidth;
+            dataTotal -= peerBandwidth;
+        }
+    }
+
+    if (peersRemaining > 0)
+    for (peer = host -> peers;
+         peer < & host -> peers [host -> peerCount];
+         ++ peer)
+    {
+        if (peer -> state != ENET_PEER_STATE_CONNECTED ||
+            peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
+          continue;
+
+        peer -> packetThrottleLimit = throttle;
+
+        if (peer -> packetThrottle > peer -> packetThrottleLimit)
+          peer -> packetThrottle = peer -> packetThrottleLimit;
+    }
+    
+    if (host -> recalculateBandwidthLimits)
+    {
+       host -> recalculateBandwidthLimits = 0;
+
+       peersRemaining = peersTotal;
+       bandwidth = host -> incomingBandwidth;
+       needsAdjustment = 1;
+
+       if (bandwidth == 0)
+         bandwidthLimit = 0;
+       else
+       while (peersRemaining > 0 && needsAdjustment != 0)
+       {
+           needsAdjustment = 0;
+           bandwidthLimit = bandwidth / peersRemaining;
+
+           for (peer = host -> peers;
+                peer < & host -> peers [host -> peerCount];
+                ++ peer)
+           {
+               if (peer -> state != ENET_PEER_STATE_CONNECTED ||
+                   peer -> incomingBandwidthThrottleEpoch == timeCurrent)
+                 continue;
+
+               if (peer -> outgoingBandwidth > 0 &&
+                   bandwidthLimit > peer -> outgoingBandwidth)
+                 continue;
+
+               peer -> incomingBandwidthThrottleEpoch = timeCurrent;
+ 
+               needsAdjustment = 1;
+               -- peersRemaining;
+               bandwidth -= peer -> outgoingBandwidth;
+           }
+       }
+
+       for (peer = host -> peers;
+            peer < & host -> peers [host -> peerCount];
+            ++ peer)
+       {
+           if (peer -> state != ENET_PEER_STATE_CONNECTED)
+             continue;
+
+           command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT;
+           command.header.channelID = 0xFF;
+           command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
+           command.header.commandLength = sizeof (ENetProtocolBandwidthLimit);
+           command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
+
+           if (peer -> incomingBandwidthThrottleEpoch == timeCurrent)
+             command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth);
+           else
+             command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit);
+
+           enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+       } 
+    }
+
+    host -> bandwidthThrottleEpoch = timeCurrent;
+
+    for (peer = host -> peers;
+         peer < & host -> peers [host -> peerCount];
+         ++ peer)
+    {
+        peer -> incomingDataTotal = 0;
+        peer -> outgoingDataTotal = 0;
+    }
+}
+    
+/** @} */

+ 1 - 0
include/Makefile.am

@@ -0,0 +1 @@
+SUBDIRS = enet

+ 12 - 0
include/enet/Makefile.am

@@ -0,0 +1,12 @@
+libenetincludedir = $(includedir)/enet
+libenetinclude_HEADERS = \
+	types.h \
+	list.h \
+	utility.h \
+	time.h \
+	callbacks.h \
+	unix.h \
+	win32.h \
+	protocol.h \
+	enet.h
+	

+ 28 - 0
include/enet/callbacks.h

@@ -0,0 +1,28 @@
+/** 
+ @file  callbacks.h
+ @brief ENet callbacks
+*/
+#ifndef __ENET_CALLBACKS_H__
+#define __ENET_CALLBACKS_H__
+
+#include <stdlib.h>
+
+typedef struct
+{
+    void * (ENET_CALLBACK * malloc) (size_t size);
+    void (ENET_CALLBACK * free) (void * memory);
+    int (ENET_CALLBACK * rand) (void);
+} ENetCallbacks;
+
+/** @defgroup callbacks ENet internal callbacks
+    @{
+    @ingroup private
+*/
+extern void * enet_malloc (size_t);
+extern void   enet_free (void *);
+extern int    enet_rand (void);
+
+/** @} */
+
+#endif /* __ENET_CALLBACKS_H__ */
+

+ 421 - 0
include/enet/enet.h

@@ -0,0 +1,421 @@
+/** 
+ @file  enet.h
+ @brief ENet public header file
+*/
+#ifndef __ENET_ENET_H__
+#define __ENET_ENET_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdlib.h>
+
+
+#ifdef WIN32
+#include "enet/win32.h"
+#else
+#include "enet/unix.h"
+#endif
+
+#include "enet/types.h"
+#include "enet/protocol.h"
+#include "enet/list.h"
+#include "enet/callbacks.h"
+
+typedef enum
+{
+   ENET_VERSION = 1
+} ENetVersion;
+
+typedef enum
+{
+   ENET_SOCKET_TYPE_STREAM   = 1,
+   ENET_SOCKET_TYPE_DATAGRAM = 2
+} ENetSocketType;
+
+typedef enum
+{
+   ENET_SOCKET_WAIT_NONE    = 0,
+   ENET_SOCKET_WAIT_SEND    = (1 << 0),
+   ENET_SOCKET_WAIT_RECEIVE = (1 << 1)
+} ENetSocketWait;
+
+enum
+{
+   ENET_HOST_ANY = 0
+};
+
+/**
+ * Portable internet address structure. 
+ *
+ * The host must be specified in network byte-order, and the port must be in host 
+ * byte-order. The constant ENET_HOST_ANY may be used to specify the default 
+ * server host.
+ */
+typedef struct _ENetAddress
+{
+   enet_uint32 host;  /**< may use ENET_HOST_ANY to specify default server host */
+   enet_uint16 port;
+} ENetAddress;
+
+/**
+ * Packet flag bit constants.
+ *
+ * The host must be specified in network byte-order, and the port must be in
+ * host byte-order. The constant ENET_HOST_ANY may be used to specify the
+ * default server host.
+ 
+   @sa ENetPacket
+*/
+typedef enum
+{
+   /** packet must be received by the target peer and resend attempts should be
+     * made until the packet is delivered */
+   ENET_PACKET_FLAG_RELIABLE    = (1 << 0),
+   /** packet will not be sequenced with other packets
+     * not supported for reliable packets
+     */
+   ENET_PACKET_FLAG_UNSEQUENCED = (1 << 1)
+} ENetPacketFlag;
+
+/**
+ * ENet packet structure.
+ *
+ * An ENet data packet that may be sent to or received from a peer. The shown 
+ * fields should only be read and never modified. The data field contains the 
+ * allocated data for the packet. The dataLength fields specifies the length 
+ * of the allocated data.  The flags field is either 0 (specifying no flags), 
+ * or a bitwise-or of any combination of the following flags:
+ *
+ *    ENET_PACKET_FLAG_RELIABLE - packet must be received by the ta
+
+   @sa ENetPacketFlag
+ */
+typedef struct _ENetPacket
+{
+   size_t               referenceCount;  /**< internal use only */
+   enet_uint32          flags;           /**< bitwise or of ENetPacketFlag constants */
+   enet_uint8 *         data;            /**< allocated data for packet */
+   size_t               dataLength;      /**< length of data */
+} ENetPacket;
+
+typedef struct _ENetAcknowledgement
+{
+   ENetListNode acknowledgementList;
+   enet_uint32  sentTime;
+   ENetProtocol command;
+} ENetAcknowledgement;
+
+typedef struct _ENetOutgoingCommand
+{
+   ENetListNode outgoingCommandList;
+   enet_uint32  reliableSequenceNumber;
+   enet_uint32  unreliableSequenceNumber;
+   enet_uint32  sentTime;
+   enet_uint32  roundTripTimeout;
+   enet_uint32  roundTripTimeoutLimit;
+   enet_uint32  fragmentOffset;
+   enet_uint16  fragmentLength;
+   ENetProtocol command;
+   ENetPacket * packet;
+} ENetOutgoingCommand;
+
+typedef struct _ENetIncomingCommand
+{  
+   ENetListNode     incomingCommandList;
+   enet_uint32      reliableSequenceNumber;
+   enet_uint32      unreliableSequenceNumber;
+   ENetProtocol     command;
+   enet_uint32      fragmentCount;
+   enet_uint32      fragmentsRemaining;
+   enet_uint32 *    fragments;
+   ENetPacket *     packet;
+} ENetIncomingCommand;
+
+typedef enum
+{
+   ENET_PEER_STATE_DISCONNECTED                = 0,
+   ENET_PEER_STATE_CONNECTING                  = 1,
+   ENET_PEER_STATE_ACKNOWLEDGING_CONNECT       = 2,
+   ENET_PEER_STATE_CONNECTED                   = 3,
+   ENET_PEER_STATE_DISCONNECTING               = 4,
+   ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT    = 5,
+   ENET_PEER_STATE_ZOMBIE                      = 6
+} ENetPeerState;
+
+#ifndef ENET_BUFFER_MAXIMUM
+#define ENET_BUFFER_MAXIMUM (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS)
+#endif
+
+enum
+{
+   ENET_HOST_RECEIVE_BUFFER_SIZE          = 256 * 1024,
+   ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL  = 1000,
+   ENET_HOST_DEFAULT_MTU                  = 1400,
+
+   ENET_PEER_DEFAULT_ROUND_TRIP_TIME      = 500,
+   ENET_PEER_DEFAULT_PACKET_THROTTLE      = 32,
+   ENET_PEER_PACKET_THROTTLE_SCALE        = 32,
+   ENET_PEER_PACKET_THROTTLE_COUNTER      = 7, 
+   ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2,
+   ENET_PEER_PACKET_THROTTLE_DECELERATION = 2,
+   ENET_PEER_PACKET_THROTTLE_INTERVAL     = 5000,
+   ENET_PEER_PACKET_LOSS_SCALE            = (1 << 16),
+   ENET_PEER_PACKET_LOSS_INTERVAL         = 10000,
+   ENET_PEER_WINDOW_SIZE_SCALE            = 64 * 1024,
+   ENET_PEER_TIMEOUT_LIMIT                = 32,
+   ENET_PEER_PING_INTERVAL                = 500,
+   ENET_PEER_UNSEQUENCED_WINDOW_SIZE      = 4 * 32
+};
+
+typedef struct _ENetChannel
+{
+   enet_uint32  outgoingReliableSequenceNumber;
+   enet_uint32  outgoingUnreliableSequenceNumber;
+   enet_uint32  incomingReliableSequenceNumber;
+   enet_uint32  incomingUnreliableSequenceNumber;
+   ENetList     incomingReliableCommands;
+   ENetList     incomingUnreliableCommands;
+} ENetChannel;
+
+/**
+ * An ENet peer which data packets may be sent or received from. 
+ *
+ * No fields should be modified unless otherwise specified. 
+ */
+typedef struct _ENetPeer
+{ 
+   struct _ENetHost * host;
+   enet_uint16   outgoingPeerID;
+   enet_uint16   incomingPeerID;
+   enet_uint32   challenge;
+   ENetAddress   address;            /**< Internet address of the peer */
+   void *        data;               /**< Application private data, may be freely modified */
+   ENetPeerState state;
+   ENetChannel * channels;
+   size_t        channelCount;       /**< Number of channels allocated for communication with peer */
+   enet_uint32   incomingBandwidth;  /**< Downstream bandwidth of the client in bytes/second */
+   enet_uint32   outgoingBandwidth;  /**< Upstream bandwidth of the client in bytes/second */
+   enet_uint32   incomingBandwidthThrottleEpoch;
+   enet_uint32   outgoingBandwidthThrottleEpoch;
+   enet_uint32   incomingDataTotal;
+   enet_uint32   outgoingDataTotal;
+   enet_uint32   lastSendTime;
+   enet_uint32   lastReceiveTime;
+   enet_uint32   nextTimeout;
+   enet_uint32   packetLossEpoch;
+   enet_uint32   packetsSent;
+   enet_uint32   packetsLost;
+   enet_uint32   packetLoss;          /**< mean packet loss of reliable packets as a ratio with respect to the constant ENET_PEER_PACKET_LOSS_SCALE */
+   enet_uint32   packetLossVariance;
+   enet_uint32   packetThrottle;
+   enet_uint32   packetThrottleLimit;
+   enet_uint32   packetThrottleCounter;
+   enet_uint32   packetThrottleEpoch;
+   enet_uint32   packetThrottleAcceleration;
+   enet_uint32   packetThrottleDeceleration;
+   enet_uint32   packetThrottleInterval;
+   enet_uint32   lastRoundTripTime;
+   enet_uint32   lowestRoundTripTime;
+   enet_uint32   lastRoundTripTimeVariance;
+   enet_uint32   highestRoundTripTimeVariance;
+   enet_uint32   roundTripTime;            /**< mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgement */
+   enet_uint32   roundTripTimeVariance;
+   enet_uint16   mtu;
+   enet_uint32   windowSize;
+   enet_uint32   reliableDataInTransit;
+   enet_uint32   outgoingReliableSequenceNumber;
+   ENetList      acknowledgements;
+   ENetList      sentReliableCommands;
+   ENetList      sentUnreliableCommands;
+   ENetList      outgoingReliableCommands;
+   ENetList      outgoingUnreliableCommands;
+   enet_uint32   incomingUnsequencedGroup;
+   enet_uint32   outgoingUnsequencedGroup;
+   enet_uint32   unsequencedWindow [ENET_PEER_UNSEQUENCED_WINDOW_SIZE / 32]; 
+} ENetPeer;
+
+/** An ENet host for communicating with peers.
+  *
+  * No fields should be modified.
+
+    @sa enet_host_create()
+    @sa enet_host_destroy()
+    @sa enet_host_connect()
+    @sa enet_host_service()
+    @sa enet_host_flush()
+    @sa enet_host_broadcast()
+    @sa enet_host_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 */
+   ENetPeer *         lastServicedPeer;
+   size_t             packetSize;
+   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;
+} ENetHost;
+
+/**
+ * An ENet event type, as specified in @ref ENetEvent.
+ */
+typedef enum
+{
+   /** no event occurred within the specified time limit */
+   ENET_EVENT_TYPE_NONE       = 0,  
+
+   /** a connection request initiated by enet_host_connect has completed.  
+     * The peer field contains the peer which successfully connected. 
+     */
+   ENET_EVENT_TYPE_CONNECT    = 1,  
+
+   /** a peer has disconnected.  This event is generated on a successful 
+     * completion of a disconnect initiated by enet_pper_disconnect, if 
+     * a peer has timed out, or if a connection request intialized by 
+     * enet_host_connect has timed out.  The peer field contains the peer 
+     * which disconnected. 
+     */
+   ENET_EVENT_TYPE_DISCONNECT = 2,  
+
+   /** a packet has been received from a peer.  The peer field specifies the
+     * peer which sent the packet.  The channelID field specifies the channel
+     * number upon which the packet was received.  The packet field contains
+     * the packet that was received; this packet must be destroyed with
+     * enet_packet_destroy after use.
+     */
+   ENET_EVENT_TYPE_RECEIVE    = 3
+} ENetEventType;
+
+/**
+ * An ENet event as returned by enet_host_service().
+   
+   @sa enet_host_service
+ */
+typedef struct _ENetEvent 
+{
+   ENetEventType        type;      /**< type of the event */
+   ENetPeer *           peer;      /**< peer that generated a connect, disconnect or receive event */
+   enet_uint8           channelID;
+   ENetPacket *         packet;
+} ENetEvent;
+
+/** @defgroup global ENet global functions
+    @{ 
+*/
+
+/** 
+  Initializes ENet globally.  Must be called prior to using any functions in
+  ENet.
+  @returns 0 on success, < 0 on failure
+*/
+ENET_API int enet_initialize (void);
+
+ENET_API int enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits);
+
+/** 
+  Shuts down ENet globally.  Should be called when a program that has
+  initialized ENet exits.
+*/
+ENET_API void enet_deinitialize (void);
+
+/** @} */
+
+/** @defgroup private ENet private implementation functions */
+
+/**
+  Returns the wall-time in milliseconds.  Its initial value is unspecified
+  unless otherwise set.
+  */
+ENET_API enet_uint32 enet_time_get (void);
+/**
+  Sets the current wall-time in milliseconds.
+  */
+ENET_API void enet_time_set (enet_uint32);
+
+/** @defgroup socket ENet socket functions
+    @{
+    @ingroup private
+*/
+extern ENetSocket enet_socket_create (ENetSocketType, const ENetAddress *);
+extern ENetSocket enet_socket_accept (ENetSocket, ENetAddress *);
+extern int        enet_socket_connect (ENetSocket, const ENetAddress *);
+extern int        enet_socket_send (ENetSocket, const ENetAddress *, const ENetBuffer *, size_t);
+extern int        enet_socket_receive (ENetSocket, ENetAddress *, ENetBuffer *, size_t);
+extern int        enet_socket_wait (ENetSocket, enet_uint32 *, enet_uint32);
+extern void       enet_socket_destroy (ENetSocket);
+
+/** @} */
+
+/** @defgroup Address ENet address functions
+    @{
+*/
+/** Attempts to resolve the host named by the parameter hostName and sets
+    the host field in the address parameter if successful.
+    @param address destination to store resolved address
+    @param hostName host name to lookup
+    @retval 0 on success
+    @retval < 0 on failure
+    @returns the address of the given hostName in address on success
+*/
+ENET_API int enet_address_set_host (ENetAddress *address, const char *hostName );
+
+/** Attempts to do a reserve lookup of the host field in the address parameter.
+    @param address    address used for reverse lookup
+    @param hostName   destination for name, must not be NULL
+    @param nameLength maximum length of hostName.
+    @returns the null-terminated name of the host in hostName on success
+    @retval 0 on success
+    @retval < 0 on failure
+*/
+ENET_API int enet_address_get_host (const ENetAddress *address, char *hostName, size_t nameLength );
+
+/** @} */
+
+ENET_API ENetPacket * enet_packet_create (const void *dataContents, size_t dataLength, enet_uint32 flags);
+ENET_API void         enet_packet_destroy (ENetPacket *packet );
+ENET_API int          enet_packet_resize  (ENetPacket *packet, size_t dataLength );
+
+ENET_API ENetHost * enet_host_create (const ENetAddress *address, size_t peerCount, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth );
+ENET_API void       enet_host_destroy (ENetHost *host );
+ENET_API ENetPeer * enet_host_connect (ENetHost *host, const ENetAddress *address, size_t channelCount );
+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_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32);
+extern   void       enet_host_bandwidth_throttle (ENetHost *);
+
+ENET_API int                 enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *);
+ENET_API ENetPacket *        enet_peer_receive (ENetPeer *, enet_uint8);
+ENET_API void                enet_peer_ping (ENetPeer *);
+ENET_API void                enet_peer_reset (ENetPeer *);
+ENET_API void                enet_peer_disconnect (ENetPeer *);
+ENET_API void                enet_peer_disconnect_now (ENetPeer *);
+ENET_API void                enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32);
+extern int                   enet_peer_throttle (ENetPeer *, enet_uint32);
+extern void                  enet_peer_reset_queues (ENetPeer *);
+extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16);
+extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32);
+extern ENetAcknowledgement * enet_peer_queue_acknowledgement (ENetPeer *, const ENetProtocol *, enet_uint32);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ENET_ENET_H__ */
+

+ 42 - 0
include/enet/list.h

@@ -0,0 +1,42 @@
+/** 
+ @file  list.h
+ @brief ENet list management 
+*/
+#ifndef __ENET_LIST_H__
+#define __ENET_LIST_H__
+
+#include <stdlib.h>
+
+typedef struct _ENetListNode
+{
+   struct _ENetListNode * next;
+   struct _ENetListNode * previous;
+} ENetListNode;
+
+typedef ENetListNode * ENetListIterator;
+
+typedef struct _ENetList
+{
+   ENetListNode sentinel;
+} ENetList;
+
+extern void enet_list_clear (ENetList *);
+
+extern ENetListIterator enet_list_insert (ENetListIterator, void *);
+extern void * enet_list_remove (ENetListIterator);
+
+extern size_t enet_list_size (ENetList *);
+
+#define enet_list_begin(list) ((list) -> sentinel.next)
+#define enet_list_end(list) (& (list) -> sentinel)
+
+#define enet_list_empty(list) (enet_list_begin (list) == enet_list_end (list))
+
+#define enet_list_next(iterator) ((iterator) -> next)
+#define enet_list_previous(iterator) ((iterator) -> previous)
+
+#define enet_list_front(list) ((void *) (list) -> sentinel.next)
+#define enet_list_back(list) ((void *) (list) -> sentinel.previous)
+
+#endif /* __ENET_LIST_H__ */
+

+ 166 - 0
include/enet/protocol.h

@@ -0,0 +1,166 @@
+/** 
+ @file  protocol.h
+ @brief ENet protocol
+*/
+#ifndef __ENET_PROTOCOL_H__
+#define __ENET_PROTOCOL_H__
+
+#include "enet/types.h"
+
+enum
+{
+   ENET_PROTOCOL_MINIMUM_MTU             = 576,
+   ENET_PROTOCOL_MAXIMUM_MTU             = 4096,
+   ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32,
+   ENET_PROTOCOL_MINIMUM_WINDOW_SIZE     = 4096,
+   ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE     = 32768,
+   ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT   = 1,
+   ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT   = 255
+};
+
+typedef enum
+{
+   ENET_PROTOCOL_COMMAND_NONE               = 0,
+   ENET_PROTOCOL_COMMAND_ACKNOWLEDGE        = 1,
+   ENET_PROTOCOL_COMMAND_CONNECT            = 2,
+   ENET_PROTOCOL_COMMAND_VERIFY_CONNECT     = 3,
+   ENET_PROTOCOL_COMMAND_DISCONNECT         = 4,
+   ENET_PROTOCOL_COMMAND_PING               = 5,
+   ENET_PROTOCOL_COMMAND_SEND_RELIABLE      = 6,
+   ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE    = 7,
+   ENET_PROTOCOL_COMMAND_SEND_FRAGMENT      = 8,
+   ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT    = 9,
+   ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 10,
+   ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED   = 11
+} ENetProtocolCommand;
+
+typedef enum
+{
+   ENET_PROTOCOL_FLAG_ACKNOWLEDGE = (1 << 0),
+   ENET_PROTOCOL_FLAG_UNSEQUENCED = (1 << 1)
+} ENetProtocolFlag;
+
+typedef struct
+{
+   enet_uint16 peerID;
+   enet_uint8 flags;
+   enet_uint8 commandCount;
+   enet_uint32 sentTime;
+   enet_uint32 challenge;
+} ENetProtocolHeader;
+
+typedef struct
+{
+   enet_uint8 command;
+   enet_uint8 channelID;
+   enet_uint8 flags;
+   enet_uint8 reserved;
+   enet_uint32 commandLength;
+   enet_uint32 reliableSequenceNumber;
+} ENetProtocolCommandHeader;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+   enet_uint32 receivedReliableSequenceNumber;
+   enet_uint32 receivedSentTime;
+} ENetProtocolAcknowledge;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+   enet_uint16 outgoingPeerID;
+   enet_uint16 mtu;
+   enet_uint32 windowSize;
+   enet_uint32 channelCount;
+   enet_uint32 incomingBandwidth;
+   enet_uint32 outgoingBandwidth;
+   enet_uint32 packetThrottleInterval;
+   enet_uint32 packetThrottleAcceleration;
+   enet_uint32 packetThrottleDeceleration;
+} ENetProtocolConnect;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+   enet_uint16 outgoingPeerID;
+   enet_uint16 mtu;
+   enet_uint32 windowSize;
+   enet_uint32 channelCount;
+   enet_uint32 incomingBandwidth;
+   enet_uint32 outgoingBandwidth;
+   enet_uint32 packetThrottleInterval;
+   enet_uint32 packetThrottleAcceleration;
+   enet_uint32 packetThrottleDeceleration;
+} ENetProtocolVerifyConnect;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+   enet_uint32 incomingBandwidth;
+   enet_uint32 outgoingBandwidth;
+} ENetProtocolBandwidthLimit;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+   enet_uint32 packetThrottleInterval;
+   enet_uint32 packetThrottleAcceleration;
+   enet_uint32 packetThrottleDeceleration;
+} ENetProtocolThrottleConfigure;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+} ENetProtocolDisconnect;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+} ENetProtocolPing;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+} ENetProtocolSendReliable;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+   enet_uint32 unreliableSequenceNumber;
+} ENetProtocolSendUnreliable;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+   enet_uint32 unsequencedGroup;
+} ENetProtocolSendUnsequenced;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+   enet_uint32 startSequenceNumber;
+   enet_uint32 fragmentCount;
+   enet_uint32 fragmentNumber;
+   enet_uint32 totalLength;
+   enet_uint32 fragmentOffset;
+} ENetProtocolSendFragment;
+
+typedef union
+{
+   ENetProtocolCommandHeader header;
+   ENetProtocolAcknowledge acknowledge;
+   ENetProtocolConnect connect;
+   ENetProtocolVerifyConnect verifyConnect;
+   ENetProtocolDisconnect disconnect;
+   ENetProtocolPing ping;
+   ENetProtocolSendReliable sendReliable;
+   ENetProtocolSendUnreliable sendUnreliable;
+   ENetProtocolSendUnsequenced sendUnsequenced;
+   ENetProtocolSendFragment sendFragment;
+   ENetProtocolBandwidthLimit bandwidthLimit;
+   ENetProtocolThrottleConfigure throttleConfigure;
+} ENetProtocol;
+
+#endif /* __ENET_PROTOCOL_H__ */
+

+ 18 - 0
include/enet/time.h

@@ -0,0 +1,18 @@
+/** 
+ @file  time.h
+ @brief ENet time constants and macros
+*/
+#ifndef __ENET_TIME_H__
+#define __ENET_TIME_H__
+
+#define ENET_TIME_OVERFLOW 86400000
+
+#define ENET_TIME_LESS(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW)
+#define ENET_TIME_GREATER(a, b) ((b) - (a) >= ENET_TIME_OVERFLOW)
+#define ENET_TIME_LESS_EQUAL(a, b) (! ENET_TIME_GREATER (a, b))
+#define ENET_TIME_GREATER_EQUAL(a, b) (! ENET_TIME_LESS (a, b))
+
+#define ENET_TIME_DIFFERENCE(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW ? (b) - (a) : (a) - (b))
+
+#endif /* __ENET_TIME_H__ */
+

+ 13 - 0
include/enet/types.h

@@ -0,0 +1,13 @@
+/** 
+ @file  types.h
+ @brief type definitions for ENet
+*/
+#ifndef __ENET_TYPES_H__
+#define __ENET_TYPES_H__
+
+typedef unsigned char enet_uint8;       /**< unsigned 8-bit type  */
+typedef unsigned short enet_uint16;     /**< unsigned 16-bit type */
+typedef unsigned int enet_uint32;      /**< unsigned 32-bit type */
+
+#endif /* __ENET_TYPES_H__ */
+

+ 36 - 0
include/enet/unix.h

@@ -0,0 +1,36 @@
+/** 
+ @file  unix.h
+ @brief ENet Unix header
+*/
+#ifndef __ENET_UNIX_H__
+#define __ENET_UNIX_H__
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+typedef int ENetSocket;
+
+enum
+{
+    ENET_SOCKET_NULL = -1
+};
+
+#define ENET_HOST_TO_NET_16(value) (htons (value)) /**< macro that converts host to net byte-order of a 16-bit value */
+#define ENET_HOST_TO_NET_32(value) (htonl (value)) /**< macro that converts host to net byte-order of a 32-bit value */
+
+#define ENET_NET_TO_HOST_16(value) (ntohs (value)) /**< macro that converts net to host byte-order of a 16-bit value */
+#define ENET_NET_TO_HOST_32(value) (ntohl (value)) /**< macro that converts net to host byte-order of a 32-bit value */
+
+typedef struct
+{
+    void * data;
+    size_t dataLength;
+} ENetBuffer;
+
+#define ENET_CALLBACK
+
+#define ENET_API extern
+
+#endif /* __ENET_UNIX_H__ */
+

+ 12 - 0
include/enet/utility.h

@@ -0,0 +1,12 @@
+/** 
+ @file  utility.h
+ @brief ENet utility header
+*/
+#ifndef __ENET_UTILITY_H__
+#define __ENET_UTILITY_H__
+
+#define ENET_MAX(x, y) ((x) > (y) ? (x) : (y))
+#define ENET_MIN(x, y) ((x) < (y) ? (x) : (y))
+
+#endif /* __ENET_UTILITY_H__ */
+

+ 42 - 0
include/enet/win32.h

@@ -0,0 +1,42 @@
+/** 
+ @file  win32.h
+ @brief ENet Win32 header
+*/
+#ifndef __ENET_WIN32_H__
+#define __ENET_WIN32_H__
+
+#include <stdlib.h>
+#include <winsock2.h>
+
+typedef SOCKET ENetSocket;
+
+enum
+{
+    ENET_SOCKET_NULL = INVALID_SOCKET
+};
+
+#define ENET_HOST_TO_NET_16(value) (htons (value))
+#define ENET_HOST_TO_NET_32(value) (htonl (value))
+
+#define ENET_NET_TO_HOST_16(value) (ntohs (value))
+#define ENET_NET_TO_HOST_32(value) (ntohl (value))
+
+typedef struct
+{
+    size_t dataLength;
+    void * data;
+} ENetBuffer;
+
+#define ENET_CALLBACK __cdecl
+
+#if defined ENET_DLL
+#if defined ENET_BUILDING_LIB
+#define ENET_API __declspec( dllexport )
+#else
+#define ENET_API __declspec( dllimport )
+#endif /* ENET_BUILDING_LIB */
+#endif /* ENET_DLL */
+
+#endif /* __ENET_WIN32_H__ */
+
+

+ 57 - 0
list.c

@@ -0,0 +1,57 @@
+/** 
+ @file list.c
+ @brief ENet linked list functions
+*/
+#define ENET_BUILDING_LIB 1
+#include "enet/list.h"
+
+/** 
+    @defgroup list ENet linked list utility functions
+    @ingroup private
+    @{
+*/
+void
+enet_list_clear (ENetList * list)
+{
+   list -> sentinel.next = & list -> sentinel;
+   list -> sentinel.previous = & list -> sentinel;
+}
+
+ENetListIterator
+enet_list_insert (ENetListIterator position, void * data)
+{
+   ENetListIterator result = (ENetListIterator) data;
+
+   result -> previous = position -> previous;
+   result -> next = position;
+
+   result -> previous -> next = result;
+   position -> previous = result;
+
+   return result;
+}
+
+void *
+enet_list_remove (ENetListIterator position)
+{
+   position -> previous -> next = position -> next;
+   position -> next -> previous = position -> previous;
+
+   return position;
+}
+
+size_t
+enet_list_size (ENetList * list)
+{
+   size_t size = 0;
+   ENetListIterator position;
+
+   for (position = enet_list_begin (list);
+        position != enet_list_end (list);
+        position = enet_list_next (position))
+     ++ size;
+   
+   return size;
+}
+
+/** @} */

+ 74 - 0
packet.c

@@ -0,0 +1,74 @@
+/** 
+ @file  packet.c
+ @brief ENet packet management functions
+*/
+#include <string.h>
+#define ENET_BUILDING_LIB 1
+#include "enet/enet.h"
+
+/** @defgroup Packet ENet packet functions 
+    @{ 
+*/
+
+/** Creates a packet that may be sent to a peer.
+    @param dataContents initial contents of the packet's data; the packet's data will remain uninitialized if dataContents is NULL.
+    @param dataLength   size of the data allocated for this packet
+    @param flags        flags for this packet as described for the ENetPacket structure.
+    @returns the packet on success, NULL on failure
+*/
+ENetPacket *
+enet_packet_create (const void * data, size_t dataLength, enet_uint32 flags)
+{
+    ENetPacket * packet = (ENetPacket *) enet_malloc (sizeof (ENetPacket));
+
+    packet -> data = (enet_uint8 *) enet_malloc (dataLength);
+
+    if (data != NULL)
+      memcpy (packet -> data, data, dataLength);
+
+    packet -> referenceCount = 0;
+    packet -> flags = flags;
+    packet -> dataLength = dataLength;
+
+    return packet;
+}
+
+/** Destroys the packet and deallocates its data.
+    @param packet packet to be destroyed
+*/
+void
+enet_packet_destroy (ENetPacket * packet)
+{
+    enet_free (packet -> data);
+    enet_free (packet);
+}
+
+/** Attempts to resize the data in the packet to length specified in the 
+    dataLength parameter 
+    @param packet packet to resize
+    @param dataLength new size for the packet data
+    @returns 0 on success, < 0 on failure
+*/
+int
+enet_packet_resize (ENetPacket * packet, size_t dataLength)
+{
+    enet_uint8 * newData;
+   
+    if (dataLength <= packet -> dataLength)
+    {
+       packet -> dataLength = dataLength;
+
+       return 0;
+    }
+
+    newData = (enet_uint8 *) enet_malloc (dataLength);
+    memcpy (newData, packet -> data, packet -> dataLength);
+    free (packet -> data);
+    
+    packet -> data = newData;
+    packet -> dataLength = dataLength;
+
+    return 0;
+}
+
+/** @} */

+ 644 - 0
peer.c

@@ -0,0 +1,644 @@
+/** 
+ @file  peer.c
+ @brief ENet peer management functions
+*/
+#define ENET_BUILDING_LIB 1
+#include "enet/enet.h"
+
+/** @defgroup peer ENet peer functions 
+    @{
+*/
+
+/** Configures throttle parameter for a peer.
+
+    Unreliable packets are dropped by ENet in response to the varying conditions
+    of the Internet connection to the peer.  The throttle represents a probability
+    that an unreliable packet should not be dropped and thus sent by ENet to the peer.
+    The lowest mean round trip time from the sending of a reliable packet to the
+    receipt of its acknowledgement is measured over an amount of time specified by
+    the interval parameter in milliseconds.  If a measured round trip time happens to
+    be significantly less than the mean round trip time measured over the interval, 
+    then the throttle probability is increased to allow more traffic by an amount
+    specified in the acceleration parameter, which is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE
+    constant.  If a measured round trip time happens to be significantly greater than
+    the mean round trip time measured over the interval, then the throttle probability
+    is decreased to limit traffic by an amount specified in the deceleration parameter, which
+    is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE constant.  When the throttle has
+    a value of ENET_PEER_PACKET_THROTTLE_SCALE, on unreliable packets are dropped by 
+    ENet, and so 100% of all unreliable packets will be sent.  When the throttle has a
+    value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable
+    packets will be sent.  Intermediate values for the throttle represent intermediate
+    probabilities between 0% and 100% of unreliable packets being sent.  The bandwidth
+    limits of the local and foreign hosts are taken into account to determine a 
+    sensible limit for the throttle probability above which it should not raise even in
+    the best of conditions.
+
+    @param peer peer to configure 
+    @param interval interval, in milliseconds, over which to measure lowest mean RTT; the default value is ENET_PEER_PACKET_THROTTLE_INTERVAL.
+    @param acceleration rate at which to increase the throttle probability as mean RTT declines
+    @param deceleration rate at which to decrease the throttle probability as mean RTT increases
+*/
+void
+enet_peer_throttle_configure (ENetPeer * peer, enet_uint32 interval, enet_uint32 acceleration, enet_uint32 deceleration)
+{
+    ENetProtocol command;
+
+    peer -> packetThrottleInterval = interval;
+    peer -> packetThrottleAcceleration = acceleration;
+    peer -> packetThrottleDeceleration = deceleration;
+
+    command.header.command = ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE;
+    command.header.channelID = 0xFF;
+    command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
+    command.header.commandLength = sizeof (ENetProtocolThrottleConfigure);
+
+    command.throttleConfigure.packetThrottleInterval = ENET_HOST_TO_NET_32 (interval);
+    command.throttleConfigure.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (acceleration);
+    command.throttleConfigure.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (deceleration);
+
+    enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+}
+
+int
+enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt)
+{
+    if (peer -> lastRoundTripTime <= peer -> lastRoundTripTimeVariance)
+    {
+        peer -> packetThrottle = peer -> packetThrottleLimit;
+    }
+    else
+    if (rtt < peer -> lastRoundTripTime)
+    {
+        peer -> packetThrottle += peer -> packetThrottleAcceleration;
+
+        if (peer -> packetThrottle > peer -> packetThrottleLimit)
+          peer -> packetThrottle = peer -> packetThrottleLimit;
+
+        return 1;
+    }
+    else
+    if (rtt > peer -> lastRoundTripTime + 2 * peer -> lastRoundTripTimeVariance)
+    {
+        if (peer -> packetThrottle > peer -> packetThrottleDeceleration)
+          peer -> packetThrottle -= peer -> packetThrottleDeceleration;
+        else
+          peer -> packetThrottle = 0;
+
+        return -1;
+    }
+
+    return 0;
+}
+
+/** Queues a packet to be sent.
+    @param peer destination for the packet
+    @param channelID channel on which to send
+    @param packet packet to send
+    @retval 0 on success
+    @retval < 0 on failure
+*/
+int
+enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet)
+{
+   ENetChannel * channel = & peer -> channels [channelID];
+   ENetProtocol command;
+   size_t fragmentLength;
+
+   if (peer -> state != ENET_PEER_STATE_CONNECTED ||
+       channelID >= peer -> channelCount)
+     return -1;
+
+   fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment);
+
+   if (packet -> dataLength > fragmentLength)
+   {
+      enet_uint32 fragmentCount = ENET_HOST_TO_NET_32 ((packet -> dataLength + fragmentLength - 1) / fragmentLength),
+             startSequenceNumber = ENET_HOST_TO_NET_32 (channel -> outgoingReliableSequenceNumber + 1),
+             fragmentNumber,
+             fragmentOffset;
+
+      packet -> flags = ENET_PACKET_FLAG_RELIABLE;
+
+      for (fragmentNumber = 0,
+             fragmentOffset = 0;
+           fragmentOffset < packet -> dataLength;
+           ++ fragmentNumber,
+             fragmentOffset += fragmentLength)
+      {
+         command.header.command = ENET_PROTOCOL_COMMAND_SEND_FRAGMENT;
+         command.header.channelID = channelID;
+         command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
+         command.header.commandLength = sizeof (ENetProtocolSendFragment);
+         command.sendFragment.startSequenceNumber = startSequenceNumber;
+         command.sendFragment.fragmentCount = fragmentCount;
+         command.sendFragment.fragmentNumber = ENET_HOST_TO_NET_32 (fragmentNumber);
+         command.sendFragment.totalLength = ENET_HOST_TO_NET_32 (packet -> dataLength);
+         command.sendFragment.fragmentOffset = ENET_NET_TO_HOST_32 (fragmentOffset);
+
+         if (packet -> dataLength - fragmentOffset < fragmentLength)
+           fragmentLength = packet -> dataLength - fragmentOffset;
+
+         enet_peer_queue_outgoing_command (peer, & command, packet, fragmentOffset, fragmentLength);
+      }
+
+      return 0;
+   }
+
+   command.header.channelID = channelID;
+
+   if (packet -> flags & ENET_PACKET_FLAG_RELIABLE)
+   {
+      command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE;
+      command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
+      command.header.commandLength = sizeof (ENetProtocolSendReliable);
+   }
+   else
+   if (packet -> flags & ENET_PACKET_FLAG_UNSEQUENCED)
+   {
+      command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED;
+      command.header.flags = ENET_PROTOCOL_FLAG_UNSEQUENCED;
+      command.header.commandLength = sizeof (ENetProtocolSendUnsequenced);
+      command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_32 (peer -> outgoingUnsequencedGroup + 1);
+   }
+   else
+   {
+      command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE;
+      command.header.flags = 0;
+      command.header.commandLength = sizeof (ENetProtocolSendUnreliable);
+      command.sendUnreliable.unreliableSequenceNumber = ENET_HOST_TO_NET_32 (channel -> outgoingUnreliableSequenceNumber + 1);
+   }
+
+   enet_peer_queue_outgoing_command (peer, & command, packet, 0, packet -> dataLength);
+
+   return 0;
+}
+
+/** Attempts to dequeue any incoming queued packet.
+    @param peer peer to dequeue packets from
+    @param channelID channel on which to receive
+    @returns a pointer to the packet, or NULL if there are no available incoming queued packets
+*/
+ENetPacket *
+enet_peer_receive (ENetPeer * peer, enet_uint8 channelID)
+{
+   ENetChannel * channel = & peer -> channels [channelID];
+   ENetIncomingCommand * incomingCommand = NULL;
+   ENetPacket * packet;
+
+   if (enet_list_empty (& channel -> incomingUnreliableCommands) == 0)
+   {
+      incomingCommand = (ENetIncomingCommand *) enet_list_front (& channel -> incomingUnreliableCommands);
+
+      if (incomingCommand -> unreliableSequenceNumber > 0)
+      {
+         if (incomingCommand -> reliableSequenceNumber > channel -> incomingReliableSequenceNumber)
+           incomingCommand = NULL;
+         else
+           channel -> incomingUnreliableSequenceNumber = incomingCommand -> unreliableSequenceNumber;
+      }
+   }
+
+   if (incomingCommand == NULL &&
+       enet_list_empty (& channel -> incomingReliableCommands) == 0)
+   {
+      do
+      {
+        incomingCommand = (ENetIncomingCommand *) enet_list_front (& channel -> incomingReliableCommands);
+
+        if (incomingCommand -> fragmentsRemaining > 0 ||
+            incomingCommand -> reliableSequenceNumber > channel -> incomingReliableSequenceNumber + 1)
+          return NULL;
+
+        if (incomingCommand -> reliableSequenceNumber <= channel -> incomingReliableSequenceNumber)
+        {
+           -- incomingCommand -> packet -> referenceCount;
+
+           if (incomingCommand -> packet -> referenceCount == 0)
+             enet_packet_destroy (incomingCommand -> packet);
+
+           if (incomingCommand -> fragments != NULL)
+             enet_free (incomingCommand -> fragments);
+
+           enet_list_remove (& incomingCommand -> incomingCommandList);
+
+           enet_free (incomingCommand);
+
+           incomingCommand = NULL;
+        }
+      } while (incomingCommand == NULL &&
+               enet_list_empty (& channel -> incomingReliableCommands) == 0);
+
+      if (incomingCommand == NULL)
+        return NULL;
+
+      channel -> incomingReliableSequenceNumber = incomingCommand -> reliableSequenceNumber;
+
+      if (incomingCommand -> fragmentCount > 0)
+        channel -> incomingReliableSequenceNumber += incomingCommand -> fragmentCount - 1;
+   }
+
+   if (incomingCommand == NULL)
+     return NULL;
+
+   enet_list_remove (& incomingCommand -> incomingCommandList);
+
+   packet = incomingCommand -> packet;
+
+   -- packet -> referenceCount;
+
+   if (incomingCommand -> fragments != NULL)
+     enet_free (incomingCommand -> fragments);
+
+   enet_free (incomingCommand);
+
+   return packet;
+}
+
+static void
+enet_peer_reset_outgoing_commands (ENetList * queue)
+{
+    ENetOutgoingCommand * outgoingCommand;
+
+    while (enet_list_empty (queue) == 0)
+    {
+       outgoingCommand = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (queue));
+
+       if (outgoingCommand -> packet != NULL)
+       {
+          -- outgoingCommand -> packet -> referenceCount;
+
+          if (outgoingCommand -> packet -> referenceCount == 0)
+            enet_packet_destroy (outgoingCommand -> packet);
+       }
+
+       enet_free (outgoingCommand);
+    }
+}
+
+static void
+enet_peer_reset_incoming_commands (ENetList * queue)
+{
+    ENetIncomingCommand * incomingCommand;
+
+    while (enet_list_empty (queue) == 0)
+    {
+       incomingCommand = (ENetIncomingCommand *) enet_list_remove (enet_list_begin (queue));
+
+       if (incomingCommand -> packet != NULL)
+       {
+          -- incomingCommand -> packet -> referenceCount;
+
+          if (incomingCommand -> packet -> referenceCount == 0)
+            enet_packet_destroy (incomingCommand -> packet);
+       }
+
+       enet_free (incomingCommand);
+    }
+}
+
+void
+enet_peer_reset_queues (ENetPeer * peer)
+{
+    ENetChannel * channel;
+
+    while (enet_list_empty (& peer -> acknowledgements) == 0)
+      enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements)));
+
+    enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands);
+    enet_peer_reset_outgoing_commands (& peer -> sentUnreliableCommands);
+    enet_peer_reset_outgoing_commands (& peer -> outgoingReliableCommands);
+    enet_peer_reset_outgoing_commands (& peer -> outgoingUnreliableCommands);
+
+    if (peer -> channels != NULL && peer -> channelCount > 0)
+    {
+        for (channel = peer -> channels;
+             channel < & peer -> channels [peer -> channelCount];
+             ++ channel)
+        {
+            enet_peer_reset_incoming_commands (& channel -> incomingReliableCommands);
+            enet_peer_reset_incoming_commands (& channel -> incomingUnreliableCommands);
+        }
+
+        enet_free (peer -> channels);
+    }
+
+    peer -> channels = NULL;
+    peer -> channelCount = 0;
+}
+
+/** Forcefully disconnects a peer.
+    @param peer peer to forcefully disconnect
+    @remarks The foreign host represented by the peer is not notified of the disconnection and will timeout
+    on its connection to the local host.
+*/
+void
+enet_peer_reset (ENetPeer * peer)
+{
+    peer -> outgoingPeerID = 0xFFFF;
+    peer -> challenge = 0;
+
+    peer -> address.host = ENET_HOST_ANY;
+    peer -> address.port = 0;
+
+    peer -> state = ENET_PEER_STATE_DISCONNECTED;
+
+    peer -> incomingBandwidth = 0;
+    peer -> outgoingBandwidth = 0;
+    peer -> incomingBandwidthThrottleEpoch = 0;
+    peer -> outgoingBandwidthThrottleEpoch = 0;
+    peer -> incomingDataTotal = 0;
+    peer -> outgoingDataTotal = 0;
+    peer -> lastSendTime = 0;
+    peer -> lastReceiveTime = 0;
+    peer -> nextTimeout = 0;
+    peer -> packetLossEpoch = 0;
+    peer -> packetsSent = 0;
+    peer -> packetsLost = 0;
+    peer -> packetLoss = 0;
+    peer -> packetLossVariance = 0;
+    peer -> packetThrottle = ENET_PEER_DEFAULT_PACKET_THROTTLE;
+    peer -> packetThrottleLimit = ENET_PEER_PACKET_THROTTLE_SCALE;
+    peer -> packetThrottleCounter = 0;
+    peer -> packetThrottleEpoch = 0;
+    peer -> packetThrottleAcceleration = ENET_PEER_PACKET_THROTTLE_ACCELERATION;
+    peer -> packetThrottleDeceleration = ENET_PEER_PACKET_THROTTLE_DECELERATION;
+    peer -> packetThrottleInterval = ENET_PEER_PACKET_THROTTLE_INTERVAL;
+    peer -> lastRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;
+    peer -> lowestRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;
+    peer -> lastRoundTripTimeVariance = 0;
+    peer -> highestRoundTripTimeVariance = 0;
+    peer -> roundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;
+    peer -> roundTripTimeVariance = 0;
+    peer -> mtu = peer -> host -> mtu;
+    peer -> reliableDataInTransit = 0;
+    peer -> outgoingReliableSequenceNumber = 0;
+    peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+    peer -> incomingUnsequencedGroup = 0;
+    peer -> outgoingUnsequencedGroup = 0;
+
+    memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow));
+    
+    enet_peer_reset_queues (peer);
+}
+
+/** Sends a ping request to a peer.
+    @param peer destination for the ping request
+    @remarks ping requests factor into the mean round trip time as designated by the 
+    roundTripTime field in the ENetPeer structure.  Enet automatically pings all connected
+    peers at regular intervals, however, this function may be called to ensure more
+    frequent ping requests.
+*/
+void
+enet_peer_ping (ENetPeer * peer)
+{
+    ENetProtocol command;
+
+    if (peer -> state != ENET_PEER_STATE_CONNECTED)
+      return;
+
+    command.header.command = ENET_PROTOCOL_COMMAND_PING;
+    command.header.channelID = 0xFF;
+    command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
+    command.header.commandLength = sizeof (ENetProtocolPing);
+   
+    enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+}
+
+/** Force an immediate disconnection from a peer.
+    @param peer peer to disconnect
+    @remarks No ENET_EVENT_DISCONNECT event will be generated. The foreign peer is not
+    guarenteed to receive the disconnect notification, and is reset immediately upon
+    return from this function.
+*/
+void
+enet_peer_disconnect_now (ENetPeer * peer)
+{
+    ENetProtocol command;
+
+    if (peer -> state == ENET_PEER_STATE_DISCONNECTED)
+      return;
+
+    if (peer -> state != ENET_PEER_STATE_ZOMBIE &&
+        peer -> state != ENET_PEER_STATE_DISCONNECTING)
+    {
+        enet_peer_reset_queues (peer);
+
+        command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT;
+        command.header.channelID = 0xFF;
+        command.header.flags = ENET_PROTOCOL_FLAG_UNSEQUENCED;
+        command.header.commandLength = sizeof (ENetProtocolDisconnect);
+
+        enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+
+        enet_host_flush (peer -> host);
+    }
+
+    enet_peer_reset (peer);
+}
+
+/** Request a disconnection from a peer.
+    @param peer peer to request a disconnection
+    @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service()
+    once the disconnection is complete.
+*/
+void
+enet_peer_disconnect (ENetPeer * peer)
+{
+    ENetProtocol command;
+
+    if (peer -> state == ENET_PEER_STATE_DISCONNECTING ||
+        peer -> state == ENET_PEER_STATE_DISCONNECTED ||
+        peer -> state == ENET_PEER_STATE_ZOMBIE)
+      return;
+
+    enet_peer_reset_queues (peer);
+
+    command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT;
+    command.header.channelID = 0xFF;
+    command.header.flags = ENET_PROTOCOL_FLAG_UNSEQUENCED;
+    command.header.commandLength = sizeof (ENetProtocolDisconnect);
+
+    if (peer -> state == ENET_PEER_STATE_CONNECTED)
+      command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
+    
+    enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+
+    if (peer -> state == ENET_PEER_STATE_CONNECTED)
+      peer -> state = ENET_PEER_STATE_DISCONNECTING;
+    else
+    {
+        enet_host_flush (peer -> host);
+        enet_peer_reset (peer);
+    }
+}
+
+ENetAcknowledgement *
+enet_peer_queue_acknowledgement (ENetPeer * peer, const ENetProtocol * command, enet_uint32 sentTime)
+{
+    ENetAcknowledgement * acknowledgement;
+
+    peer -> outgoingDataTotal += sizeof (ENetProtocolAcknowledge);
+
+    acknowledgement = (ENetAcknowledgement *) enet_malloc (sizeof (ENetAcknowledgement));
+
+    acknowledgement -> sentTime = sentTime;
+    acknowledgement -> command = * command;
+    
+    enet_list_insert (enet_list_end (& peer -> acknowledgements), acknowledgement);
+    
+    return acknowledgement;
+}
+
+ENetOutgoingCommand *
+enet_peer_queue_outgoing_command (ENetPeer * peer, const ENetProtocol * command, ENetPacket * packet, enet_uint32 offset, enet_uint16 length)
+{
+    ENetChannel * channel = & peer -> channels [command -> header.channelID];
+    ENetOutgoingCommand * outgoingCommand;
+
+    peer -> outgoingDataTotal += command -> header.commandLength + length;
+
+    outgoingCommand = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand));
+
+    if (command -> header.channelID == 0xFF)
+    {
+       ++ peer -> outgoingReliableSequenceNumber;
+
+       outgoingCommand -> reliableSequenceNumber = peer -> outgoingReliableSequenceNumber;
+       outgoingCommand -> unreliableSequenceNumber = 0;
+    }
+    else
+    if (command -> header.flags & ENET_PROTOCOL_FLAG_ACKNOWLEDGE)
+    {
+       ++ channel -> outgoingReliableSequenceNumber;
+       
+       outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
+       outgoingCommand -> unreliableSequenceNumber = 0;
+    }
+    else
+    if (command -> header.flags & ENET_PROTOCOL_FLAG_UNSEQUENCED)
+    {
+       outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
+       outgoingCommand -> unreliableSequenceNumber = 0;
+    }
+    else
+    {
+       ++ channel -> outgoingUnreliableSequenceNumber;
+        
+       outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
+       outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber;
+    }
+   
+    outgoingCommand -> sentTime = 0;
+    outgoingCommand -> roundTripTimeout = 0;
+    outgoingCommand -> roundTripTimeoutLimit = 0;
+    outgoingCommand -> fragmentOffset = offset;
+    outgoingCommand -> fragmentLength = length;
+    outgoingCommand -> packet = packet;
+    outgoingCommand -> command = * command;
+    outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_32 (outgoingCommand -> reliableSequenceNumber);
+
+    if (packet != NULL)
+      ++ packet -> referenceCount;
+
+    if (command -> header.flags & ENET_PROTOCOL_FLAG_ACKNOWLEDGE)
+      enet_list_insert (enet_list_end (& peer -> outgoingReliableCommands), outgoingCommand);
+    else
+      enet_list_insert (enet_list_end (& peer -> outgoingUnreliableCommands), outgoingCommand);
+
+    return outgoingCommand;
+}
+
+ENetIncomingCommand *
+enet_peer_queue_incoming_command (ENetPeer * peer, const ENetProtocol * command, ENetPacket * packet, enet_uint32 fragmentCount)
+{
+    ENetChannel * channel = & peer -> channels [command -> header.channelID];
+    enet_uint32 unreliableSequenceNumber = 0;
+    ENetIncomingCommand * incomingCommand;
+    ENetListIterator currentCommand;
+
+    switch (command -> header.command)
+    {
+    case ENET_PROTOCOL_COMMAND_SEND_RELIABLE:
+       for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands));
+            currentCommand != enet_list_end (& channel -> incomingReliableCommands);
+            currentCommand = enet_list_previous (currentCommand))
+       {
+          incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+          if (incomingCommand -> reliableSequenceNumber <= command -> header.reliableSequenceNumber)
+          {
+             if (incomingCommand -> reliableSequenceNumber < command -> header.reliableSequenceNumber)
+               break;
+
+             goto freePacket;
+          }
+       }
+       break;
+
+    case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE:
+       unreliableSequenceNumber = ENET_NET_TO_HOST_32 (command -> sendUnreliable.unreliableSequenceNumber);
+
+       if (command -> header.reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+         goto freePacket;
+
+       if (unreliableSequenceNumber <= channel -> incomingUnreliableSequenceNumber)
+         goto freePacket;
+
+       for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands));
+            currentCommand != enet_list_end (& channel -> incomingUnreliableCommands);
+            currentCommand = enet_list_previous (currentCommand))
+       {
+          incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+          if (incomingCommand -> unreliableSequenceNumber <= unreliableSequenceNumber)
+          {
+             if (incomingCommand -> unreliableSequenceNumber < unreliableSequenceNumber)
+               break;
+
+             goto freePacket;
+          }
+       }
+       break;
+
+    case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED:
+       currentCommand = enet_list_end (& channel -> incomingUnreliableCommands);
+       break;
+
+    default:
+       goto freePacket;
+    }
+
+    incomingCommand = (ENetIncomingCommand *) enet_malloc (sizeof (ENetIncomingCommand));
+
+    incomingCommand -> reliableSequenceNumber = command -> header.reliableSequenceNumber;
+    incomingCommand -> unreliableSequenceNumber = unreliableSequenceNumber;
+    incomingCommand -> command = * command;
+    incomingCommand -> fragmentCount = fragmentCount;
+    incomingCommand -> fragmentsRemaining = fragmentCount;
+    incomingCommand -> packet = packet;
+    incomingCommand -> fragments = NULL;
+    
+    if (fragmentCount > 0)
+    { 
+       incomingCommand -> fragments = (enet_uint32 *) enet_malloc ((fragmentCount + 31) / 32 * sizeof (enet_uint32));
+       memset (incomingCommand -> fragments, 0, (fragmentCount + 31) / 32 * sizeof (enet_uint32));
+    }
+
+    if (packet != NULL)
+      ++ packet -> referenceCount;
+
+    enet_list_insert (enet_list_next (currentCommand), incomingCommand);
+
+    return incomingCommand;
+
+freePacket:
+    if (packet != NULL)
+    {
+       if (packet -> referenceCount == 0)
+         enet_packet_destroy (packet);
+    }
+
+    return NULL;
+}
+
+/** @} */

+ 1296 - 0
protocol.c

@@ -0,0 +1,1296 @@
+/** 
+ @file  protocol.c
+ @brief ENet protocol functions
+*/
+#include <stdio.h>
+#include <string.h>
+#define ENET_BUILDING_LIB 1
+#include "enet/utility.h"
+#include "enet/time.h"
+#include "enet/enet.h"
+
+static enet_uint32 timeCurrent;
+
+static int
+enet_protocol_dispatch_incoming_commands (ENetHost * host, ENetEvent * event)
+{
+    ENetPeer * currentPeer = host -> lastServicedPeer;
+    ENetChannel * channel;
+
+    do
+    {
+       ++ currentPeer;
+       
+       if (currentPeer >= & host -> peers [host -> peerCount])
+         currentPeer = host -> peers;
+
+       if (currentPeer -> state == ENET_PEER_STATE_ZOMBIE)
+       {
+           host -> recalculateBandwidthLimits = 1;
+
+           event -> type = ENET_EVENT_TYPE_DISCONNECT;
+           event -> peer = currentPeer;
+
+           enet_peer_reset (currentPeer);
+
+           host -> lastServicedPeer = currentPeer;
+
+           return 1;
+       }
+
+       if (currentPeer -> state != ENET_PEER_STATE_CONNECTED)
+         continue;
+
+       for (channel = currentPeer -> channels;
+            channel < & currentPeer -> channels [currentPeer -> channelCount];
+            ++ channel)
+       {
+           if (enet_list_empty (& channel -> incomingReliableCommands) &&
+               enet_list_empty (& channel -> incomingUnreliableCommands))
+             continue;
+
+           event -> packet = enet_peer_receive (currentPeer, channel - currentPeer -> channels);
+           if (event -> packet == NULL)
+             continue;
+             
+           event -> type = ENET_EVENT_TYPE_RECEIVE;
+           event -> peer = currentPeer;
+           event -> channelID = (enet_uint8) (channel - currentPeer -> channels);
+
+           host -> lastServicedPeer = currentPeer;
+
+           return 1;
+       }
+    } while (currentPeer != host -> lastServicedPeer);
+
+    return 0;
+}
+
+static void
+enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer)
+{
+    ENetOutgoingCommand * outgoingCommand;
+
+    while (enet_list_empty (& peer -> sentUnreliableCommands) == 0)
+    {
+        outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentUnreliableCommands);
+        
+        enet_list_remove (& outgoingCommand -> outgoingCommandList);
+
+        if (outgoingCommand -> packet != NULL)
+        {
+           -- outgoingCommand -> packet -> referenceCount;
+
+           if (outgoingCommand -> packet -> referenceCount == 0)
+             enet_packet_destroy (outgoingCommand -> packet);
+        }
+
+        enet_free (outgoingCommand);
+    }
+}
+
+static ENetProtocolCommand
+enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint32 reliableSequenceNumber, enet_uint8 channelID)
+{
+    ENetOutgoingCommand * outgoingCommand;
+    ENetListIterator currentCommand;
+    ENetProtocolCommand commandNumber;
+
+    for (currentCommand = enet_list_begin (& peer -> sentReliableCommands);
+         currentCommand != enet_list_end (& peer -> sentReliableCommands);
+         currentCommand = enet_list_next (currentCommand))
+    {
+       outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+        
+       if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber &&
+           outgoingCommand -> command.header.channelID == channelID)
+         break;
+    }
+
+    if (currentCommand == enet_list_end (& peer -> sentReliableCommands))
+      return ENET_PROTOCOL_COMMAND_NONE;
+
+    commandNumber = outgoingCommand -> command.header.command;
+
+    enet_list_remove (& outgoingCommand -> outgoingCommandList);
+
+    if (outgoingCommand -> packet != NULL)
+    {
+       peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;
+
+       -- outgoingCommand -> packet -> referenceCount;
+
+       if (outgoingCommand -> packet -> referenceCount == 0)
+         enet_packet_destroy (outgoingCommand -> packet);
+    }
+
+    enet_free (outgoingCommand);
+
+    if (enet_list_empty (& peer -> sentReliableCommands))
+      return commandNumber;
+    
+    outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentReliableCommands);
+    
+    peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout;
+
+    return commandNumber;
+} 
+
+static ENetPeer *
+enet_protocol_handle_connect (ENetHost * host, const ENetProtocolHeader * header, const ENetProtocol * command)
+{
+    enet_uint16 mtu;
+    enet_uint32 windowSize;
+    ENetChannel * channel;
+    size_t channelCount;
+    ENetPeer * currentPeer;
+    ENetProtocol verifyCommand;
+
+    if (command -> header.commandLength < sizeof (ENetProtocolConnect))
+      return NULL;
+
+    channelCount = ENET_NET_TO_HOST_32 (command -> connect.channelCount);
+
+    if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT ||
+        channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
+      return NULL;
+
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+        if (currentPeer -> state != ENET_PEER_STATE_DISCONNECTED &&
+            currentPeer -> address.host == host -> receivedAddress.host &&
+            currentPeer -> address.port == host -> receivedAddress.port &&
+            currentPeer -> challenge == header -> challenge)
+          return NULL;
+    }
+
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+        if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED)
+          break;
+    }
+
+    if (currentPeer >= & host -> peers [host -> peerCount])
+      return NULL;
+
+    currentPeer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT;
+    currentPeer -> challenge = header -> challenge;
+    currentPeer -> address = host -> receivedAddress;
+    currentPeer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID);
+    currentPeer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth);
+    currentPeer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth);
+    currentPeer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleInterval);
+    currentPeer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleAcceleration);
+    currentPeer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleDeceleration);
+    currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel));
+    currentPeer -> channelCount = channelCount;
+
+    for (channel = currentPeer -> channels;
+         channel < & currentPeer -> channels [channelCount];
+         ++ channel)
+    {
+        channel -> outgoingReliableSequenceNumber = 0;
+        channel -> outgoingUnreliableSequenceNumber = 0;
+        channel -> incomingReliableSequenceNumber = 0;
+        channel -> incomingUnreliableSequenceNumber = 0;
+
+        enet_list_clear (& channel -> incomingReliableCommands);
+        enet_list_clear (& channel -> incomingUnreliableCommands);
+    }
+
+    mtu = ENET_NET_TO_HOST_16 (command -> connect.mtu);
+
+    if (mtu < ENET_PROTOCOL_MINIMUM_MTU)
+      mtu = ENET_PROTOCOL_MINIMUM_MTU;
+    else
+    if (mtu > ENET_PROTOCOL_MAXIMUM_MTU)
+      mtu = ENET_PROTOCOL_MAXIMUM_MTU;
+
+    currentPeer -> mtu = mtu;
+
+    if (host -> outgoingBandwidth == 0 &&
+        currentPeer -> incomingBandwidth == 0)
+      currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+    else
+      currentPeer -> windowSize = (ENET_MIN (host -> outgoingBandwidth, currentPeer -> incomingBandwidth) /
+                                    ENET_PEER_WINDOW_SIZE_SCALE) * 
+                                      ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+    if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+      currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+    else
+    if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+      currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+    if (host -> incomingBandwidth == 0)
+      windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+    else
+      windowSize = (host -> incomingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) *
+                     ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+    if (windowSize > ENET_NET_TO_HOST_32 (command -> connect.windowSize))
+      windowSize = ENET_NET_TO_HOST_32 (command -> connect.windowSize);
+
+    if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+      windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+    else
+    if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+      windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+    verifyCommand.header.command = ENET_PROTOCOL_COMMAND_VERIFY_CONNECT;
+    verifyCommand.header.channelID = 0xFF;
+    verifyCommand.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
+    verifyCommand.header.commandLength = sizeof (ENetProtocolVerifyConnect);
+    verifyCommand.verifyConnect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID);
+    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);
+    verifyCommand.verifyConnect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth);
+    verifyCommand.verifyConnect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
+    verifyCommand.verifyConnect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval);
+    verifyCommand.verifyConnect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration);
+    verifyCommand.verifyConnect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration);
+
+    enet_peer_queue_outgoing_command (currentPeer, & verifyCommand, NULL, 0, 0);
+
+    return currentPeer;
+}
+
+static void
+enet_protocol_handle_send_reliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    ENetPacket * packet;
+
+    if (command -> header.commandLength <= sizeof (ENetProtocolSendReliable) ||
+        command -> header.channelID >= peer -> channelCount ||
+        peer -> state != ENET_PEER_STATE_CONNECTED)
+      return;
+
+    packet = enet_packet_create ((const enet_uint8 *) command + sizeof (ENetProtocolSendReliable),
+                                 command -> header.commandLength - sizeof (ENetProtocolSendReliable),
+                                 ENET_PACKET_FLAG_RELIABLE);
+
+    enet_peer_queue_incoming_command (peer, command, packet, 0);
+}
+
+static void
+enet_protocol_handle_send_unsequenced (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    ENetPacket * packet;
+    enet_uint32 unsequencedGroup, index;
+
+    if (command -> header.commandLength <= sizeof (ENetProtocolSendUnsequenced) ||
+        command -> header.channelID >= peer -> channelCount ||
+        peer -> state != ENET_PEER_STATE_CONNECTED)
+      return;
+
+    unsequencedGroup = ENET_NET_TO_HOST_32 (command -> sendUnsequenced.unsequencedGroup);
+    index = unsequencedGroup % ENET_PEER_UNSEQUENCED_WINDOW_SIZE;
+    
+    if (unsequencedGroup >= peer -> incomingUnsequencedGroup + ENET_PEER_UNSEQUENCED_WINDOW_SIZE)
+    {
+        peer -> incomingUnsequencedGroup = unsequencedGroup - index;
+
+        memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow));
+    }
+    else
+    if (unsequencedGroup < peer -> incomingUnsequencedGroup ||
+        peer -> unsequencedWindow [index / 32] & (1 << (index % 32)))
+      return;
+      
+    peer -> unsequencedWindow [index / 32] |= 1 << (index % 32);
+    
+                        
+    packet = enet_packet_create ((const enet_uint8 *) command + sizeof (ENetProtocolSendUnsequenced),
+                                 command -> header.commandLength - sizeof (ENetProtocolSendUnsequenced),
+                                 ENET_PACKET_FLAG_UNSEQUENCED);
+
+    enet_peer_queue_incoming_command (peer, command, packet, 0);
+}
+
+static void
+enet_protocol_handle_send_unreliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    ENetPacket * packet;
+
+    if (command -> header.commandLength <= sizeof (ENetProtocolSendUnreliable) ||
+        command -> header.channelID >= peer -> channelCount ||
+        peer -> state != ENET_PEER_STATE_CONNECTED)
+      return;
+
+    packet = enet_packet_create ((const enet_uint8 *) command + sizeof (ENetProtocolSendUnreliable),
+                                 command -> header.commandLength - sizeof (ENetProtocolSendUnreliable),
+                                 0);
+
+    enet_peer_queue_incoming_command (peer, command, packet, 0);
+}
+
+static void
+enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    enet_uint32 fragmentNumber,
+           fragmentCount,
+           fragmentOffset,
+           fragmentLength,
+           startSequenceNumber,
+           totalLength;
+    ENetChannel * channel;
+    ENetListIterator currentCommand;
+    ENetIncomingCommand * startCommand;
+
+    if (command -> header.commandLength <= sizeof (ENetProtocolSendFragment) ||
+        command -> header.channelID >= peer -> channelCount ||
+        peer -> state != ENET_PEER_STATE_CONNECTED)
+      return;
+
+    startSequenceNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.startSequenceNumber);
+    fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber);
+    fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount);
+    fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset);
+    totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength);
+    fragmentLength = command -> header.commandLength - sizeof (ENetProtocolSendFragment);
+    
+    if (fragmentOffset >= totalLength ||
+        fragmentOffset + fragmentLength > totalLength ||
+        fragmentNumber >= fragmentCount)
+      return;
+ 
+    channel = & peer -> channels [command -> header.channelID];
+
+    if (startSequenceNumber <= channel -> incomingReliableSequenceNumber)
+      return;
+
+    for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands));
+         currentCommand != enet_list_end (& channel -> incomingReliableCommands);
+         currentCommand = enet_list_previous (currentCommand))
+    {
+       startCommand = (ENetIncomingCommand *) currentCommand;
+
+       if (startCommand -> command.header.command == ENET_PROTOCOL_COMMAND_SEND_FRAGMENT &&
+           startCommand -> command.sendFragment.startSequenceNumber == startSequenceNumber)
+         break;
+    }
+ 
+    if (currentCommand == enet_list_end (& channel -> incomingReliableCommands))
+    {
+       ENetProtocol hostCommand = * command;
+       
+       hostCommand.header.reliableSequenceNumber = startSequenceNumber;
+       hostCommand.sendFragment.startSequenceNumber = startSequenceNumber;
+       hostCommand.sendFragment.fragmentNumber = fragmentNumber;
+       hostCommand.sendFragment.fragmentCount = fragmentCount;
+       hostCommand.sendFragment.fragmentOffset = fragmentOffset;
+       hostCommand.sendFragment.totalLength = totalLength;
+
+       startCommand = enet_peer_queue_incoming_command (peer, 
+                                                        & hostCommand, 
+                                                        enet_packet_create (NULL, totalLength, ENET_PACKET_FLAG_RELIABLE),
+                                                        fragmentCount);
+    }
+    else
+    if (totalLength != startCommand -> packet -> dataLength ||
+        fragmentCount != startCommand -> fragmentCount)
+      return;
+    
+    if ((startCommand -> fragments [fragmentNumber / 32] & (1 << fragmentNumber)) == 0)
+      -- startCommand -> fragmentsRemaining;
+
+    startCommand -> fragments [fragmentNumber / 32] |= (1 << fragmentNumber);
+
+    if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength)
+      fragmentLength = startCommand -> packet -> dataLength - fragmentOffset;
+
+    memcpy (startCommand -> packet -> data + fragmentOffset,
+            (enet_uint8 *) command + sizeof (ENetProtocolSendFragment),
+            fragmentLength);
+}
+
+static void
+enet_protocol_handle_ping (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    if (command -> header.commandLength < sizeof (ENetProtocolPing))
+      return;
+}
+
+static void
+enet_protocol_handle_bandwidth_limit (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    if (command -> header.commandLength < sizeof (ENetProtocolBandwidthLimit))
+      return;
+
+    peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.incomingBandwidth);
+    peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.outgoingBandwidth);
+
+    if (peer -> incomingBandwidth == 0 &&
+        host -> outgoingBandwidth == 0)
+      peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+    else
+      peer -> windowSize = (ENET_MIN (peer -> incomingBandwidth, host -> outgoingBandwidth) /
+                             ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+    if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+      peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+    else
+    if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+      peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+}
+
+static void
+enet_protocol_handle_throttle_configure (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    if (command -> header.commandLength < sizeof (ENetProtocolThrottleConfigure))
+      return;
+
+    peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleInterval);
+    peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleAcceleration);
+    peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleDeceleration);
+}
+
+static void
+enet_protocol_handle_disconnect (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    if (command -> header.commandLength < sizeof (ENetProtocolDisconnect))
+      return;
+
+    enet_peer_reset_queues (peer);
+
+    if (peer -> state != ENET_PEER_STATE_CONNECTED)
+      enet_peer_reset (peer);
+    else
+    if (command -> header.flags & ENET_PROTOCOL_FLAG_ACKNOWLEDGE)
+      peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT;
+    else
+      peer -> state = ENET_PEER_STATE_ZOMBIE;
+}
+
+static int
+enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command)
+{
+    enet_uint32 roundTripTime,
+           receivedSentTime,
+           receivedReliableSequenceNumber;
+    ENetProtocolCommand commandNumber;
+
+    if (command -> header.commandLength < sizeof (ENetProtocolAcknowledge))
+      return 0;
+
+    receivedSentTime = ENET_NET_TO_HOST_32 (command -> acknowledge.receivedSentTime);
+
+    if (ENET_TIME_LESS (timeCurrent, receivedSentTime))
+      return 0;
+
+    peer -> lastReceiveTime = timeCurrent;
+
+    roundTripTime = ENET_TIME_DIFFERENCE (timeCurrent, receivedSentTime);
+
+    enet_peer_throttle (peer, roundTripTime);
+
+    peer -> roundTripTimeVariance -= peer -> roundTripTimeVariance / 4;
+
+    if (roundTripTime >= peer -> roundTripTime)
+    {
+       peer -> roundTripTime += (roundTripTime - peer -> roundTripTime) / 8;
+       peer -> roundTripTimeVariance += (roundTripTime - peer -> roundTripTime) / 4;
+    }
+    else
+    {
+       peer -> roundTripTime -= (peer -> roundTripTime - roundTripTime) / 8;
+       peer -> roundTripTimeVariance += (peer -> roundTripTime - roundTripTime) / 4;
+    }
+
+    if (peer -> roundTripTime < peer -> lowestRoundTripTime)
+      peer -> lowestRoundTripTime = peer -> roundTripTime;
+
+    if (peer -> roundTripTimeVariance > peer -> highestRoundTripTimeVariance) 
+      peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance;
+
+    if (peer -> packetThrottleEpoch == 0 ||
+        ENET_TIME_DIFFERENCE(timeCurrent, peer -> packetThrottleEpoch) >= peer -> packetThrottleInterval)
+    {
+        peer -> lastRoundTripTime = peer -> lowestRoundTripTime;
+        peer -> lastRoundTripTimeVariance = peer -> highestRoundTripTimeVariance;
+        peer -> lowestRoundTripTime = peer -> roundTripTime;
+        peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance;
+        peer -> packetThrottleEpoch = timeCurrent;
+    }
+
+    receivedReliableSequenceNumber = ENET_NET_TO_HOST_32 (command -> acknowledge.receivedReliableSequenceNumber);
+
+    commandNumber = enet_protocol_remove_sent_reliable_command (peer, receivedReliableSequenceNumber, command -> header.channelID);
+
+    switch (peer -> state)
+    {
+    case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT:
+       if (commandNumber != ENET_PROTOCOL_COMMAND_VERIFY_CONNECT)
+         return 0;
+
+       host -> recalculateBandwidthLimits = 1;
+
+       peer -> state = ENET_PEER_STATE_CONNECTED;
+
+       event -> type = ENET_EVENT_TYPE_CONNECT;
+       event -> peer = peer;
+
+       return 1;
+
+    case ENET_PEER_STATE_DISCONNECTING:
+       if (commandNumber != ENET_PROTOCOL_COMMAND_DISCONNECT)
+         return 0;
+
+       host -> recalculateBandwidthLimits = 1;
+
+       event -> type = ENET_EVENT_TYPE_DISCONNECT;
+       event -> peer = peer;
+
+       enet_peer_reset (peer);
+
+       return 1;
+
+    default:
+       break;
+    }
+   
+    return 0;
+}
+
+static void
+enet_protocol_handle_verify_connect (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command)
+{
+    enet_uint16 mtu;
+    enet_uint32 windowSize;
+
+    if (command -> header.commandLength < sizeof (ENetProtocolVerifyConnect) ||
+        peer -> state != ENET_PEER_STATE_CONNECTING)
+      return;
+
+    if (ENET_NET_TO_HOST_32 (command -> verifyConnect.channelCount) != peer -> channelCount ||
+        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)
+    {
+        peer -> state = ENET_PEER_STATE_ZOMBIE;
+
+        return;
+    }
+
+    peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> verifyConnect.outgoingPeerID);
+
+    mtu = ENET_NET_TO_HOST_16 (command -> verifyConnect.mtu);
+
+    if (mtu < ENET_PROTOCOL_MINIMUM_MTU)
+      mtu = ENET_PROTOCOL_MINIMUM_MTU;
+    else 
+    if (mtu > ENET_PROTOCOL_MAXIMUM_MTU)
+      mtu = ENET_PROTOCOL_MAXIMUM_MTU;
+
+    if (mtu < peer -> mtu)
+      peer -> mtu = mtu;
+
+    windowSize = ENET_NET_TO_HOST_32 (command -> verifyConnect.windowSize);
+
+    if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+      windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+    if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+      windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+    if (windowSize < peer -> windowSize)
+      peer -> windowSize = windowSize;
+
+    peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.incomingBandwidth);
+    peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.outgoingBandwidth);
+
+    host -> recalculateBandwidthLimits = 1;
+
+    peer -> state = ENET_PEER_STATE_CONNECTED;
+
+    event -> type = ENET_EVENT_TYPE_CONNECT;
+    event -> peer = peer;
+}
+
+static int
+enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event)
+{
+    ENetProtocolHeader * header;
+    ENetProtocol * command;
+    ENetPeer * peer;
+    enet_uint8 * currentData;
+    size_t commandCount;
+
+    if (host -> receivedDataLength < sizeof (ENetProtocolHeader))
+      return 0;
+
+    header = (ENetProtocolHeader *) host -> receivedData;
+
+    header -> peerID = ENET_NET_TO_HOST_16 (header -> peerID);
+    header -> sentTime = ENET_NET_TO_HOST_32 (header -> sentTime);
+
+    if (header -> peerID == 0xFFFF)
+      peer = NULL;
+    else
+    if (header -> peerID >= host -> peerCount)
+      return 0;
+    else
+    {
+       peer = & host -> peers [header -> peerID];
+
+       if (peer -> state == ENET_PEER_STATE_DISCONNECTED ||
+           peer -> state == ENET_PEER_STATE_ZOMBIE || 
+           host -> receivedAddress.host != peer -> address.host ||
+           header -> challenge != peer -> challenge)
+         return 0;
+       else
+         peer -> address.port = host -> receivedAddress.port;
+    }
+
+    if (peer != NULL)
+      peer -> incomingDataTotal += host -> receivedDataLength;
+
+    commandCount = header -> commandCount;
+    currentData = host -> receivedData + sizeof (ENetProtocolHeader);
+  
+    while (commandCount > 0 &&
+           currentData < & host -> receivedData [host -> receivedDataLength])
+    {
+       command = (ENetProtocol *) currentData;
+
+       if (currentData + sizeof (ENetProtocolCommandHeader) > & host -> receivedData [host -> receivedDataLength])
+         return 0;
+
+       command -> header.commandLength = ENET_NET_TO_HOST_32 (command -> header.commandLength);
+
+       if (currentData + command -> header.commandLength > & host -> receivedData [host -> receivedDataLength])
+         return 0;
+
+       -- commandCount;
+       currentData += command -> header.commandLength;
+
+       if (peer == NULL)
+       {
+          if (command -> header.command != ENET_PROTOCOL_COMMAND_CONNECT)
+            return 0;
+       }
+         
+       command -> header.reliableSequenceNumber = ENET_NET_TO_HOST_32 (command -> header.reliableSequenceNumber);
+
+       switch (command -> header.command)
+       {
+       case ENET_PROTOCOL_COMMAND_ACKNOWLEDGE:
+          enet_protocol_handle_acknowledge (host, event, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_CONNECT:
+          peer = enet_protocol_handle_connect (host, header, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_VERIFY_CONNECT:
+          enet_protocol_handle_verify_connect (host, event, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_DISCONNECT:
+          enet_protocol_handle_disconnect (host, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_PING:
+          enet_protocol_handle_ping (host, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_SEND_RELIABLE:
+          enet_protocol_handle_send_reliable (host, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE:
+          enet_protocol_handle_send_unreliable (host, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED:
+          enet_protocol_handle_send_unsequenced (host, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT:
+          enet_protocol_handle_send_fragment (host, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT:
+          enet_protocol_handle_bandwidth_limit (host, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE:
+          enet_protocol_handle_throttle_configure (host, peer, command);
+
+          break;
+
+       default:
+          break;
+       }
+
+       if (peer != NULL &&
+           (command -> header.flags & ENET_PROTOCOL_FLAG_ACKNOWLEDGE) != 0)
+       {
+           switch (peer -> state)
+           {
+           case ENET_PEER_STATE_DISCONNECTING:
+              break;
+
+           case ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT:
+              if (command -> header.command != ENET_PROTOCOL_COMMAND_DISCONNECT)
+                break;
+
+           default:   
+              enet_peer_queue_acknowledgement (peer, command, header -> sentTime);        
+
+              break;
+           }
+       }
+    }
+
+    if (event -> type != ENET_EVENT_TYPE_NONE)
+      return 1;
+
+    return 0;
+}
+ 
+static int
+enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event)
+{
+    for (;;)
+    {
+       int receivedLength;
+       ENetBuffer buffer;
+
+       buffer.data = host -> receivedData;
+       buffer.dataLength = sizeof (host -> receivedData);
+
+       receivedLength = enet_socket_receive (host -> socket,
+                                             & host -> receivedAddress,
+                                             & buffer,
+                                             1);
+
+       if (receivedLength < 0)
+         return -1;
+
+       if (receivedLength == 0)
+         return 0;
+
+       host -> receivedDataLength = receivedLength;
+       
+       switch (enet_protocol_handle_incoming_commands (host, event))
+       {
+       case 1:
+          return 1;
+       
+       case -1:
+          return -1;
+
+       default:
+          break;
+       }
+    }
+
+    return -1;
+}
+
+static void
+enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer)
+{
+    ENetProtocol * command = & host -> commands [host -> commandCount];
+    ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
+    ENetAcknowledgement * acknowledgement;
+    ENetListIterator currentAcknowledgement;
+  
+    currentAcknowledgement = enet_list_begin (& peer -> acknowledgements);
+         
+    while (currentAcknowledgement != enet_list_end (& peer -> acknowledgements))
+    {
+       if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] ||
+           buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
+           peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge))
+         break;
+
+       acknowledgement = (ENetAcknowledgement *) currentAcknowledgement;
+ 
+       currentAcknowledgement = enet_list_next (currentAcknowledgement);
+
+       buffer -> data = command;
+       buffer -> dataLength = sizeof (ENetProtocolAcknowledge);
+
+       host -> packetSize += buffer -> dataLength;
+ 
+       command -> header.command = ENET_PROTOCOL_COMMAND_ACKNOWLEDGE;
+       command -> header.channelID = acknowledgement -> command.header.channelID;
+       command -> header.flags = 0;
+       command -> header.commandLength = ENET_HOST_TO_NET_32 (sizeof (ENetProtocolAcknowledge));
+       command -> acknowledge.receivedReliableSequenceNumber = ENET_HOST_TO_NET_32 (acknowledgement -> command.header.reliableSequenceNumber);
+       command -> acknowledge.receivedSentTime = ENET_HOST_TO_NET_32 (acknowledgement -> sentTime);
+  
+       if (acknowledgement -> command.header.command == ENET_PROTOCOL_COMMAND_DISCONNECT)
+         peer -> state = ENET_PEER_STATE_ZOMBIE;
+
+       enet_list_remove (& acknowledgement -> acknowledgementList);
+       enet_free (acknowledgement);
+
+       ++ command;
+       ++ buffer;
+    }
+
+    host -> commandCount = command - host -> commands;
+    host -> bufferCount = buffer - host -> buffers;
+}
+
+static void
+enet_protocol_send_unreliable_outgoing_commands (ENetHost * host, ENetPeer * peer)
+{
+    ENetProtocol * command = & host -> commands [host -> commandCount];
+    ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
+    ENetOutgoingCommand * outgoingCommand;
+    ENetListIterator currentCommand;
+
+    currentCommand = enet_list_begin (& peer -> outgoingUnreliableCommands);
+    
+    while (currentCommand != enet_list_end (& peer -> outgoingUnreliableCommands))
+    {
+       outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+       if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] ||
+           buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
+           peer -> mtu - host -> packetSize < outgoingCommand -> command.header.commandLength ||
+           (outgoingCommand -> packet != NULL &&
+             peer -> mtu - host -> packetSize < outgoingCommand -> command.header.commandLength + 
+                                                         outgoingCommand -> packet -> dataLength))
+         break;
+
+       currentCommand = enet_list_next (currentCommand);
+
+       if (outgoingCommand -> packet != NULL)
+       {
+          peer -> packetThrottleCounter += ENET_PEER_PACKET_THROTTLE_COUNTER;
+          peer -> packetThrottleCounter %= ENET_PEER_PACKET_THROTTLE_SCALE;
+          
+          if (peer -> packetThrottleCounter > peer -> packetThrottle)
+          {
+             -- outgoingCommand -> packet -> referenceCount;
+
+             if (outgoingCommand -> packet -> referenceCount == 0)
+               enet_packet_destroy (outgoingCommand -> packet);
+         
+             enet_list_remove (& outgoingCommand -> outgoingCommandList);
+             enet_free (outgoingCommand);
+           
+             continue;
+          }
+       }
+
+       buffer -> data = command;
+       buffer -> dataLength = outgoingCommand -> command.header.commandLength;
+      
+       host -> packetSize += buffer -> dataLength;
+
+       * command = outgoingCommand -> command;
+       
+       enet_list_remove (& outgoingCommand -> outgoingCommandList);
+
+       if (outgoingCommand -> packet != NULL)
+       {
+          ++ buffer;
+          
+          buffer -> data = outgoingCommand -> packet -> data;
+          buffer -> dataLength = outgoingCommand -> packet -> dataLength;
+
+          command -> header.commandLength += buffer -> dataLength;
+
+          host -> packetSize += buffer -> dataLength;
+
+          enet_list_insert (enet_list_end (& peer -> sentUnreliableCommands), outgoingCommand);
+       }
+       else
+         enet_free (outgoingCommand);
+
+       command -> header.commandLength = ENET_HOST_TO_NET_32 (command -> header.commandLength);
+
+       ++ command;
+       ++ buffer;
+    } 
+
+    host -> commandCount = command - host -> commands;
+    host -> bufferCount = buffer - host -> buffers;
+}
+
+static int
+enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event)
+{
+    ENetOutgoingCommand * outgoingCommand;
+    ENetListIterator currentCommand;
+
+    currentCommand = enet_list_begin (& peer -> sentReliableCommands);
+
+    while (currentCommand != enet_list_end (& peer -> sentReliableCommands))
+    {
+       outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+       currentCommand = enet_list_next (currentCommand);
+
+       if (ENET_TIME_DIFFERENCE (timeCurrent, outgoingCommand -> sentTime) < outgoingCommand -> roundTripTimeout)
+         continue;
+
+       if (outgoingCommand -> roundTripTimeout >= outgoingCommand -> roundTripTimeoutLimit)
+       {
+          event -> type = ENET_EVENT_TYPE_DISCONNECT;
+          event -> peer = peer;
+
+          enet_peer_reset (peer);
+
+          return 1;
+       }
+
+       if (outgoingCommand -> packet != NULL)
+         peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;
+          
+       ++ peer -> packetsLost;
+
+       outgoingCommand -> roundTripTimeout *= 2;
+
+       enet_list_insert (enet_list_begin (& peer -> outgoingReliableCommands),
+                         enet_list_remove (& outgoingCommand -> outgoingCommandList));
+
+       if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) &&
+           enet_list_empty (& peer -> sentReliableCommands) == 0)
+       {
+          outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+          peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout;
+       }
+    }
+    
+    return 0;
+}
+
+static void
+enet_protocol_send_reliable_outgoing_commands (ENetHost * host, ENetPeer * peer)
+{
+    ENetProtocol * command = & host -> commands [host -> commandCount];
+    ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
+    ENetOutgoingCommand * outgoingCommand;
+    ENetListIterator currentCommand;
+
+    currentCommand = enet_list_begin (& peer -> outgoingReliableCommands);
+    
+    while (currentCommand != enet_list_end (& peer -> outgoingReliableCommands))
+    {
+       outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+       if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] ||
+           buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
+           peer -> mtu - host -> packetSize < outgoingCommand -> command.header.commandLength)
+         break;
+
+       currentCommand = enet_list_next (currentCommand);
+
+       if (outgoingCommand -> packet != NULL)
+       {
+          if ((enet_uint16) (peer -> mtu - host -> packetSize) <
+                (enet_uint16) (outgoingCommand -> command.header.commandLength +
+                           outgoingCommand -> fragmentLength) ||
+              peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > peer -> windowSize)
+            break;
+       }
+       
+       if (outgoingCommand -> roundTripTimeout == 0)
+       {
+          outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance;
+          outgoingCommand -> roundTripTimeoutLimit = ENET_PEER_TIMEOUT_LIMIT * outgoingCommand -> roundTripTimeout;
+       }
+
+       if (enet_list_empty (& peer -> sentReliableCommands))
+         peer -> nextTimeout = timeCurrent + outgoingCommand -> roundTripTimeout;
+
+       enet_list_insert (enet_list_end (& peer -> sentReliableCommands),
+                         enet_list_remove (& outgoingCommand -> outgoingCommandList));
+
+       outgoingCommand -> sentTime = timeCurrent;
+
+       buffer -> data = command;
+       buffer -> dataLength = outgoingCommand -> command.header.commandLength;
+
+       host -> packetSize += buffer -> dataLength;
+
+       * command = outgoingCommand -> command;
+
+       if (outgoingCommand -> packet != NULL)
+       {
+          ++ buffer;
+          
+          buffer -> data = outgoingCommand -> packet -> data + outgoingCommand -> fragmentOffset;
+          buffer -> dataLength = outgoingCommand -> fragmentLength;
+
+          command -> header.commandLength += outgoingCommand -> fragmentLength;
+
+          host -> packetSize += outgoingCommand -> fragmentLength;
+
+          peer -> reliableDataInTransit += outgoingCommand -> fragmentLength;
+       }
+
+       command -> header.commandLength = ENET_HOST_TO_NET_32 (command -> header.commandLength);
+
+       ++ peer -> packetsSent;
+        
+       ++ command;
+       ++ buffer;
+    }
+
+    host -> commandCount = command - host -> commands;
+    host -> bufferCount = buffer - host -> buffers;
+}
+
+static int
+enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int checkForTimeouts)
+{
+    size_t packetsSent = 1;
+    ENetProtocolHeader header;
+    ENetPeer * currentPeer;
+    int sentLength;
+    
+    while (packetsSent > 0)
+    for (currentPeer = host -> peers,
+           packetsSent = 0;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+        if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED ||
+            currentPeer -> state == ENET_PEER_STATE_ZOMBIE)
+          continue;
+
+        host -> commandCount = 0;
+        host -> bufferCount = 1;
+        host -> packetSize = sizeof (ENetProtocolHeader);
+
+        if (enet_list_empty (& currentPeer -> acknowledgements) == 0)
+          enet_protocol_send_acknowledgements (host, currentPeer);
+     
+        if (host -> commandCount < sizeof (host -> commands) / sizeof (ENetProtocol))
+        {
+            if (checkForTimeouts != 0 &&
+                enet_list_empty (& currentPeer -> sentReliableCommands) == 0 &&
+                ENET_TIME_GREATER_EQUAL (timeCurrent, currentPeer -> nextTimeout) &&
+                enet_protocol_check_timeouts (host, currentPeer, event) == 1)
+              return 1;
+        }
+        if (enet_list_empty (& currentPeer -> outgoingReliableCommands) == 0)
+          enet_protocol_send_reliable_outgoing_commands (host, currentPeer);
+        else
+        if (enet_list_empty (& currentPeer -> sentReliableCommands) &&
+            ENET_TIME_DIFFERENCE (timeCurrent, currentPeer -> lastReceiveTime) >= ENET_PEER_PING_INTERVAL &&
+            currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing))
+        { 
+            enet_peer_ping (currentPeer);
+            enet_protocol_send_reliable_outgoing_commands (host, currentPeer);
+        }
+                      
+        if (host -> commandCount < sizeof (host -> commands) / sizeof (ENetProtocol) &&
+            enet_list_empty (& currentPeer -> outgoingUnreliableCommands) == 0)
+          enet_protocol_send_unreliable_outgoing_commands (host, currentPeer);
+
+        if (host -> commandCount == 0)
+          continue;
+
+        if (currentPeer -> packetLossEpoch == 0)
+          currentPeer -> packetLossEpoch = timeCurrent;
+        else
+        if (ENET_TIME_DIFFERENCE (timeCurrent, currentPeer -> packetLossEpoch) >= ENET_PEER_PACKET_LOSS_INTERVAL &&
+            currentPeer -> packetsSent > 0)
+        {
+           enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent;
+
+#ifdef ENET_DEBUG
+#ifdef WIN32
+           printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u/%u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingReliableCommands), enet_list_size (& currentPeer -> outgoingUnreliableCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands));
+#else
+           fprintf (stderr, "peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u/%u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingReliableCommands), enet_list_size (& currentPeer -> outgoingUnreliableCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands));
+#endif
+#endif
+          
+           currentPeer -> packetLossVariance -= currentPeer -> packetLossVariance / 4;
+
+           if (packetLoss >= currentPeer -> packetLoss)
+           {
+              currentPeer -> packetLoss += (packetLoss - currentPeer -> packetLoss) / 8;
+              currentPeer -> packetLossVariance += (packetLoss - currentPeer -> packetLoss) / 4;
+           }
+           else
+           {
+              currentPeer -> packetLoss -= (currentPeer -> packetLoss - packetLoss) / 8;
+              currentPeer -> packetLossVariance += (currentPeer -> packetLoss - packetLoss) / 4;
+           }
+
+           currentPeer -> packetLossEpoch = timeCurrent;
+           currentPeer -> packetsSent = 0;
+           currentPeer -> packetsLost = 0;
+        }
+
+        header.peerID = ENET_HOST_TO_NET_16 (currentPeer -> outgoingPeerID);
+        header.flags = 0;
+        header.commandCount = host -> commandCount;
+        header.sentTime = ENET_HOST_TO_NET_32 (timeCurrent);
+        header.challenge = currentPeer -> challenge;
+
+        host -> buffers -> data = & header;
+        host -> buffers -> dataLength = sizeof (ENetProtocolHeader);
+
+        currentPeer -> lastSendTime = timeCurrent;
+
+        ++ packetsSent;
+
+        sentLength = enet_socket_send (host -> socket, & currentPeer -> address, host -> buffers, host -> bufferCount);
+
+        enet_protocol_remove_sent_unreliable_commands (currentPeer);
+
+        if (sentLength < 0)
+          return -1;
+    }
+   
+    return 0;
+}
+
+/** Sends any queued packets on the host specified to its designated peers.
+
+    @param host   host to flush
+    @remarks this function need only be used in circumstances where one wishes to send queued packets earlier than in a call to enet_host_service().
+    @ingroup host
+*/
+void
+enet_host_flush (ENetHost * host)
+{
+    timeCurrent = enet_time_get ();
+
+    enet_protocol_send_outgoing_commands (host, NULL, 0);
+}
+
+/** Waits for events on the host specified and shuttles packets between
+    the host and its peers.
+
+    @param host    host to service
+    @param event   an event structure where event details will be placed if one occurs
+    @param timeout number of milliseconds that ENet should wait for events
+    @retval > 1 if an event occurred within the specified time limit
+    @retval 0 if no event occurred
+    @retval < 1 on failure
+    @remarks enet_host_service should be called fairly regularly for adequate performance
+    @ingroup host
+*/
+int
+enet_host_service (ENetHost * host, ENetEvent * event, enet_uint32 timeout)
+{
+    enet_uint32 waitCondition;
+
+    event -> type = ENET_EVENT_TYPE_NONE;
+    event -> peer = NULL;
+    event -> packet = NULL;
+
+    switch (enet_protocol_dispatch_incoming_commands (host, event))
+    {
+    case 1:
+       return 1;
+
+    case -1:
+       perror ("Error dispatching incoming packets");
+
+       return -1;
+
+    default:
+       break;
+    }
+   
+    timeCurrent = enet_time_get ();
+    
+    timeout += timeCurrent;
+
+    do
+    {
+       if (ENET_TIME_DIFFERENCE (timeCurrent, host -> bandwidthThrottleEpoch) >= ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
+         enet_host_bandwidth_throttle (host);
+
+       switch (enet_protocol_send_outgoing_commands (host, event, 1))
+       {
+       case 1:
+          return 1;
+
+       case -1:
+          perror ("Error sending outgoing packets");
+
+          return -1;
+
+       default:
+          break;
+       }
+
+       switch (enet_protocol_receive_incoming_commands (host, event))
+       {
+       case 1:
+          return 1;
+
+       case -1:
+          perror ("Error receiving incoming packets");
+
+          return -1;
+
+       default:
+          break;
+       }
+
+       switch (enet_protocol_send_outgoing_commands (host, event, 1))
+       {
+       case 1:
+          return 1;
+
+       case -1:
+          perror ("Error sending outgoing packets");
+
+          return -1;
+
+       default:
+          break;
+       }
+
+       switch (enet_protocol_dispatch_incoming_commands (host, event))
+       {
+       case 1:
+          return 1;
+
+       case -1:
+          perror ("Error dispatching incoming packets");
+
+          return -1;
+
+       default:
+          break;
+       }
+
+       timeCurrent = enet_time_get ();
+
+       if (ENET_TIME_GREATER_EQUAL (timeCurrent, timeout))
+         return 0;
+
+       waitCondition = ENET_SOCKET_WAIT_RECEIVE;
+
+       if (enet_socket_wait (host -> socket, & waitCondition, ENET_TIME_DIFFERENCE (timeout, timeCurrent)) != 0)
+         return -1;
+       
+       timeCurrent = enet_time_get ();
+    } while (waitCondition == ENET_SOCKET_WAIT_RECEIVE);
+
+    return 0; 
+}
+

+ 620 - 0
pyenet/enet.pyx

@@ -0,0 +1,620 @@
+# enet.pyx
+#
+# DESCRIPTION
+#
+#   Python ENET Wrapper implemented in pyrexc.
+#
+# RATIONALE
+#
+#   Ling Lo's pyenet.c module had a problem with dropping a connection after
+#   a short amount of time. Having seen other Python <-> C interfaces
+#   defined in pyrexc, I decided it probably has a much better time of
+#   surviving time.
+#
+#   Hopefully no one will be too mad with the option of choice?
+#
+# LICENSE
+#
+#   Copyright (C) 2003, Scott Robinson (scott@tranzoa.com)
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions are
+#   met:
+#
+#   Redistributions of source code must retain the above copyright notice,
+#   this list of conditions and the following disclaimer.
+#
+#   Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+#
+#   The names of its contributors may not be used to endorse or promote
+#   products derived from this software without specific prior written
+#   permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+#   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+#   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+#   PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+#   OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+#   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+#   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+#   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+#   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+#   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+#   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# CHANGELOG
+#
+#   Sat Nov  1 00:36:02 PST 2003  Scott Robinson <scott@tranzoa.com>
+#     Began developing test interface after a day of coding...
+#
+#   Mon Nov  3 08:46:33 PST 2003  Scott Robinson <scott@tranzoa.com>
+#     Added documentation to all classes, functions, and attributes.
+#     While adding documentation, added accessors to a few more attributes.
+#     Cleaned up a few methods to match proper pyrex behavior.
+#     Removed a few, and added a couple of obvious todos for the future.
+#     Fixed Address.__getattr__ extra \0s in the case .host.
+#
+#   Fri Feb 13 18:18:04 PST 2004  Scott Robinson <scott@tranzoa.com>
+#     Added Socket class for use with select and poll.
+#
+
+import  atexit
+
+# SECTION
+#   C declarations and definitions for the interface.
+
+cdef extern from "Python.h" :
+  object  PyBuffer_FromMemory         (void *ptr, int size)
+  object  PyString_FromString         (char *v)
+  object  PyString_FromStringAndSize  (char *v, int len)
+
+cdef extern from "enet/types.h" :
+  ctypedef unsigned char  enet_uint8
+  ctypedef unsigned short enet_uint16
+  ctypedef unsigned int   enet_uint32
+  ctypedef unsigned int   size_t
+
+cdef extern from "enet/enet.h" :
+  cdef enum :
+    ENET_HOST_ANY = 0
+
+  # TODO: Handle Windows situation.
+  ctypedef int    ENetSocket
+
+  ctypedef struct ENetAddress :
+    enet_uint32   host
+    enet_uint16   port
+
+  ctypedef enum ENetPacketFlag :
+    ENET_PACKET_FLAG_RELIABLE = (1 << 0)
+
+  ctypedef struct ENetPacket :
+    size_t        referenceCount
+    enet_uint32   flags
+    enet_uint8   *data
+    size_t        dataLength
+
+  ctypedef enum ENetPeerState :
+    ENET_PEER_STATE_DISCONNECTED                = 0
+    ENET_PEER_STATE_CONNECTING                  = 1
+    ENET_PEER_STATE_ACKNOWLEDGING_CONNECT       = 2
+    ENET_PEER_STATE_CONNECTED                   = 3
+    ENET_PEER_STATE_DISCONNECTING               = 4
+    ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT    = 5
+    ENET_PEER_STATE_ZOMBIE                      = 6
+
+  cdef enum :
+    ENET_PEER_PACKET_LOSS_SCALE = (1 << 16)
+
+  ctypedef struct ENetPeer :
+    ENetAddress   address
+    ENetPeerState state
+    enet_uint32   packetLoss
+    enet_uint32   packetThrottleAcceleration
+    enet_uint32   packetThrottleDeceleration
+    enet_uint32   packetThrottleInterval
+    enet_uint32   roundTripTime
+
+  ctypedef struct ENetHost :
+    ENetSocket    socket
+    ENetAddress   address
+
+  ctypedef enum ENetEventType :
+    ENET_EVENT_TYPE_NONE       = 0
+    ENET_EVENT_TYPE_CONNECT    = 1
+    ENET_EVENT_TYPE_DISCONNECT = 2
+    ENET_EVENT_TYPE_RECEIVE    = 3
+
+  ctypedef struct ENetEvent :
+    ENetEventType   type
+    ENetPeer       *peer
+    enet_uint8      channelID
+    ENetPacket     *packet
+
+  int   enet_initialize     ()
+  void  enet_deinitialize   ()
+
+  int enet_address_set_host (ENetAddress *address, char *hostName)
+  int enet_address_get_host (ENetAddress *address, char *hostName, size_t nameLength)
+
+  ENetPacket *  enet_packet_create    (void *dataContents, size_t dataLength, enet_uint32 flags)
+  void          enet_packet_destroy   (ENetPacket *packet)
+  int           enet_packet_resize    (ENetPacket *packet, size_t dataLength)
+
+  ENetHost *  enet_host_create              (ENetAddress *address, size_t peerCount, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
+  void        enet_host_destroy             (ENetHost *host)
+  ENetPeer *  enet_host_connect             (ENetHost *host, ENetAddress *address, size_t channelCount)
+  int         enet_host_service             (ENetHost *host, ENetEvent *event, enet_uint32 timeout)
+  void        enet_host_flush               (ENetHost *host)
+  void        enet_host_broadcast           (ENetHost *host, enet_uint8 channelID, ENetPacket *packet)
+  void        enet_host_bandwidth_limit     (ENetHost *host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
+
+  int             enet_peer_send                  (ENetPeer *peer, enet_uint8 channelID, ENetPacket *packet)
+  ENetPacket *    enet_peer_receive               (ENetPeer *peer, enet_uint8 channelID)
+  void            enet_peer_ping                  (ENetPeer *peer)
+  void            enet_peer_reset                 (ENetPeer *peer)
+  void            enet_peer_disconnect            (ENetPeer *peer)
+  void            enet_peer_disconnect_now        (ENetPeer *peer)
+  void            enet_peer_throttle_configure    (ENetPeer *peer, enet_uint32 interval, enet_uint32 acceleration, enet_uint32 deacceleration)
+
+# SECTION
+#   Enumerations and constants.
+
+HOST_ANY              = ENET_HOST_ANY
+
+PACKET_FLAG_RELIABLE  = ENET_PACKET_FLAG_RELIABLE
+
+PEER_STATE_DISCONNECT                   = ENET_PEER_STATE_DISCONNECTED
+PEER_STATE_CONNECTING                   = ENET_PEER_STATE_CONNECTING
+PEER_STATE_ACKNOWLEDGING_CONNECT        = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT 
+PEER_STATE_CONNECTED                    = ENET_PEER_STATE_CONNECTED
+PEER_STATE_DISCONNECTING                = ENET_PEER_STATE_DISCONNECTING
+PEER_STATE_ACKNOWLEDGING_DISCONNECT     = ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT
+PEER_STATE_ZOMBIE                       = ENET_PEER_STATE_ZOMBIE
+
+PEER_PACKET_LOSS_SCALE  = ENET_PEER_PACKET_LOSS_SCALE
+
+EVENT_TYPE_NONE       = ENET_EVENT_TYPE_NONE
+EVENT_TYPE_CONNECT    = ENET_EVENT_TYPE_CONNECT
+EVENT_TYPE_DISCONNECT = ENET_EVENT_TYPE_DISCONNECT
+EVENT_TYPE_RECEIVE    = ENET_EVENT_TYPE_RECEIVE
+
+# SECTION
+#   Python exposed class definitions.
+
+cdef class Socket :
+  """Socket (int socket)
+
+  DESCRIPTION
+
+    An ENet socket.
+
+    Can be used with select and poll."""
+
+  cdef ENetSocket     _enet_socket
+
+  def fileno (self) :
+    return self._enet_socket
+
+cdef class Address :
+  """Address (str address, int port)
+
+  ATTRIBUTES
+
+    str host    Hostname referred to by the Address.
+    int port    Port referred to by the Address.
+
+  DESCRIPTION
+
+    An ENet address and port pair.
+
+    When instantiated, performs a resolution upon 'address'. However, if 'address' is None, enet.HOST_ANY is assumed."""
+
+  cdef ENetAddress    _enet_address
+
+  def __init__ (self, address, port) :
+    self.host = address
+    self.port = port
+
+  def __getattr__ (self, name) :
+    if name == "host" :
+      if self._enet_address.host == ENET_HOST_ANY :
+        return "*"
+      elif self._enet_address.host :
+        maxhostname = 257   # We'll follow Solaris' standard.
+        host = PyString_FromStringAndSize (NULL, maxhostname)
+
+        if enet_address_get_host (&self._enet_address, host, maxhostname) :
+          raise IOError ("Resolution failure!")
+
+        return PyString_FromString (host)
+      else :
+        assert (not ENET_HOST_ANY)
+    elif name == "port" :
+      return self._enet_address.port
+    else :
+      return AttributeError ("Address object has no attribute '" + name + "'")
+
+  def __setattr__ (self, name, value) :
+    if name == "host" :
+      if not value or value == "*":
+        self._enet_address.host = ENET_HOST_ANY
+      else :
+        if enet_address_set_host (&self._enet_address, value) :
+          raise IOError ("Resolution failure!")
+    elif name == "port" :
+      self._enet_address.port = value
+    else :
+      return AttributeError ("Address object has no attribute '" + name + "'")
+
+  def __str__ (self) :
+      return "%s:%u" % (self.host, self.port)
+
+cdef class Packet :
+  """Packet ([dataContents, int flags])
+
+  ATTRIBUTES
+
+    str data                        Contains the data for the packet.
+    int flags                       Flags modifying delivery of the Packet:
+      enet.PACKET_FLAG_RELIABLE         Packet must be received by the target peer and resend attempts should be made until the packet is delivered.
+
+  DESCRIPTION
+
+    An ENet data packet that may be sent to or received from a peer."""
+
+  cdef ENetPacket  *_enet_packet
+
+  def __init__ (self, char *dataContents = "", flags = 0) :
+    if dataContents or flags :
+      self._enet_packet = enet_packet_create (dataContents, len (dataContents), flags)
+
+      if not self._enet_packet :
+        raise MemoryError ("Unable to create packet structure!")
+
+  def __dealloc__ (self) :
+    if self._enet_packet and not self._enet_packet.referenceCount :
+      # WARNING: referenceCount is an internal structure. Is there a better way of doing this?
+      enet_packet_destroy (self._enet_packet)
+
+  def __getattr__ (self, name) :
+    if self._enet_packet :
+      if name == "flags" :
+        return self._enet_packet.flags
+      elif name == "data" :
+        # TODO: Find out why the PyBuffer interface is cutting off data!
+        #return PyBuffer_FromMemory (self._enet_packet.data, self._enet_packet.dataLength)
+        return PyString_FromStringAndSize (<char *> self._enet_packet.data, self._enet_packet.dataLength)
+      elif name == "dataLength" :
+        return len (self.data)
+      else :
+        raise AttributeError ("Packet object has no attribute '" + name + "'")
+    else :
+      raise MemoryError ("Empty Packet object accessed!")
+
+cdef class Peer :
+  """Peer ()
+
+  ATTRIBUTES
+
+    Address address
+    int     state                                   The peer's current state.
+      enet.PEER_STATE_DISCONNECT
+          .PEER_STATE_CONNECTING
+          .PEER_STATE_CONNECTED
+          .PEER_STATE_DISCONNECTING
+          .PEER_STATE_ACKNOWLEDGING_DISCONNECT
+          .PEER_STATE_ZOMBIE
+    int     packetLoss                              Mean packet loss of reliable packets as a ratio with respect to the constant enet.PEER_PACKET_LOSS_SCALE.
+    int     packetThrottleAcceleration
+    int     packetThrottleDeceleration
+    int     packetThrottleInterval
+    int     roundTripTime                           Mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgement.
+
+  DESCRIPTION
+
+    An ENet peer which data packets may be sent or received from.
+
+    This class should never be instantiated directly, but rather via enet.Host.connect or enet.Event.Peer."""
+
+  cdef ENetPeer  *_enet_peer
+
+  def send (self, channelID, Packet packet) :
+    """send (int channelID, Packet packet)
+
+    Queues a packet to be sent."""
+
+    if self._enet_peer and packet._enet_packet :
+      return enet_peer_send (self._enet_peer, channelID, packet._enet_packet)
+
+  def receive (self, channelID) :
+    """receive (int channelID)
+
+    Attempts to dequeue any incoming queued packet."""
+
+    if self._enet_peer :
+      packet = Packet ()
+      (<Packet> packet)._enet_packet = enet_peer_receive (self._enet_peer, channelID)
+
+      if packet._enet_packet :
+        return packet
+      else :
+        return None
+
+  def reset (self) :
+    """reset ()
+
+    Forcefully disconnects a peer."""
+
+    if self._enet_peer :
+      enet_peer_reset (self._enet_peer)
+
+  def ping (self) :
+    """ping ()
+
+    Sends a ping request to a peer."""
+
+    if self._enet_peer :
+      enet_peer_ping (self._enet_peer)
+
+  def disconnect (self) :
+    """disconnect ()
+
+    Request a disconnection from a peer."""
+
+    if self._enet_peer :
+      enet_peer_disconnect (self._enet_peer)
+
+  def __getattr__ (self, name) :
+    if self._enet_peer :
+      if name == "address" :
+        address = Address (0, 0)
+        (<Address> address)._enet_address = self._enet_peer.address
+
+        return address
+      elif name == "state" :
+        return self._enet_peer.state
+      elif name == "packetLoss" :
+        return self._enet_peer.packetLoss
+      elif name == "packetThrottleInterval" :
+        return self._enet_peer.packetThrottleInterval
+      elif name == "packetThrottleAcceleration" :
+        return self._enet_peer.packetThrottleAcceleration
+      elif name == "packetThrottleDeceleration" :
+        return self._enet_peer.packetThrottleDeceleration
+      elif name == "roundTripTime" :
+        return self._enet_peer.roundTripTime
+      else :
+        raise AttributeError ("Peer object has no attribute '" + name + "'")
+    else :
+      raise MemoryError ("Empty Peer object accessed!")
+
+  def __setattr__ (self, name, value) :
+    if self._enet_peer :
+      if name == "packetThrottleInterval" : 
+        enet_peer_throttle_configure (self._enet_peer, value, self._enet_peer.packetThrottleAcceleration, self._enet_peer.packetThrottleDeceleration)
+      elif name == "packetThrottleAcceleration" :
+        enet_peer_throttle_configure (self._enet_peer, self._enet_peer.packetThrottleInterval, value, self._enet_peer.packetThrottleDeceleration)
+      elif name == "packetThrottleDeceleration" :
+        enet_peer_throttle_configure (self._enet_peer, self._enet_peer.packetThrottleInterval, self._enet_peer.packetThrottleAcceleration, value)
+      else :
+        raise AttributeError ("Peer object has no attribute '" + name + "'")
+    else :
+      raise MemoryError ("Empty Peer object accessed!")
+
+cdef class Event :
+  """Event ()
+
+  ATTRIBUTES
+
+    int     type        Type of the event.
+      enet.EVENT_TYPE_NONE
+          .EVENT_TYPE_CONNECT
+          .EVENT_TYPE_DISCONNECT
+          .EVENT_TYPE_RECEIVE
+    Peer    peer        Peer that generated a connect, disconnect or receive event.
+    int     channelID
+    Packet  packet
+
+  DESCRIPTION
+
+    An ENet event as returned by enet.Host.service.
+
+    This class should never be instantiated directly."""
+
+  cdef ENetEvent  _enet_event
+
+  def __getattr__ (self, name) :
+    if name == "type" :
+      return self._enet_event.type
+    elif name == "peer" :
+      peer = Peer ()
+      (<Peer> peer)._enet_peer = self._enet_event.peer
+
+      return peer
+    elif name == "channelID" :
+      return self._enet_event.channelID
+    elif name == "packet" :
+      packet = Packet ()
+      (<Packet> packet)._enet_packet = self._enet_event.packet
+
+      return packet
+    else :
+      raise AttributeError ("Event object has no attribute '" + name + "'")
+
+  def __setattr__ (self, name, value) :
+    if name == "type" or name == "peer" or name == "channelID" or name == "packet" : 
+      raise AttributeError ("Attribute '" + name +"' on Event object is read-only.")
+    else :
+      raise AttributeError ("Event object has no attribute '" + name + "'")
+
+cdef class Host :
+  """Host (Address address, int peerCount, int incomingBandwidth, int outgoingBandwidth)
+
+  ATTRIBUTES
+
+    Address address             Internet address of the host.
+    Socket  socket              The socket the host services.
+    int     incomingBandwidth   Downstream bandwidth of the host.
+    int     outgoingBandwidth   Upstream bandwidth of the host.
+
+  DESCRIPTION
+
+    An ENet host for communicating with peers.
+
+    If 'address' is None, then the Host will be client only."""
+
+  cdef ENetHost    *_enet_host
+  cdef enet_uint32  _enet_incomingBandwidth
+  cdef enet_uint32  _enet_outgoingBandwidth
+
+  def __init__ (self, Address address = None, peerCount = 0, incomingBandwidth = 0, outgoingBandwidth = 0) :
+    (self._enet_incomingBandwidth, self._enet_outgoingBandwidth) = (incomingBandwidth, outgoingBandwidth)
+
+    if address :
+      self._enet_host = enet_host_create (&address._enet_address, peerCount, incomingBandwidth, outgoingBandwidth)
+    else :
+      self._enet_host = enet_host_create (NULL, peerCount, incomingBandwidth, outgoingBandwidth)
+
+    if not self._enet_host :
+      raise MemoryError ("Unable to create host structure!")
+
+  def __dealloc__ (self) :
+    if self._enet_host :
+      enet_host_destroy (self._enet_host)
+
+  def connect (self, Address address, channelCount) :
+    """Peer connect (Address address, int channelCount)
+
+    Initiates a connection to a foreign host."""
+
+    if self._enet_host :
+      peer = Peer ()
+      (<Peer> peer)._enet_peer = enet_host_connect (self._enet_host, &address._enet_address, channelCount)
+
+      if not (<Peer> peer)._enet_peer :
+        raise IOError ("Connection failure!")
+
+      return peer
+
+  def service (self, timeout) :
+    """Event service (int timeout)
+
+    Waits for events on the host specified and shuttles packets between the host and its peers."""
+
+    if self._enet_host :
+      event = Event ()
+      result = enet_host_service (self._enet_host, &(<Event> event)._enet_event, timeout)
+
+      if result < 0 :
+        raise IOError ("Servicing error - probably disconnected.")
+      else :
+        return event
+
+  def flush (self) :
+    """flush ()
+
+    Sends any queued packets on the host specified to its designated peers."""
+
+    if self._enet_host :
+      enet_host_flush (self._enet_host)
+
+  def broadcast (self, channelID, Packet packet) :
+    """broadcast (int channelID, Packet packet)
+
+    Queues a packet to be sent to all peers associated with the host."""
+
+    if self._enet_host and packet._enet_packet :
+      enet_host_broadcast (self._enet_host, channelID, packet._enet_packet)
+
+  def __getattr__ (self, name) :
+    # TODO: Add 'peers'.
+    if name == "address" and self._enet_host :
+      address = Address (0, 0)
+      (<Address> address)._enet_address = self._enet_host.address
+
+      return address
+    elif name == "incomingBandwidth" :
+      return self._enet_incomingBandwidth
+    elif name == "outgoingBandwidth" :
+      return self._enet_outgoingBandwidth
+    elif name == "socket" :
+      socket = Socket ()
+      (<Socket> socket)._enet_socket = self._enet_host.socket
+
+      return socket
+    else :
+      raise AttributeError ("Host object has no attribute '" + name + "'")
+
+  def __setattr__ (self, name, value) :
+    if name == "incomingBandwidth" :
+      self._enet_incomingBandwidth = value
+      enet_host_bandwidth_limit (self._enet_host, self._enet_incomingBandwidth, self._enet_outgoingBandwidth)
+    elif name == "outgoingBandwidth" :
+      self._enet_outgoingBandwidth = value
+      enet_host_bandwidth_limit (self._enet_host, self._enet_incomingBandwidth, self._enet_outgoingBandwidth)
+    else :
+      raise AttributeError ("Host object has no attribute '" + name + "'")
+
+# SECTION
+#   Testing
+#
+# TODO
+#   Switch to using the unittest framework.
+
+class test :
+  """test ()
+
+  DESCRIPTION
+
+    A very simple testing class that will change between releases. This is for the maintainer only."""
+
+  def check (self, host) :
+
+    event = host.service (0)
+
+    if event.type == EVENT_TYPE_NONE :
+      pass
+    elif event.type == EVENT_TYPE_CONNECT :
+      print "%s connected to %s via %s." % (host, event.peer.address, event.peer)
+    elif event.type == EVENT_TYPE_DISCONNECT :
+      print "%s disconnected from %s via %s." % (host, event.peer.address, event.peer)
+    elif event.type == EVENT_TYPE_RECEIVE :
+      print "%s received %s containing '%s' from %s via %s." % (host, event.packet, event.packet.data, event.peer.address, event.peer)
+    else :
+      print "%s received invalid event %s of type %u." % (host, event, event.type)
+
+  def test (self) :
+    print "Starting services..."
+
+    host1 = Host (None, 1, 0, 0)
+    host2 = Host (Address ("localhost", 6666), 1, 0, 0)
+
+    print "Connecting %s (client) to %s (server)..." % (host1, host2)
+
+    peer1 = host1.connect (Address ("localhost", 6666), 1)
+
+    print "Entering service loop..."
+
+    count = 0
+
+    while 1 :
+      self.check (host1)
+      self.check (host2)
+
+      count = count + 1
+
+      if not (count % 10000) :
+        print "Sending broadcast..."
+        host1.broadcast (0, Packet ("SuperJoe"))
+
+# SECTION
+#   Ensure ENET is properly initialized and de-initialized.
+
+def _enet_atexit () :
+  enet_deinitialize ()
+
+enet_initialize ()
+atexit.register (_enet_atexit)

+ 50 - 0
pyenet/setup.py

@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+
+# see http://www.python.org/doc/current/dist/setup-script.html 
+#  for distutil options.
+#
+# Original version by Rene Dudfield <illumen@yahoo.com>
+import sys, os, os.path
+from distutils.core import setup, Extension
+
+source_dirs = ['..']
+define_macros = [('HAS_GETHOSTBYNAME_R', None),
+                 ('HAS_GETHOSTBYADDR_R', None),
+                 ('HAS_POLL', None),
+                 ('HAS_FCNTL', None),
+                 ('HAS_MSGHDR_FLAGS', None) ]
+
+libraries = []
+
+# For enet.pyx
+
+os.system("pyrexc enet.pyx")
+source_files = ['enet.c']
+
+# For pyenet
+
+#source_files = ['pyenet.c']
+
+# Build a list of all the source files
+for dir in source_dirs:
+    for file in os.listdir(dir):
+        if '.c' == os.path.splitext(file)[1]:
+            source_files.append(dir + '/' + file)
+
+# Additional Windows dependencies
+if sys.platform == 'win32':
+    define_macros.append(('WIN32', None))
+    libraries.append('ws2_32')
+
+# Go force and multiply
+setup(name="enet", version="0.1",
+      ext_modules=[Extension("enet", 
+                             source_files,
+                             include_dirs=["../include/"],
+			     define_macros=define_macros,
+			     libraries=libraries,
+			     library_dirs=[]
+			    )
+		  ]
+    )
+

+ 325 - 0
tutorial.txt

@@ -0,0 +1,325 @@
+* 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]);
+
+    /* 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.");
+    }
+    ...
+    ...
+    ...
+

+ 371 - 0
unix.c

@@ -0,0 +1,371 @@
+/** 
+ @file  unix.c
+ @brief ENet Unix system specific functions
+*/
+#ifndef WIN32
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+#define ENET_BUILDING_LIB 1
+#include "enet/enet.h"
+
+#ifdef HAS_FCNTL
+#include <fcntl.h>
+#endif
+
+#ifdef HAS_POLL
+#include <sys/poll.h>
+#endif
+
+#ifndef HAS_SOCKLEN_T
+typedef int socklen_t;
+#endif
+
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+static enet_uint32 timeBase = 0;
+
+int
+enet_initialize (void)
+{
+    return 0;
+}
+
+void
+enet_deinitialize (void)
+{
+}
+
+enet_uint32
+enet_time_get (void)
+{
+    struct timeval timeVal;
+
+    gettimeofday (& timeVal, NULL);
+
+    return timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - timeBase;
+}
+
+void
+enet_time_set (enet_uint32 newTimeBase)
+{
+    struct timeval timeVal;
+
+    gettimeofday (& timeVal, NULL);
+    
+    timeBase = timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - newTimeBase;
+}
+
+int
+enet_address_set_host (ENetAddress * address, const char * name)
+{
+    struct hostent * hostEntry = NULL;
+#ifdef HAS_GETHOSTBYNAME_R
+    struct hostent hostData;
+    char buffer [2048];
+    int errnum;
+
+#ifdef linux
+    gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum);
+#else
+    hostEntry = gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & errnum);
+#endif
+#else
+    hostEntry = gethostbyname (name);
+#endif
+
+    if (hostEntry == NULL ||
+        hostEntry -> h_addrtype != AF_INET)
+      return -1;
+
+    address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0];
+
+    return 0;
+}
+
+int
+enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength)
+{
+    struct in_addr in;
+    struct hostent * hostEntry = NULL;
+#ifdef HAS_GETHOSTBYADDR_R
+    struct hostent hostData;
+    char buffer [2048];
+    int errnum;
+
+    in.s_addr = address -> host;
+
+#ifdef linux
+    gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum);
+#else
+    hostEntry = gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & errnum);
+#endif
+#else
+    in.s_addr = address -> host;
+
+    hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET);
+#endif
+
+    if (hostEntry == NULL)
+      return -1;
+
+    strncpy (name, hostEntry -> h_name, nameLength);
+
+    return 0;
+}
+
+ENetSocket
+enet_socket_create (ENetSocketType type, const ENetAddress * address)
+{
+    ENetSocket newSocket = socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0);
+    int receiveBufferSize = ENET_HOST_RECEIVE_BUFFER_SIZE;
+#ifndef HAS_FCNTL
+    int nonBlocking = 1;
+#endif
+    struct sockaddr_in sin;
+
+    if (newSocket == ENET_SOCKET_NULL)
+      return ENET_SOCKET_NULL;
+
+    if (type == ENET_SOCKET_TYPE_DATAGRAM)
+    {
+#ifdef HAS_FCNTL
+        fcntl (newSocket, F_SETFL, O_NONBLOCK | fcntl (newSocket, F_GETFL));
+#else
+        ioctl (newSocket, FIONBIO, & nonBlocking);
+#endif
+
+        setsockopt (newSocket, SOL_SOCKET, SO_RCVBUF, (char *) & receiveBufferSize, sizeof (int));
+    }
+    
+    if (address == NULL)
+      return newSocket;
+
+    memset (& sin, 0, sizeof (struct sockaddr_in));
+
+    sin.sin_family = AF_INET;
+    sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+    sin.sin_addr.s_addr = address -> host;
+
+    if (bind (newSocket, 
+              (struct sockaddr *) & sin,
+              sizeof (struct sockaddr_in)) == -1 ||
+        (type == ENET_SOCKET_TYPE_STREAM &&
+          listen (newSocket, SOMAXCONN) == -1))
+    {
+       close (newSocket);
+
+       return ENET_SOCKET_NULL;
+    }
+
+    return newSocket;
+}
+
+int
+enet_socket_connect (ENetSocket socket, const ENetAddress * address)
+{
+    struct sockaddr_in sin;
+
+    memset (& sin, 0, sizeof (struct sockaddr_in));
+
+    sin.sin_family = AF_INET;
+    sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+    sin.sin_addr.s_addr = address -> host;
+
+    return connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in));
+}
+
+ENetSocket
+enet_socket_accept (ENetSocket socket, ENetAddress * address)
+{
+    int result;
+    struct sockaddr_in sin;
+    socklen_t sinLength = sizeof (struct sockaddr_in);
+
+    result = accept (socket, 
+                     address != NULL ? (struct sockaddr *) & sin : NULL, 
+                     address != NULL ? & sinLength : NULL);
+    
+    if (result == -1)
+      return ENET_SOCKET_NULL;
+
+    if (address != NULL)
+    {
+        address -> host = (enet_uint32) sin.sin_addr.s_addr;
+        address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+    }
+
+    return result;
+} 
+    
+void
+enet_socket_destroy (ENetSocket socket)
+{
+    close (socket);
+}
+
+int
+enet_socket_send (ENetSocket socket,
+                  const ENetAddress * address,
+                  const ENetBuffer * buffers,
+                  size_t bufferCount)
+{
+    struct msghdr msgHdr;
+    struct sockaddr_in sin;
+    int sentLength;
+
+    memset (& msgHdr, 0, sizeof (struct msghdr));
+
+    if (address != NULL)
+    {
+        sin.sin_family = AF_INET;
+        sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+        sin.sin_addr.s_addr = address -> host;
+
+        msgHdr.msg_name = & sin;
+        msgHdr.msg_namelen = sizeof (struct sockaddr_in);
+    }
+
+    msgHdr.msg_iov = (struct iovec *) buffers;
+    msgHdr.msg_iovlen = bufferCount;
+
+    sentLength = sendmsg (socket, & msgHdr, MSG_NOSIGNAL);
+    
+    if (sentLength == -1)
+    {
+       if (errno == EWOULDBLOCK)
+         return 0;
+
+       return -1;
+    }
+
+    return sentLength;
+}
+
+int
+enet_socket_receive (ENetSocket socket,
+                     ENetAddress * address,
+                     ENetBuffer * buffers,
+                     size_t bufferCount)
+{
+    struct msghdr msgHdr;
+    struct sockaddr_in sin;
+    int recvLength;
+
+    memset (& msgHdr, 0, sizeof (struct msghdr));
+
+    if (address != NULL)
+    {
+        msgHdr.msg_name = & sin;
+        msgHdr.msg_namelen = sizeof (struct sockaddr_in);
+    }
+
+    msgHdr.msg_iov = (struct iovec *) buffers;
+    msgHdr.msg_iovlen = bufferCount;
+
+    recvLength = recvmsg (socket, & msgHdr, MSG_NOSIGNAL);
+
+    if (recvLength == -1)
+    {
+       if (errno == EWOULDBLOCK)
+         return 0;
+
+       return -1;
+    }
+
+#ifdef HAS_MSGHDR_FLAGS
+    if (msgHdr.msg_flags & MSG_TRUNC)
+      return -1;
+#endif
+
+    if (address != NULL)
+    {
+        address -> host = (enet_uint32) sin.sin_addr.s_addr;
+        address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+    }
+
+    return recvLength;
+}
+
+int
+enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout)
+{
+#ifdef HAS_POLL
+    struct pollfd pollSocket;
+    int pollCount;
+    
+    pollSocket.fd = socket;
+    pollSocket.events = 0;
+
+    if (* condition & ENET_SOCKET_WAIT_SEND)
+      pollSocket.events |= POLLOUT;
+
+    if (* condition & ENET_SOCKET_WAIT_RECEIVE)
+      pollSocket.events |= POLLIN;
+
+    pollCount = poll (& pollSocket, 1, timeout);
+
+    if (pollCount < 0)
+      return -1;
+
+    * condition = ENET_SOCKET_WAIT_NONE;
+
+    if (pollCount == 0)
+      return 0;
+
+    if (pollSocket.revents & POLLOUT)
+      * condition |= ENET_SOCKET_WAIT_SEND;
+    
+    if (pollSocket.revents & POLLIN)
+      * condition |= ENET_SOCKET_WAIT_RECEIVE;
+
+    return 0;
+#else
+    fd_set readSet, writeSet;
+    struct timeval timeVal;
+    int selectCount;
+
+    timeVal.tv_sec = timeout / 1000;
+    timeVal.tv_usec = (timeout % 1000) * 1000;
+
+    FD_ZERO (& readSet);
+    FD_ZERO (& writeSet);
+
+    if (* condition & ENET_SOCKET_WAIT_SEND)
+      FD_SET (socket, & writeSet);
+
+    if (* condition & ENET_SOCKET_WAIT_RECEIVE)
+      FD_SET (socket, & readSet);
+
+    selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal);
+
+    if (selectCount < 0)
+      return -1;
+
+    * condition = ENET_SOCKET_WAIT_NONE;
+
+    if (selectCount == 0)
+      return 0;
+
+    if (FD_ISSET (socket, & writeSet))
+      * condition |= ENET_SOCKET_WAIT_SEND;
+
+    if (FD_ISSET (socket, & readSet))
+      * condition |= ENET_SOCKET_WAIT_RECEIVE;
+
+    return 0;
+#endif
+}
+
+#endif
+

+ 295 - 0
win32.c

@@ -0,0 +1,295 @@
+/** 
+ @file  win32.c
+ @brief ENet Win32 system specific functions
+*/
+#ifdef WIN32
+
+#include <time.h>
+#define ENET_BUILDING_LIB 1
+#include "enet/enet.h"
+
+static enet_uint32 timeBase = 0;
+
+int
+enet_initialize (void)
+{
+    WORD versionRequested = MAKEWORD (1, 1);
+    WSADATA wsaData;
+   
+    if (WSAStartup (versionRequested, & wsaData))
+       return -1;
+
+    if (LOBYTE (wsaData.wVersion) != 1||
+        HIBYTE (wsaData.wVersion) != 1)
+    {
+       WSACleanup ();
+       
+       return -1;
+    }
+
+    timeBeginPeriod (1);
+
+    return 0;
+}
+
+void
+enet_deinitialize (void)
+{
+    timeEndPeriod (1);
+
+    WSACleanup ();
+}
+
+enet_uint32
+enet_time_get (void)
+{
+    return (enet_uint32) timeGetTime () - timeBase;
+}
+
+void
+enet_time_set (enet_uint32 newTimeBase)
+{
+    timeBase = (enet_uint32) timeGetTime () - newTimeBase;
+}
+
+int
+enet_address_set_host (ENetAddress * address, const char * name)
+{
+    struct hostent * hostEntry;
+
+    hostEntry = gethostbyname (name);
+    if (hostEntry == NULL ||
+        hostEntry -> h_addrtype != AF_INET)
+      return -1;
+
+    address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0];
+
+    return 0;
+}
+
+int
+enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength)
+{
+    struct in_addr in;
+    struct hostent * hostEntry;
+    
+    in.s_addr = address -> host;
+    
+    hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET);
+    if (hostEntry == NULL)
+      return -1;
+
+    strncpy (name, hostEntry -> h_name, nameLength);
+
+    return 0;
+}
+
+ENetSocket
+enet_socket_create (ENetSocketType type, const ENetAddress * address)
+{
+    ENetSocket newSocket = socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0);
+    int nonBlocking = 1,
+        receiveBufferSize = ENET_HOST_RECEIVE_BUFFER_SIZE;
+    struct sockaddr_in sin;
+
+    if (newSocket == ENET_SOCKET_NULL)
+      return ENET_SOCKET_NULL;
+
+    if (type == ENET_SOCKET_TYPE_DATAGRAM)
+    {
+        ioctlsocket (newSocket, FIONBIO, & nonBlocking);
+
+        setsockopt (newSocket, SOL_SOCKET, SO_RCVBUF, (char *) & receiveBufferSize, sizeof (int));
+    }
+
+    memset (& sin, 0, sizeof (struct sockaddr_in));
+
+    sin.sin_family = AF_INET;
+    
+    if (address != NULL)
+    {
+       sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+       sin.sin_addr.s_addr = address -> host;
+    }
+    else
+    {
+       sin.sin_port = 0;
+       sin.sin_addr.s_addr = INADDR_ANY;
+    }
+
+    if (bind (newSocket,    
+              (struct sockaddr *) & sin,
+              sizeof (struct sockaddr_in)) == SOCKET_ERROR ||
+        (type == ENET_SOCKET_TYPE_STREAM &&
+          address != NULL &&
+          listen (newSocket, SOMAXCONN) == SOCKET_ERROR))
+    {
+       closesocket (newSocket);
+
+       return ENET_SOCKET_NULL;
+    }
+
+    return newSocket;
+}
+
+int
+enet_socket_connect (ENetSocket socket, const ENetAddress * address)
+{
+    struct sockaddr_in sin;
+
+    memset (& sin, 0, sizeof (struct sockaddr_in));
+
+    sin.sin_family = AF_INET;
+    sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+    sin.sin_addr.s_addr = address -> host;
+
+    return connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in));
+}
+
+ENetSocket
+enet_socket_accept (ENetSocket socket, ENetAddress * address)
+{
+    int result;
+    struct sockaddr_in sin;
+    int sinLength = sizeof (struct sockaddr_in);
+
+    result = accept (socket, 
+                     address != NULL ? (struct sockaddr *) & sin : NULL, 
+                     address != NULL ? & sinLength : NULL);
+
+    if (result == -1)
+      return ENET_SOCKET_NULL;
+
+    if (address != NULL)
+    {
+        address -> host = (enet_uint32) sin.sin_addr.s_addr;
+        address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+    }
+
+    return result;
+}
+
+void
+enet_socket_destroy (ENetSocket socket)
+{
+    closesocket (socket);
+}
+
+int
+enet_socket_send (ENetSocket socket,
+                  const ENetAddress * address,
+                  const ENetBuffer * buffers,
+                  size_t bufferCount)
+{
+    struct sockaddr_in sin;
+    DWORD sentLength;
+
+    if (address != NULL)
+    {
+        sin.sin_family = AF_INET;
+        sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+        sin.sin_addr.s_addr = address -> host;
+    }
+
+    if (WSASendTo (socket, 
+                   (LPWSABUF) buffers,
+                   (DWORD) bufferCount,
+                   & sentLength,
+                   0,
+                   address != NULL ? (struct sockaddr *) & sin : 0,
+                   address != NULL ? sizeof (struct sockaddr_in) : 0,
+                   NULL,
+                   NULL) == SOCKET_ERROR)
+    {
+       if (WSAGetLastError () == WSAEWOULDBLOCK)
+         return 0;
+
+       return -1;
+    }
+
+    return (int) sentLength;
+}
+
+int
+enet_socket_receive (ENetSocket socket,
+                     ENetAddress * address,
+                     ENetBuffer * buffers,
+                     size_t bufferCount)
+{
+    DWORD sinLength = sizeof (struct sockaddr_in),
+          flags = 0,
+          recvLength;
+    struct sockaddr_in sin;
+
+    if (WSARecvFrom (socket,
+                     (LPWSABUF) buffers,
+                     (DWORD) bufferCount,
+                     & recvLength,
+                     & flags,
+                     address != NULL ? (struct sockaddr *) & sin : NULL,
+                     address != NULL ? & sinLength : NULL,
+                     NULL,
+                     NULL) == SOCKET_ERROR)
+    {
+       switch (WSAGetLastError ())
+       {
+       case WSAEWOULDBLOCK:
+       case WSAECONNRESET:
+          return 0;
+       }
+
+       return -1;
+    }
+
+    if (flags & MSG_PARTIAL)
+      return -1;
+
+    if (address != NULL)
+    {
+        address -> host = (enet_uint32) sin.sin_addr.s_addr;
+        address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+    }
+
+    return (int) recvLength;
+}
+
+int
+enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout)
+{
+    fd_set readSet, writeSet;
+    struct timeval timeVal;
+    int selectCount;
+    
+    timeVal.tv_sec = timeout / 1000;
+    timeVal.tv_usec = (timeout % 1000) * 1000;
+    
+    FD_ZERO (& readSet);
+    FD_ZERO (& writeSet);
+
+    if (* condition & ENET_SOCKET_WAIT_SEND)
+      FD_SET (socket, & writeSet);
+
+    if (* condition & ENET_SOCKET_WAIT_RECEIVE)
+      FD_SET (socket, & readSet);
+
+    selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal);
+
+    if (selectCount < 0)
+      return -1;
+
+    * condition = ENET_SOCKET_WAIT_NONE;
+
+    if (selectCount == 0)
+      return 0;
+
+    if (FD_ISSET (socket, & writeSet))
+      * condition |= ENET_SOCKET_WAIT_SEND;
+    
+    if (FD_ISSET (socket, & readSet))
+      * condition |= ENET_SOCKET_WAIT_RECEIVE;
+
+    return 0;
+} 
+
+#endif
+