/** * include/enet.h - a Single-Header auto-generated variant of enet.h library. * * Usage: * #define ENET_IMPLEMENTATION exactly in ONE source file right BEFORE including the library, like: * * #define ENET_IMPLEMENTATION * #include * * License: * The MIT License (MIT) * * Copyright (c) 2002-2016 Lee Salzman * Copyright (c) 2017-2021 Vladyslav Hrytsenko, Dominik Madarász * * 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. * */ #ifndef ENET_INCLUDE_H #define ENET_INCLUDE_H #include #include #include #include #include #define ENET_VERSION_MAJOR 2 #define ENET_VERSION_MINOR 3 #define ENET_VERSION_PATCH 9 #define ENET_VERSION_CREATE(major, minor, patch) (((major)<<16) | ((minor)<<8) | (patch)) #define ENET_VERSION_GET_MAJOR(version) (((version)>>16)&0xFF) #define ENET_VERSION_GET_MINOR(version) (((version)>>8)&0xFF) #define ENET_VERSION_GET_PATCH(version) ((version)&0xFF) #define ENET_VERSION ENET_VERSION_CREATE(ENET_VERSION_MAJOR, ENET_VERSION_MINOR, ENET_VERSION_PATCH) #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)) // =======================================================================// // ! // ! System differences // ! // =======================================================================// #if defined(_WIN32) #if defined(_MSC_VER) && defined(ENET_IMPLEMENTATION) #pragma warning (disable: 4267) // size_t to int conversion #pragma warning (disable: 4244) // 64bit to 32bit int #pragma warning (disable: 4018) // signed/unsigned mismatch #pragma warning (disable: 4146) // unary minus operator applied to unsigned type #endif #ifndef ENET_NO_PRAGMA_LINK #ifndef __GNUC__ #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "winmm.lib") #endif #endif #if _MSC_VER >= 1910 /* It looks like there were changes as of Visual Studio 2017 and there are no 32/64 bit versions of _InterlockedExchange[operation], only InterlockedExchange[operation] (without leading underscore), so we have to distinguish between compiler versions */ #define NOT_UNDERSCORED_INTERLOCKED_EXCHANGE #endif #ifdef __GNUC__ #if (_WIN32_WINNT < 0x0600) #undef _WIN32_WINNT #define _WIN32_WINNT 0x0600 #endif #endif #include #include #include #include #if defined(_WIN32) && defined(_MSC_VER) #if _MSC_VER < 1900 typedef struct timespec { long tv_sec; long tv_nsec; }; #endif #define CLOCK_MONOTONIC 0 #endif typedef SOCKET ENetSocket; #define 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 #ifdef ENET_DLL #ifdef ENET_IMPLEMENTATION #define ENET_API __declspec( dllexport ) #else #define ENET_API __declspec( dllimport ) #endif // ENET_IMPLEMENTATION #else #define ENET_API extern #endif // ENET_DLL typedef fd_set ENetSocketSet; #define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO(&(sockset)) #define ENET_SOCKETSET_ADD(sockset, socket) FD_SET(socket, &(sockset)) #define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR(socket, &(sockset)) #define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET(socket, &(sockset)) #else #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __APPLE__ #include #include #include #endif #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif #ifdef MSG_MAXIOVLEN #define ENET_BUFFER_MAXIMUM MSG_MAXIOVLEN #endif typedef int ENetSocket; #define ENET_SOCKET_NULL -1 #define ENET_HOST_TO_NET_16(value) (htons(value)) /**< macro that converts host to net byte-order of a 16-bit value */ #define ENET_HOST_TO_NET_32(value) (htonl(value)) /**< macro that converts host to net byte-order of a 32-bit value */ #define ENET_NET_TO_HOST_16(value) (ntohs(value)) /**< macro that converts net to host byte-order of a 16-bit value */ #define ENET_NET_TO_HOST_32(value) (ntohl(value)) /**< macro that converts net to host byte-order of a 32-bit value */ typedef struct { void * data; size_t dataLength; } ENetBuffer; #define ENET_CALLBACK #define ENET_API extern typedef fd_set ENetSocketSet; #define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO(&(sockset)) #define ENET_SOCKETSET_ADD(sockset, socket) FD_SET(socket, &(sockset)) #define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR(socket, &(sockset)) #define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET(socket, &(sockset)) #endif #ifdef __GNUC__ #define ENET_DEPRECATED(func) func __attribute__ ((deprecated)) #elif defined(_MSC_VER) #define ENET_DEPRECATED(func) __declspec(deprecated) func #else #pragma message("WARNING: Please ENET_DEPRECATED for this compiler") #define ENET_DEPRECATED(func) func #endif #ifndef ENET_BUFFER_MAXIMUM #define ENET_BUFFER_MAXIMUM (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS) #endif #define ENET_UNUSED(x) (void)x; #define ENET_MAX(x, y) ((x) > (y) ? (x) : (y)) #define ENET_MIN(x, y) ((x) < (y) ? (x) : (y)) #define ENET_IPV6 1 static const struct in6_addr enet_v4_anyaddr = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }}}; static const struct in6_addr enet_v4_noaddr = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}; static const struct in6_addr enet_v4_localhost = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01 }}}; static const struct in6_addr enet_v6_anyaddr = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}}; static const struct in6_addr enet_v6_noaddr = {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}; static const struct in6_addr enet_v6_localhost = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}}; #define ENET_HOST_ANY in6addr_any #define ENET_HOST_BROADCAST 0xFFFFFFFFU #define ENET_PORT_ANY 0 #ifdef __cplusplus extern "C" { #endif // =======================================================================// // ! // ! Basic stuff // ! // =======================================================================// typedef uint8_t enet_uint8; /**< unsigned 8-bit type */ typedef uint16_t enet_uint16; /**< unsigned 16-bit type */ typedef uint32_t enet_uint32; /**< unsigned 32-bit type */ typedef uint64_t enet_uint64; /**< unsigned 64-bit type */ typedef enet_uint32 ENetVersion; typedef struct _ENetPacket ENetPacket; typedef struct _ENetCallbacks { void *(ENET_CALLBACK *malloc) (size_t size); void (ENET_CALLBACK *free) (void *memory); void (ENET_CALLBACK *no_memory) (void); ENetPacket *(ENET_CALLBACK *packet_create) (const void *data, size_t dataLength, enet_uint32 flags); void (ENET_CALLBACK *packet_destroy) (ENetPacket *packet); } ENetCallbacks; extern void *enet_malloc(size_t); extern void enet_free(void *); extern ENetPacket* enet_packet_create(const void*,size_t,enet_uint32); extern int enet_packet_resize(ENetPacket*, size_t); extern ENetPacket* enet_packet_copy(ENetPacket*); extern void enet_packet_destroy(ENetPacket*); // =======================================================================// // ! // ! List // ! // =======================================================================// typedef struct _ENetListNode { struct _ENetListNode *next; struct _ENetListNode *previous; } ENetListNode; typedef ENetListNode *ENetListIterator; typedef struct _ENetList { ENetListNode sentinel; } ENetList; extern ENetListIterator enet_list_insert(ENetListIterator, void *); extern ENetListIterator enet_list_move(ENetListIterator, void *, void *); extern void *enet_list_remove(ENetListIterator); extern void enet_list_clear(ENetList *); 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) // =======================================================================// // ! // ! Protocol // ! // =======================================================================// 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 = 65536, ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1, ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255, ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xFFF, ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT = 1024 * 1024 }; typedef enum _ENetProtocolCommand { ENET_PROTOCOL_COMMAND_NONE = 0, ENET_PROTOCOL_COMMAND_ACKNOWLEDGE = 1, ENET_PROTOCOL_COMMAND_CONNECT = 2, ENET_PROTOCOL_COMMAND_VERIFY_CONNECT = 3, ENET_PROTOCOL_COMMAND_DISCONNECT = 4, ENET_PROTOCOL_COMMAND_PING = 5, ENET_PROTOCOL_COMMAND_SEND_RELIABLE = 6, ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE = 7, ENET_PROTOCOL_COMMAND_SEND_FRAGMENT = 8, ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 9, ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 10, ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11, ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT = 12, ENET_PROTOCOL_COMMAND_COUNT = 13, ENET_PROTOCOL_COMMAND_MASK = 0x0F } ENetProtocolCommand; typedef enum _ENetProtocolFlag { ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 << 7), ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 << 6), ENET_PROTOCOL_HEADER_FLAG_COMPRESSED = (1 << 14), ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 << 15), ENET_PROTOCOL_HEADER_FLAG_MASK = ENET_PROTOCOL_HEADER_FLAG_COMPRESSED | ENET_PROTOCOL_HEADER_FLAG_SENT_TIME, ENET_PROTOCOL_HEADER_SESSION_MASK = (3 << 12), ENET_PROTOCOL_HEADER_SESSION_SHIFT = 12 } ENetProtocolFlag; #ifdef _MSC_VER #pragma pack(push, 1) #define ENET_PACKED #elif defined(__GNUC__) || defined(__clang__) #define ENET_PACKED __attribute__ ((packed)) #else #define ENET_PACKED #endif typedef struct _ENetProtocolHeader { enet_uint16 peerID; enet_uint16 sentTime; } ENET_PACKED ENetProtocolHeader; typedef struct _ENetProtocolCommandHeader { enet_uint8 command; enet_uint8 channelID; enet_uint16 reliableSequenceNumber; } ENET_PACKED ENetProtocolCommandHeader; typedef struct _ENetProtocolAcknowledge { ENetProtocolCommandHeader header; enet_uint16 receivedReliableSequenceNumber; enet_uint16 receivedSentTime; } ENET_PACKED ENetProtocolAcknowledge; typedef struct _ENetProtocolConnect { ENetProtocolCommandHeader header; enet_uint16 outgoingPeerID; enet_uint8 incomingSessionID; enet_uint8 outgoingSessionID; enet_uint32 mtu; enet_uint32 windowSize; enet_uint32 channelCount; enet_uint32 incomingBandwidth; enet_uint32 outgoingBandwidth; enet_uint32 packetThrottleInterval; enet_uint32 packetThrottleAcceleration; enet_uint32 packetThrottleDeceleration; enet_uint32 connectID; enet_uint32 data; } ENET_PACKED ENetProtocolConnect; typedef struct _ENetProtocolVerifyConnect { ENetProtocolCommandHeader header; enet_uint16 outgoingPeerID; enet_uint8 incomingSessionID; enet_uint8 outgoingSessionID; enet_uint32 mtu; enet_uint32 windowSize; enet_uint32 channelCount; enet_uint32 incomingBandwidth; enet_uint32 outgoingBandwidth; enet_uint32 packetThrottleInterval; enet_uint32 packetThrottleAcceleration; enet_uint32 packetThrottleDeceleration; enet_uint32 connectID; } ENET_PACKED ENetProtocolVerifyConnect; typedef struct _ENetProtocolBandwidthLimit { ENetProtocolCommandHeader header; enet_uint32 incomingBandwidth; enet_uint32 outgoingBandwidth; } ENET_PACKED ENetProtocolBandwidthLimit; typedef struct _ENetProtocolThrottleConfigure { ENetProtocolCommandHeader header; enet_uint32 packetThrottleInterval; enet_uint32 packetThrottleAcceleration; enet_uint32 packetThrottleDeceleration; } ENET_PACKED ENetProtocolThrottleConfigure; typedef struct _ENetProtocolDisconnect { ENetProtocolCommandHeader header; enet_uint32 data; } ENET_PACKED ENetProtocolDisconnect; typedef struct _ENetProtocolPing { ENetProtocolCommandHeader header; } ENET_PACKED ENetProtocolPing; typedef struct _ENetProtocolSendReliable { ENetProtocolCommandHeader header; enet_uint16 dataLength; } ENET_PACKED ENetProtocolSendReliable; typedef struct _ENetProtocolSendUnreliable { ENetProtocolCommandHeader header; enet_uint16 unreliableSequenceNumber; enet_uint16 dataLength; } ENET_PACKED ENetProtocolSendUnreliable; typedef struct _ENetProtocolSendUnsequenced { ENetProtocolCommandHeader header; enet_uint16 unsequencedGroup; enet_uint16 dataLength; } ENET_PACKED ENetProtocolSendUnsequenced; typedef struct _ENetProtocolSendFragment { ENetProtocolCommandHeader header; enet_uint16 startSequenceNumber; enet_uint16 dataLength; enet_uint32 fragmentCount; enet_uint32 fragmentNumber; enet_uint32 totalLength; enet_uint32 fragmentOffset; } ENET_PACKED ENetProtocolSendFragment; typedef union _ENetProtocol { ENetProtocolCommandHeader header; ENetProtocolAcknowledge acknowledge; ENetProtocolConnect connect; ENetProtocolVerifyConnect verifyConnect; ENetProtocolDisconnect disconnect; ENetProtocolPing ping; ENetProtocolSendReliable sendReliable; ENetProtocolSendUnreliable sendUnreliable; ENetProtocolSendUnsequenced sendUnsequenced; ENetProtocolSendFragment sendFragment; ENetProtocolBandwidthLimit bandwidthLimit; ENetProtocolThrottleConfigure throttleConfigure; } ENET_PACKED ENetProtocol; #ifdef _MSC_VER #pragma pack(pop) #endif // =======================================================================// // ! // ! General ENet structs/enums // ! // =======================================================================// typedef enum _ENetSocketType { ENET_SOCKET_TYPE_STREAM = 1, ENET_SOCKET_TYPE_DATAGRAM = 2 } ENetSocketType; typedef enum _ENetSocketWait { ENET_SOCKET_WAIT_NONE = 0, ENET_SOCKET_WAIT_SEND = (1 << 0), ENET_SOCKET_WAIT_RECEIVE = (1 << 1), ENET_SOCKET_WAIT_INTERRUPT = (1 << 2) } ENetSocketWait; typedef enum _ENetSocketOption { ENET_SOCKOPT_NONBLOCK = 1, ENET_SOCKOPT_BROADCAST = 2, ENET_SOCKOPT_RCVBUF = 3, ENET_SOCKOPT_SNDBUF = 4, ENET_SOCKOPT_REUSEADDR = 5, ENET_SOCKOPT_RCVTIMEO = 6, ENET_SOCKOPT_SNDTIMEO = 7, ENET_SOCKOPT_ERROR = 8, ENET_SOCKOPT_NODELAY = 9, ENET_SOCKOPT_IPV6_V6ONLY = 10, } ENetSocketOption; typedef enum _ENetSocketShutdown { ENET_SOCKET_SHUTDOWN_READ = 0, ENET_SOCKET_SHUTDOWN_WRITE = 1, ENET_SOCKET_SHUTDOWN_READ_WRITE = 2 } ENetSocketShutdown; /** * Portable internet address structure. * * The host must be specified in network byte-order, and the port must be in host * byte-order. The constant ENET_HOST_ANY may be used to specify the default * server host. The constant ENET_HOST_BROADCAST may be used to specify the * broadcast address (255.255.255.255). This makes sense for enet_host_connect, * but not for enet_host_create. Once a server responds to a broadcast, the * address is updated from ENET_HOST_BROADCAST to the server's actual IP address. */ typedef struct _ENetAddress { struct in6_addr host; enet_uint16 port; enet_uint16 sin6_scope_id; } ENetAddress; #define in6_equal(in6_addr_a, in6_addr_b) (memcmp(&in6_addr_a, &in6_addr_b, sizeof(struct in6_addr)) == 0) /** * Packet flag bit constants. * * The host must be specified in network byte-order, and the port must be in * host byte-order. The constant ENET_HOST_ANY may be used to specify the * default server host. * * @sa ENetPacket */ typedef enum _ENetPacketFlag { ENET_PACKET_FLAG_RELIABLE = (1 << 0), /** packet must be received by the target peer and resend attempts should be made until the packet is delivered */ ENET_PACKET_FLAG_UNSEQUENCED = (1 << 1), /** packet will not be sequenced with other packets not supported for reliable packets */ ENET_PACKET_FLAG_NO_ALLOCATE = (1 << 2), /** packet will not allocate data, and user must supply it instead */ ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT = (1 << 3), /** packet will be fragmented using unreliable (instead of reliable) sends if it exceeds the MTU */ ENET_PACKET_FLAG_SENT = (1 << 8), /** whether the packet has been sent from all queues it has been entered into */ } ENetPacketFlag; typedef void (ENET_CALLBACK *ENetPacketFreeCallback)(void *); /** * ENet packet structure. * * An ENet data packet that may be sent to or received from a peer. The shown * fields should only be read and never modified. The data field contains the * allocated data for the packet. The dataLength fields specifies the length * of the allocated data. The flags field is either 0 (specifying no flags), * or a bitwise-or of any combination of the following flags: * * ENET_PACKET_FLAG_RELIABLE - packet must be received by the target peer and resend attempts should be made until the packet is delivered * ENET_PACKET_FLAG_UNSEQUENCED - packet will not be sequenced with other packets (not supported for reliable packets) * ENET_PACKET_FLAG_NO_ALLOCATE - packet will not allocate data, and user must supply it instead * ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT - packet will be fragmented using unreliable (instead of reliable) sends if it exceeds the MTU * ENET_PACKET_FLAG_SENT - whether the packet has been sent from all queues it has been entered into * @sa ENetPacketFlag */ typedef struct _ENetPacket { size_t referenceCount; /**< internal use only */ enet_uint32 flags; /**< bitwise-or of ENetPacketFlag constants */ enet_uint8 * data; /**< allocated data for packet */ size_t dataLength; /**< length of data */ ENetPacketFreeCallback freeCallback; /**< function to be called when the packet is no longer in use */ void * userData; /**< application private data, may be freely modified */ } ENetPacket; typedef struct _ENetAcknowledgement { ENetListNode acknowledgementList; enet_uint32 sentTime; ENetProtocol command; } ENetAcknowledgement; typedef struct _ENetOutgoingCommand { ENetListNode outgoingCommandList; enet_uint16 reliableSequenceNumber; enet_uint16 unreliableSequenceNumber; enet_uint32 sentTime; enet_uint32 roundTripTimeout; enet_uint32 roundTripTimeoutLimit; enet_uint32 fragmentOffset; enet_uint16 fragmentLength; enet_uint16 sendAttempts; ENetProtocol command; ENetPacket * packet; } ENetOutgoingCommand; typedef struct _ENetIncomingCommand { ENetListNode incomingCommandList; enet_uint16 reliableSequenceNumber; enet_uint16 unreliableSequenceNumber; ENetProtocol command; enet_uint32 fragmentCount; enet_uint32 fragmentsRemaining; enet_uint32 *fragments; ENetPacket * packet; } ENetIncomingCommand; typedef enum _ENetPeerState { ENET_PEER_STATE_DISCONNECTED = 0, ENET_PEER_STATE_CONNECTING = 1, ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2, ENET_PEER_STATE_CONNECTION_PENDING = 3, ENET_PEER_STATE_CONNECTION_SUCCEEDED = 4, ENET_PEER_STATE_CONNECTED = 5, ENET_PEER_STATE_DISCONNECT_LATER = 6, ENET_PEER_STATE_DISCONNECTING = 7, ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT = 8, ENET_PEER_STATE_ZOMBIE = 9 } ENetPeerState; enum { ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024, ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024, ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000, ENET_HOST_DEFAULT_MTU = 1392, ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE = 32 * 1024 * 1024, ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA = 32 * 1024 * 1024, ENET_PEER_DEFAULT_ROUND_TRIP_TIME = 500, ENET_PEER_DEFAULT_PACKET_THROTTLE = 32, ENET_PEER_PACKET_THROTTLE_SCALE = 32, ENET_PEER_PACKET_THROTTLE_COUNTER = 7, ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2, ENET_PEER_PACKET_THROTTLE_DECELERATION = 2, ENET_PEER_PACKET_THROTTLE_INTERVAL = 5000, ENET_PEER_PACKET_LOSS_SCALE = (1 << 16), ENET_PEER_PACKET_LOSS_INTERVAL = 10000, ENET_PEER_WINDOW_SIZE_SCALE = 64 * 1024, ENET_PEER_TIMEOUT_LIMIT = 32, ENET_PEER_TIMEOUT_MINIMUM = 5000, ENET_PEER_TIMEOUT_MAXIMUM = 30000, ENET_PEER_PING_INTERVAL = 500, ENET_PEER_UNSEQUENCED_WINDOWS = 64, ENET_PEER_UNSEQUENCED_WINDOW_SIZE = 1024, ENET_PEER_FREE_UNSEQUENCED_WINDOWS = 32, ENET_PEER_RELIABLE_WINDOWS = 16, ENET_PEER_RELIABLE_WINDOW_SIZE = 0x1000, ENET_PEER_FREE_RELIABLE_WINDOWS = 8 }; typedef struct _ENetChannel { enet_uint16 outgoingReliableSequenceNumber; enet_uint16 outgoingUnreliableSequenceNumber; enet_uint16 usedReliableWindows; enet_uint16 reliableWindows[ENET_PEER_RELIABLE_WINDOWS]; enet_uint16 incomingReliableSequenceNumber; enet_uint16 incomingUnreliableSequenceNumber; ENetList incomingReliableCommands; ENetList incomingUnreliableCommands; } ENetChannel; /** * An ENet peer which data packets may be sent or received from. * * No fields should be modified unless otherwise specified. */ typedef struct _ENetPeer { ENetListNode dispatchList; struct _ENetHost *host; enet_uint16 outgoingPeerID; enet_uint16 incomingPeerID; enet_uint32 connectID; enet_uint8 outgoingSessionID; enet_uint8 incomingSessionID; ENetAddress address; /**< Internet address of the peer */ void * data; /**< Application private data, may be freely modified */ ENetPeerState state; ENetChannel * channels; size_t channelCount; /**< Number of channels allocated for communication with peer */ enet_uint32 incomingBandwidth; /**< Downstream bandwidth of the client in bytes/second */ enet_uint32 outgoingBandwidth; /**< Upstream bandwidth of the client in bytes/second */ enet_uint32 incomingBandwidthThrottleEpoch; enet_uint32 outgoingBandwidthThrottleEpoch; enet_uint32 incomingDataTotal; enet_uint64 totalDataReceived; enet_uint32 outgoingDataTotal; enet_uint64 totalDataSent; enet_uint32 lastSendTime; enet_uint32 lastReceiveTime; enet_uint32 nextTimeout; enet_uint32 earliestTimeout; enet_uint32 packetLossEpoch; enet_uint32 packetsSent; enet_uint64 totalPacketsSent; /**< total number of packets sent during a session */ enet_uint32 packetsLost; enet_uint32 totalPacketsLost; /**< total number of packets lost during a session */ enet_uint32 packetLoss; /**< mean packet loss of reliable packets as a ratio with respect to the constant ENET_PEER_PACKET_LOSS_SCALE */ enet_uint32 packetLossVariance; enet_uint32 packetThrottle; enet_uint32 packetThrottleLimit; enet_uint32 packetThrottleCounter; enet_uint32 packetThrottleEpoch; enet_uint32 packetThrottleAcceleration; enet_uint32 packetThrottleDeceleration; enet_uint32 packetThrottleInterval; enet_uint32 pingInterval; enet_uint32 timeoutLimit; enet_uint32 timeoutMinimum; enet_uint32 timeoutMaximum; enet_uint32 lastRoundTripTime; enet_uint32 lowestRoundTripTime; enet_uint32 lastRoundTripTimeVariance; enet_uint32 highestRoundTripTimeVariance; enet_uint32 roundTripTime; /**< mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgement */ enet_uint32 roundTripTimeVariance; enet_uint32 mtu; enet_uint32 windowSize; enet_uint32 reliableDataInTransit; enet_uint16 outgoingReliableSequenceNumber; ENetList acknowledgements; ENetList sentReliableCommands; ENetList sentUnreliableCommands; ENetList outgoingReliableCommands; ENetList outgoingUnreliableCommands; ENetList dispatchedCommands; int needsDispatch; enet_uint16 incomingUnsequencedGroup; enet_uint16 outgoingUnsequencedGroup; enet_uint32 unsequencedWindow[ENET_PEER_UNSEQUENCED_WINDOW_SIZE / 32]; enet_uint32 eventData; size_t totalWaitingData; } ENetPeer; /** An ENet packet compressor for compressing UDP packets before socket sends or receives. */ typedef struct _ENetCompressor { /** Context data for the compressor. Must be non-NULL. */ void *context; /** Compresses from inBuffers[0:inBufferCount-1], containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */ size_t(ENET_CALLBACK * compress) (void *context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit); /** Decompresses from inData, containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */ size_t(ENET_CALLBACK * decompress) (void *context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit); /** Destroys the context when compression is disabled or the host is destroyed. May be NULL. */ void (ENET_CALLBACK * destroy)(void *context); } ENetCompressor; /** Callback that computes the checksum of the data held in buffers[0:bufferCount-1] */ typedef enet_uint32 (ENET_CALLBACK * ENetChecksumCallback)(const ENetBuffer *buffers, size_t bufferCount); /** Callback for intercepting received raw UDP packets. Should return 1 to intercept, 0 to ignore, or -1 to propagate an error. */ typedef int (ENET_CALLBACK * ENetInterceptCallback)(struct _ENetHost *host, void *event); /** An ENet host for communicating with peers. * * No fields should be modified unless otherwise stated. * * @sa enet_host_create() * @sa enet_host_destroy() * @sa enet_host_connect() * @sa enet_host_service() * @sa enet_host_flush() * @sa enet_host_broadcast() * @sa enet_host_compress() * @sa enet_host_channel_limit() * @sa enet_host_bandwidth_limit() * @sa enet_host_bandwidth_throttle() */ typedef struct _ENetHost { ENetSocket socket; ENetAddress address; /**< Internet address of the host */ enet_uint32 incomingBandwidth; /**< downstream bandwidth of the host */ enet_uint32 outgoingBandwidth; /**< upstream bandwidth of the host */ enet_uint32 bandwidthThrottleEpoch; enet_uint32 mtu; enet_uint32 randomSeed; int recalculateBandwidthLimits; ENetPeer * peers; /**< array of peers allocated for this host */ size_t peerCount; /**< number of peers allocated for this host */ size_t channelLimit; /**< maximum number of channels allowed for connected peers */ enet_uint32 serviceTime; ENetList dispatchQueue; int continueSending; size_t packetSize; enet_uint16 headerFlags; ENetProtocol commands[ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS]; size_t commandCount; ENetBuffer buffers[ENET_BUFFER_MAXIMUM]; size_t bufferCount; ENetChecksumCallback checksum; /**< callback the user can set to enable packet checksums for this host */ ENetCompressor compressor; enet_uint8 packetData[2][ENET_PROTOCOL_MAXIMUM_MTU]; ENetAddress receivedAddress; enet_uint8 * receivedData; size_t receivedDataLength; enet_uint32 totalSentData; /**< total data sent, user should reset to 0 as needed to prevent overflow */ enet_uint32 totalSentPackets; /**< total UDP packets sent, user should reset to 0 as needed to prevent overflow */ enet_uint32 totalReceivedData; /**< total data received, user should reset to 0 as needed to prevent overflow */ enet_uint32 totalReceivedPackets; /**< total UDP packets received, user should reset to 0 as needed to prevent overflow */ ENetInterceptCallback intercept; /**< callback the user can set to intercept received raw UDP packets */ size_t connectedPeers; size_t bandwidthLimitedPeers; size_t duplicatePeers; /**< optional number of allowed peers from duplicate IPs, defaults to ENET_PROTOCOL_MAXIMUM_PEER_ID */ size_t maximumPacketSize; /**< the maximum allowable packet size that may be sent or received on a peer */ size_t maximumWaitingData; /**< the maximum aggregate amount of buffer space a peer may use waiting for packets to be delivered */ } ENetHost; /** * An ENet event type, as specified in @ref ENetEvent. */ typedef enum _ENetEventType { /** no event occurred within the specified time limit */ ENET_EVENT_TYPE_NONE = 0, /** a connection request initiated by enet_host_connect has completed. * The peer field contains the peer which successfully connected. */ ENET_EVENT_TYPE_CONNECT = 1, /** a peer has disconnected. This event is generated on a successful * completion of a disconnect initiated by enet_peer_disconnect, if * a peer has timed out. The peer field contains the peer * which disconnected. The data field contains user supplied data * describing the disconnection, or 0, if none is available. */ ENET_EVENT_TYPE_DISCONNECT = 2, /** a packet has been received from a peer. The peer field specifies the * peer which sent the packet. The channelID field specifies the channel * number upon which the packet was received. The packet field contains * the packet that was received; this packet must be destroyed with * enet_packet_destroy after use. */ ENET_EVENT_TYPE_RECEIVE = 3, /** a peer is disconnected because the host didn't receive the acknowledgment * packet within certain maximum time out. The reason could be because of bad * network connection or host crashed. */ ENET_EVENT_TYPE_DISCONNECT_TIMEOUT = 4, } ENetEventType; /** * An ENet event as returned by enet_host_service(). * * @sa enet_host_service */ typedef struct _ENetEvent { ENetEventType type; /**< type of the event */ ENetPeer * peer; /**< peer that generated a connect, disconnect or receive event */ enet_uint8 channelID; /**< channel on the peer that generated the event, if appropriate */ enet_uint32 data; /**< data associated with the event, if appropriate */ ENetPacket * packet; /**< packet associated with the event, if appropriate */ } ENetEvent; // =======================================================================// // ! // ! Public API // ! // =======================================================================// /** * Initializes ENet globally. Must be called prior to using any functions in ENet. * @returns 0 on success, < 0 on failure */ ENET_API int enet_initialize(void); /** * Initializes ENet globally and supplies user-overridden callbacks. Must be called prior to using any functions in ENet. Do not use enet_initialize() if you use this variant. Make sure the ENetCallbacks structure is zeroed out so that any additional callbacks added in future versions will be properly ignored. * * @param version the constant ENET_VERSION should be supplied so ENet knows which version of ENetCallbacks struct to use * @param inits user-overridden callbacks where any NULL callbacks will use ENet's defaults * @returns 0 on success, < 0 on failure */ ENET_API int enet_initialize_with_callbacks(ENetVersion version, const ENetCallbacks * inits); /** * Shuts down ENet globally. Should be called when a program that has initialized ENet exits. */ ENET_API void enet_deinitialize(void); /** * Gives the linked version of the ENet library. * @returns the version number */ ENET_API ENetVersion enet_linked_version(void); /** Returns the monotonic time in milliseconds. Its initial value is unspecified unless otherwise set. */ ENET_API enet_uint32 enet_time_get(void); /** ENet socket functions */ ENET_API ENetSocket enet_socket_create(ENetSocketType); ENET_API int enet_socket_bind(ENetSocket, const ENetAddress *); ENET_API int enet_socket_get_address(ENetSocket, ENetAddress *); ENET_API int enet_socket_listen(ENetSocket, int); ENET_API ENetSocket enet_socket_accept(ENetSocket, ENetAddress *); ENET_API int enet_socket_connect(ENetSocket, const ENetAddress *); ENET_API int enet_socket_send(ENetSocket, const ENetAddress *, const ENetBuffer *, size_t); ENET_API int enet_socket_receive(ENetSocket, ENetAddress *, ENetBuffer *, size_t); ENET_API int enet_socket_wait(ENetSocket, enet_uint32 *, enet_uint64); ENET_API int enet_socket_set_option(ENetSocket, ENetSocketOption, int); ENET_API int enet_socket_get_option(ENetSocket, ENetSocketOption, int *); ENET_API int enet_socket_shutdown(ENetSocket, ENetSocketShutdown); ENET_API void enet_socket_destroy(ENetSocket); ENET_API int enet_socketset_select(ENetSocket, ENetSocketSet *, ENetSocketSet *, enet_uint32); /** Attempts to parse the printable form of the IP address in the parameter hostName and sets the host field in the address parameter if successful. @param address destination to store the parsed IP address @param hostName IP address to parse @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_ip_old(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. @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_old(ENetAddress * address, const char * hostName); /** Gives the printable form of the IP address specified in the address parameter. @param address address printed @param hostName destination for name, must not be NULL @param nameLength maximum length of hostName. @returns the null-terminated name of the host in hostName on success @retval 0 on success @retval < 0 on failure */ ENET_API int enet_address_get_host_ip_old(const ENetAddress * address, char * hostName, size_t nameLength); /** Attempts to do a reverse lookup of the host field in the address parameter. @param address address used for reverse lookup @param hostName destination for name, must not be NULL @param nameLength maximum length of hostName. @returns the null-terminated name of the host in hostName on success @retval 0 on success @retval < 0 on failure */ ENET_API int enet_address_get_host_old(const ENetAddress * address, char * hostName, size_t nameLength); ENET_API int enet_address_set_host_ip_new(ENetAddress * address, const char * hostName); ENET_API int enet_address_set_host_new(ENetAddress * address, const char * hostName); ENET_API int enet_address_get_host_ip_new(const ENetAddress * address, char * hostName, size_t nameLength); ENET_API int enet_address_get_host_new(const ENetAddress * address, char * hostName, size_t nameLength); #ifdef ENET_FEATURE_ADDRESS_MAPPING #define enet_address_set_host_ip enet_address_set_host_ip_new #define enet_address_set_host enet_address_set_host_new #define enet_address_get_host_ip enet_address_get_host_ip_new #define enet_address_get_host enet_address_get_host_new #else #define enet_address_set_host_ip enet_address_set_host_ip_old #define enet_address_set_host enet_address_set_host_old #define enet_address_get_host_ip enet_address_get_host_ip_old #define enet_address_get_host enet_address_get_host_old #endif ENET_API enet_uint32 enet_host_get_peers_count(ENetHost *); ENET_API enet_uint32 enet_host_get_packets_sent(ENetHost *); ENET_API enet_uint32 enet_host_get_packets_received(ENetHost *); ENET_API enet_uint32 enet_host_get_bytes_sent(ENetHost *); ENET_API enet_uint32 enet_host_get_bytes_received(ENetHost *); ENET_API enet_uint32 enet_host_get_received_data(ENetHost *, enet_uint8** data); ENET_API enet_uint32 enet_host_get_mtu(ENetHost *); ENET_API enet_uint32 enet_peer_get_id(ENetPeer *); ENET_API enet_uint32 enet_peer_get_ip(ENetPeer *, char * ip, size_t ipLength); ENET_API enet_uint16 enet_peer_get_port(ENetPeer *); ENET_API enet_uint32 enet_peer_get_rtt(ENetPeer *); ENET_API enet_uint64 enet_peer_get_packets_sent(ENetPeer *); ENET_API enet_uint32 enet_peer_get_packets_lost(ENetPeer *); ENET_API enet_uint64 enet_peer_get_bytes_sent(ENetPeer *); ENET_API enet_uint64 enet_peer_get_bytes_received(ENetPeer *); ENET_API ENetPeerState enet_peer_get_state(ENetPeer *); ENET_API void * enet_peer_get_data(ENetPeer *); ENET_API void enet_peer_set_data(ENetPeer *, const void *); ENET_API void * enet_packet_get_data(ENetPacket *); ENET_API enet_uint32 enet_packet_get_length(ENetPacket *); ENET_API void enet_packet_set_free_callback(ENetPacket *, void *); ENET_API ENetPacket * enet_packet_create_offset(const void *, size_t, size_t, enet_uint32); ENET_API enet_uint32 enet_crc32(const ENetBuffer *, size_t); ENET_API ENetHost * enet_host_create(const ENetAddress *, size_t, size_t, enet_uint32, enet_uint32); ENET_API void enet_host_destroy(ENetHost *); ENET_API ENetPeer * enet_host_connect(ENetHost *, const ENetAddress *, size_t, enet_uint32); ENET_API int enet_host_check_events(ENetHost *, ENetEvent *); ENET_API int enet_host_service(ENetHost *, ENetEvent *, enet_uint32); ENET_API int enet_host_send_raw(ENetHost *, const ENetAddress *, enet_uint8 *, size_t); ENET_API int enet_host_send_raw_ex(ENetHost *host, const ENetAddress* address, enet_uint8* data, size_t skipBytes, size_t bytesToSend); ENET_API void enet_host_set_intercept(ENetHost *, const ENetInterceptCallback); ENET_API void enet_host_flush(ENetHost *); ENET_API void enet_host_broadcast(ENetHost *, enet_uint8, ENetPacket *); ENET_API void enet_host_compress(ENetHost *, const ENetCompressor *); ENET_API void enet_host_channel_limit(ENetHost *, size_t); ENET_API void enet_host_bandwidth_limit(ENetHost *, enet_uint32, enet_uint32); extern void enet_host_bandwidth_throttle(ENetHost *); extern enet_uint64 enet_host_random_seed(void); ENET_API int enet_peer_send(ENetPeer *, enet_uint8, ENetPacket *); ENET_API ENetPacket * enet_peer_receive(ENetPeer *, enet_uint8 * channelID); ENET_API void enet_peer_ping(ENetPeer *); ENET_API void enet_peer_ping_interval(ENetPeer *, enet_uint32); ENET_API void enet_peer_timeout(ENetPeer *, enet_uint32, enet_uint32, enet_uint32); ENET_API void enet_peer_reset(ENetPeer *); ENET_API void enet_peer_disconnect(ENetPeer *, enet_uint32); ENET_API void enet_peer_disconnect_now(ENetPeer *, enet_uint32); ENET_API void enet_peer_disconnect_later(ENetPeer *, enet_uint32); ENET_API void enet_peer_throttle_configure(ENetPeer *, enet_uint32, enet_uint32, enet_uint32); extern int enet_peer_throttle(ENetPeer *, enet_uint32); extern void enet_peer_reset_queues(ENetPeer *); extern void enet_peer_setup_outgoing_command(ENetPeer *, ENetOutgoingCommand *); extern ENetOutgoingCommand * enet_peer_queue_outgoing_command(ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16); extern ENetIncomingCommand * enet_peer_queue_incoming_command(ENetPeer *, const ENetProtocol *, const void *, size_t, enet_uint32, enet_uint32); extern ENetAcknowledgement * enet_peer_queue_acknowledgement(ENetPeer *, const ENetProtocol *, enet_uint16); extern void enet_peer_dispatch_incoming_unreliable_commands(ENetPeer *, ENetChannel *); extern void enet_peer_dispatch_incoming_reliable_commands(ENetPeer *, ENetChannel *); extern void enet_peer_on_connect(ENetPeer *); extern void enet_peer_on_disconnect(ENetPeer *); extern size_t enet_protocol_command_size (enet_uint8); #ifdef __cplusplus } #endif #if defined(ENET_IMPLEMENTATION) && !defined(ENET_IMPLEMENTATION_DONE) #define ENET_IMPLEMENTATION_DONE 1 #ifdef __cplusplus extern "C" { #endif // =======================================================================// // ! // ! Atomics // ! // =======================================================================// #if defined(_MSC_VER) #define ENET_AT_CASSERT_PRED(predicate) sizeof(char[2 * !!(predicate)-1]) #define ENET_IS_SUPPORTED_ATOMIC(size) ENET_AT_CASSERT_PRED(size == 1 || size == 2 || size == 4 || size == 8) #define ENET_ATOMIC_SIZEOF(variable) (ENET_IS_SUPPORTED_ATOMIC(sizeof(*(variable))), sizeof(*(variable))) __inline int64_t enet_at_atomic_read(char *ptr, size_t size) { switch (size) { case 1: return _InterlockedExchangeAdd8((volatile char *)ptr, 0); case 2: return _InterlockedExchangeAdd16((volatile SHORT *)ptr, 0); case 4: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedExchangeAdd((volatile LONG *)ptr, 0); #else return _InterlockedExchangeAdd((volatile LONG *)ptr, 0); #endif case 8: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedExchangeAdd64((volatile LONGLONG *)ptr, 0); #else return _InterlockedExchangeAdd64((volatile LONGLONG *)ptr, 0); #endif default: return 0xbad13bad; /* never reached */ } } __inline int64_t enet_at_atomic_write(char *ptr, int64_t value, size_t size) { switch (size) { case 1: return _InterlockedExchange8((volatile char *)ptr, (char)value); case 2: return _InterlockedExchange16((volatile SHORT *)ptr, (SHORT)value); case 4: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedExchange((volatile LONG *)ptr, (LONG)value); #else return _InterlockedExchange((volatile LONG *)ptr, (LONG)value); #endif case 8: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedExchange64((volatile LONGLONG *)ptr, (LONGLONG)value); #else return _InterlockedExchange64((volatile LONGLONG *)ptr, (LONGLONG)value); #endif default: return 0xbad13bad; /* never reached */ } } __inline int64_t enet_at_atomic_cas(char *ptr, int64_t new_val, int64_t old_val, size_t size) { switch (size) { case 1: return _InterlockedCompareExchange8((volatile char *)ptr, (char)new_val, (char)old_val); case 2: return _InterlockedCompareExchange16((volatile SHORT *)ptr, (SHORT)new_val, (SHORT)old_val); case 4: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedCompareExchange((volatile LONG *)ptr, (LONG)new_val, (LONG)old_val); #else return _InterlockedCompareExchange((volatile LONG *)ptr, (LONG)new_val, (LONG)old_val); #endif case 8: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedCompareExchange64((volatile LONGLONG *)ptr, (LONGLONG)new_val, (LONGLONG)old_val); #else return _InterlockedCompareExchange64((volatile LONGLONG *)ptr, (LONGLONG)new_val, (LONGLONG)old_val); #endif default: return 0xbad13bad; /* never reached */ } } __inline int64_t enet_at_atomic_inc(char *ptr, int64_t delta, size_t data_size) { switch (data_size) { case 1: return _InterlockedExchangeAdd8((volatile char *)ptr, (char)delta); case 2: return _InterlockedExchangeAdd16((volatile SHORT *)ptr, (SHORT)delta); case 4: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedExchangeAdd((volatile LONG *)ptr, (LONG)delta); #else return _InterlockedExchangeAdd((volatile LONG *)ptr, (LONG)delta); #endif case 8: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedExchangeAdd64((volatile LONGLONG *)ptr, (LONGLONG)delta); #else return _InterlockedExchangeAdd64((volatile LONGLONG *)ptr, (LONGLONG)delta); #endif default: return 0xbad13bad; /* never reached */ } } #define ENET_ATOMIC_READ(variable) enet_at_atomic_read((char *)(variable), ENET_ATOMIC_SIZEOF(variable)) #define ENET_ATOMIC_WRITE(variable, new_val) \ enet_at_atomic_write((char *)(variable), (int64_t)(new_val), ENET_ATOMIC_SIZEOF(variable)) #define ENET_ATOMIC_CAS(variable, old_value, new_val) \ enet_at_atomic_cas((char *)(variable), (int64_t)(new_val), (int64_t)(old_value), \ ENET_ATOMIC_SIZEOF(variable)) #define ENET_ATOMIC_INC(variable) enet_at_atomic_inc((char *)(variable), 1, ENET_ATOMIC_SIZEOF(variable)) #define ENET_ATOMIC_DEC(variable) enet_at_atomic_inc((char *)(variable), -1, ENET_ATOMIC_SIZEOF(variable)) #define ENET_ATOMIC_INC_BY(variable, delta) \ enet_at_atomic_inc((char *)(variable), (delta), ENET_ATOMIC_SIZEOF(variable)) #define ENET_ATOMIC_DEC_BY(variable, delta) \ enet_at_atomic_inc((char *)(variable), -(delta), ENET_ATOMIC_SIZEOF(variable)) #elif defined(__GNUC__) || defined(__clang__) #if defined(__clang__) || (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) #define AT_HAVE_ATOMICS #endif /* We want to use __atomic built-ins if possible because the __sync primitives are deprecated, because the __atomic build-ins allow us to use ENET_ATOMIC_WRITE on uninitialized memory without running into undefined behavior, and because the __atomic versions generate more efficient code since we don't need to rely on CAS when we don't actually want it. Note that we use acquire-release memory order (like mutexes do). We could use sequentially consistent memory order but that has lower performance and is almost always unneeded. */ #ifdef AT_HAVE_ATOMICS #define ENET_ATOMIC_READ(ptr) __atomic_load_n((ptr), __ATOMIC_ACQUIRE) #define ENET_ATOMIC_WRITE(ptr, value) __atomic_store_n((ptr), (value), __ATOMIC_RELEASE) #ifndef typeof #define typeof __typeof__ #endif /* clang_analyzer doesn't know that CAS writes to memory so it complains about potentially lost data. Replace the code with the equivalent non-sync code. */ #ifdef __clang_analyzer__ #define ENET_ATOMIC_CAS(ptr, old_value, new_value) \ ({ \ typeof(*(ptr)) ENET_ATOMIC_CAS_old_actual_ = (*(ptr)); \ if (ATOMIC_CAS_old_actual_ == (old_value)) { \ *(ptr) = new_value; \ } \ ENET_ATOMIC_CAS_old_actual_; \ }) #else /* Could use __auto_type instead of typeof but that shouldn't work in C++. The ({ }) syntax is a GCC extension called statement expression. It lets us return a value out of the macro. TODO We should return bool here instead of the old value to avoid the ABA problem. */ #define ENET_ATOMIC_CAS(ptr, old_value, new_value) \ ({ \ typeof(*(ptr)) ENET_ATOMIC_CAS_expected_ = (old_value); \ __atomic_compare_exchange_n((ptr), &ENET_ATOMIC_CAS_expected_, (new_value), false, \ __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE); \ ENET_ATOMIC_CAS_expected_; \ }) #endif /* __clang_analyzer__ */ #define ENET_ATOMIC_INC(ptr) __atomic_fetch_add((ptr), 1, __ATOMIC_ACQ_REL) #define ENET_ATOMIC_DEC(ptr) __atomic_fetch_sub((ptr), 1, __ATOMIC_ACQ_REL) #define ENET_ATOMIC_INC_BY(ptr, delta) __atomic_fetch_add((ptr), (delta), __ATOMIC_ACQ_REL) #define ENET_ATOMIC_DEC_BY(ptr, delta) __atomic_fetch_sub((ptr), (delta), __ATOMIC_ACQ_REL) #else #define ENET_ATOMIC_READ(variable) __sync_fetch_and_add(variable, 0) #define ENET_ATOMIC_WRITE(variable, new_val) \ (void) __sync_val_compare_and_swap((variable), *(variable), (new_val)) #define ENET_ATOMIC_CAS(variable, old_value, new_val) \ __sync_val_compare_and_swap((variable), (old_value), (new_val)) #define ENET_ATOMIC_INC(variable) __sync_fetch_and_add((variable), 1) #define ENET_ATOMIC_DEC(variable) __sync_fetch_and_sub((variable), 1) #define ENET_ATOMIC_INC_BY(variable, delta) __sync_fetch_and_add((variable), (delta), 1) #define ENET_ATOMIC_DEC_BY(variable, delta) __sync_fetch_and_sub((variable), (delta), 1) #endif /* AT_HAVE_ATOMICS */ #undef AT_HAVE_ATOMICS #endif /* defined(_MSC_VER) */ // =======================================================================// // ! // ! Callbacks // ! // =======================================================================// ENetCallbacks callbacks = { malloc, free, abort, enet_packet_create, enet_packet_destroy }; int enet_initialize_with_callbacks(ENetVersion version, const ENetCallbacks *inits) { if (version < ENET_VERSION_CREATE(1, 3, 0)) { return -1; } if (inits->malloc != NULL || inits->free != NULL) { if (inits->malloc == NULL || inits->free == NULL) { return -1; } callbacks.malloc = inits->malloc; callbacks.free = inits->free; } if (inits->no_memory != NULL) { callbacks.no_memory = inits->no_memory; } if (inits->packet_create != NULL || inits->packet_destroy != NULL) { if (inits->packet_create == NULL || inits->packet_destroy == NULL) { return -1; } callbacks.packet_create = inits->packet_create; callbacks.packet_destroy = inits->packet_destroy; } return enet_initialize(); } ENetVersion enet_linked_version(void) { return ENET_VERSION; } void * enet_malloc(size_t size) { void *memory = callbacks.malloc(size); if (memory == NULL) { callbacks.no_memory(); } return memory; } void enet_free(void *memory) { callbacks.free(memory); } // =======================================================================// // ! // ! List // ! // =======================================================================// void enet_list_clear(ENetList *list) { list->sentinel.next = &list->sentinel; list->sentinel.previous = &list->sentinel; } ENetListIterator enet_list_insert(ENetListIterator position, void *data) { ENetListIterator result = (ENetListIterator)data; result->previous = position->previous; result->next = position; result->previous->next = result; position->previous = result; return result; } void *enet_list_remove(ENetListIterator position) { position->previous->next = position->next; position->next->previous = position->previous; return position; } ENetListIterator enet_list_move(ENetListIterator position, void *dataFirst, void *dataLast) { ENetListIterator first = (ENetListIterator)dataFirst; ENetListIterator last = (ENetListIterator)dataLast; first->previous->next = last->next; last->next->previous = first->previous; first->previous = position->previous; last->next = position; first->previous->next = first; position->previous = last; return first; } size_t enet_list_size(ENetList *list) { size_t size = 0; ENetListIterator position; for (position = enet_list_begin(list); position != enet_list_end(list); position = enet_list_next(position)) { ++size; } return size; } // =======================================================================// // ! // ! Packet // ! // =======================================================================// /** * Creates a packet that may be sent to a peer. * @param data initial contents of the packet's data; the packet's data will remain uninitialized if data 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; if (flags & ENET_PACKET_FLAG_NO_ALLOCATE) { packet = (ENetPacket *)enet_malloc(sizeof (ENetPacket)); if (packet == NULL) { return NULL; } packet->data = (enet_uint8 *)data; } else { packet = (ENetPacket *)enet_malloc(sizeof (ENetPacket) + dataLength); if (packet == NULL) { return NULL; } packet->data = (enet_uint8 *)packet + sizeof(ENetPacket); if (data != NULL) { memcpy(packet->data, data, dataLength); } } packet->referenceCount = 0; packet->flags = flags; packet->dataLength = dataLength; packet->freeCallback = NULL; packet->userData = NULL; return packet; } /** 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) { ENetPacket *newPacket = NULL; if (dataLength <= packet->dataLength || (packet->flags & ENET_PACKET_FLAG_NO_ALLOCATE)) { packet->dataLength = dataLength; return 0; } newPacket = (ENetPacket *)enet_malloc(sizeof (ENetPacket) + dataLength); if (newPacket == NULL) return -1; memcpy(newPacket, packet, sizeof(ENetPacket) + packet->dataLength); newPacket->data = (enet_uint8 *)newPacket + sizeof(ENetPacket); enet_free(packet); return 0; } ENetPacket *enet_packet_create_offset(const void *data, size_t dataLength, size_t dataOffset, enet_uint32 flags) { ENetPacket *packet; if (flags & ENET_PACKET_FLAG_NO_ALLOCATE) { packet = (ENetPacket *)enet_malloc(sizeof (ENetPacket)); if (packet == NULL) { return NULL; } packet->data = (enet_uint8 *)data; } else { packet = (ENetPacket *)enet_malloc(sizeof (ENetPacket) + dataLength + dataOffset); if (packet == NULL) { return NULL; } packet->data = (enet_uint8 *)packet + sizeof(ENetPacket); if (data != NULL) { memcpy(packet->data + dataOffset, data, dataLength); } } packet->referenceCount = 0; packet->flags = flags; packet->dataLength = dataLength + dataOffset; packet->freeCallback = NULL; packet->userData = NULL; return packet; } ENetPacket *enet_packet_copy(ENetPacket *packet) { return enet_packet_create(packet->data, packet->dataLength, packet->flags); } /** * Destroys the packet and deallocates its data. * @param packet packet to be destroyed */ void enet_packet_destroy(ENetPacket *packet) { if (packet == NULL) { return; } if (packet->freeCallback != NULL) { (*packet->freeCallback)((void *)packet); } enet_free(packet); } static int initializedCRC32 = 0; static enet_uint32 crcTable[256]; static enet_uint32 reflect_crc(int val, int bits) { int result = 0, bit; for (bit = 0; bit < bits; bit++) { if (val & 1) { result |= 1 << (bits - 1 - bit); } val >>= 1; } return result; } static void initialize_crc32(void) { int byte; for (byte = 0; byte < 256; ++byte) { enet_uint32 crc = reflect_crc(byte, 8) << 24; int offset; for (offset = 0; offset < 8; ++offset) { if (crc & 0x80000000) { crc = (crc << 1) ^ 0x04c11db7; } else { crc <<= 1; } } crcTable[byte] = reflect_crc(crc, 32); } initializedCRC32 = 1; } enet_uint32 enet_crc32(const ENetBuffer *buffers, size_t bufferCount) { enet_uint32 crc = 0xFFFFFFFF; if (!initializedCRC32) { initialize_crc32(); } while (bufferCount-- > 0) { const enet_uint8 *data = (const enet_uint8 *)buffers->data; const enet_uint8 *dataEnd = &data[buffers->dataLength]; while (data < dataEnd) { crc = (crc >> 8) ^ crcTable[(crc & 0xFF) ^ *data++]; } ++buffers; } return ENET_HOST_TO_NET_32(~crc); } // =======================================================================// // ! // ! Protocol // ! // =======================================================================// static size_t commandSizes[ENET_PROTOCOL_COMMAND_COUNT] = { 0, sizeof(ENetProtocolAcknowledge), sizeof(ENetProtocolConnect), sizeof(ENetProtocolVerifyConnect), sizeof(ENetProtocolDisconnect), sizeof(ENetProtocolPing), sizeof(ENetProtocolSendReliable), sizeof(ENetProtocolSendUnreliable), sizeof(ENetProtocolSendFragment), sizeof(ENetProtocolSendUnsequenced), sizeof(ENetProtocolBandwidthLimit), sizeof(ENetProtocolThrottleConfigure), sizeof(ENetProtocolSendFragment) }; size_t enet_protocol_command_size(enet_uint8 commandNumber) { return commandSizes[commandNumber & ENET_PROTOCOL_COMMAND_MASK]; } static void enet_protocol_change_state(ENetHost *host, ENetPeer *peer, ENetPeerState state) { ENET_UNUSED(host) if (state == ENET_PEER_STATE_CONNECTED || state == ENET_PEER_STATE_DISCONNECT_LATER) { enet_peer_on_connect(peer); } else { enet_peer_on_disconnect(peer); } peer->state = state; } static void enet_protocol_dispatch_state(ENetHost *host, ENetPeer *peer, ENetPeerState state) { enet_protocol_change_state(host, peer, state); if (!peer->needsDispatch) { enet_list_insert(enet_list_end(&host->dispatchQueue), &peer->dispatchList); peer->needsDispatch = 1; } } static int enet_protocol_dispatch_incoming_commands(ENetHost *host, ENetEvent *event) { while (!enet_list_empty(&host->dispatchQueue)) { ENetPeer *peer = (ENetPeer *) enet_list_remove(enet_list_begin(&host->dispatchQueue)); peer->needsDispatch = 0; switch (peer->state) { case ENET_PEER_STATE_CONNECTION_PENDING: case ENET_PEER_STATE_CONNECTION_SUCCEEDED: enet_protocol_change_state(host, peer, ENET_PEER_STATE_CONNECTED); event->type = ENET_EVENT_TYPE_CONNECT; event->peer = peer; event->data = peer->eventData; return 1; case ENET_PEER_STATE_ZOMBIE: host->recalculateBandwidthLimits = 1; event->type = ENET_EVENT_TYPE_DISCONNECT; event->peer = peer; event->data = peer->eventData; enet_peer_reset(peer); return 1; case ENET_PEER_STATE_CONNECTED: if (enet_list_empty(&peer->dispatchedCommands)) { continue; } event->packet = enet_peer_receive(peer, &event->channelID); if (event->packet == NULL) { continue; } event->type = ENET_EVENT_TYPE_RECEIVE; event->peer = peer; if (!enet_list_empty(&peer->dispatchedCommands)) { peer->needsDispatch = 1; enet_list_insert(enet_list_end(&host->dispatchQueue), &peer->dispatchList); } return 1; default: break; } } return 0; } /* enet_protocol_dispatch_incoming_commands */ static void enet_protocol_notify_connect(ENetHost *host, ENetPeer *peer, ENetEvent *event) { host->recalculateBandwidthLimits = 1; if (event != NULL) { enet_protocol_change_state(host, peer, ENET_PEER_STATE_CONNECTED); peer->totalDataSent = 0; peer->totalDataReceived = 0; peer->totalPacketsSent = 0; peer->totalPacketsLost = 0; event->type = ENET_EVENT_TYPE_CONNECT; event->peer = peer; event->data = peer->eventData; } else { enet_protocol_dispatch_state(host, peer, peer->state == ENET_PEER_STATE_CONNECTING ? ENET_PEER_STATE_CONNECTION_SUCCEEDED : ENET_PEER_STATE_CONNECTION_PENDING); } } static void enet_protocol_notify_disconnect(ENetHost *host, ENetPeer *peer, ENetEvent *event) { if (peer->state >= ENET_PEER_STATE_CONNECTION_PENDING) { host->recalculateBandwidthLimits = 1; } if (peer->state != ENET_PEER_STATE_CONNECTING && peer->state < ENET_PEER_STATE_CONNECTION_SUCCEEDED) { enet_peer_reset(peer); } else if (event != NULL) { event->type = ENET_EVENT_TYPE_DISCONNECT; event->peer = peer; event->data = 0; enet_peer_reset(peer); } else { peer->eventData = 0; enet_protocol_dispatch_state(host, peer, ENET_PEER_STATE_ZOMBIE); } } static void enet_protocol_notify_disconnect_timeout (ENetHost * host, ENetPeer * peer, ENetEvent * event) { if (peer->state >= ENET_PEER_STATE_CONNECTION_PENDING) { host->recalculateBandwidthLimits = 1; } if (peer->state != ENET_PEER_STATE_CONNECTING && peer->state < ENET_PEER_STATE_CONNECTION_SUCCEEDED) { enet_peer_reset (peer); } else if (event != NULL) { event->type = ENET_EVENT_TYPE_DISCONNECT_TIMEOUT; event->peer = peer; event->data = 0; enet_peer_reset(peer); } else { peer->eventData = 0; enet_protocol_dispatch_state(host, peer, ENET_PEER_STATE_ZOMBIE); } } static void enet_protocol_remove_sent_unreliable_commands(ENetPeer *peer) { ENetOutgoingCommand *outgoingCommand; while (!enet_list_empty(&peer->sentUnreliableCommands)) { outgoingCommand = (ENetOutgoingCommand *) enet_list_front(&peer->sentUnreliableCommands); enet_list_remove(&outgoingCommand->outgoingCommandList); if (outgoingCommand->packet != NULL) { --outgoingCommand->packet->referenceCount; if (outgoingCommand->packet->referenceCount == 0) { outgoingCommand->packet->flags |= ENET_PACKET_FLAG_SENT; callbacks.packet_destroy(outgoingCommand->packet); } } enet_free(outgoingCommand); } } static ENetProtocolCommand enet_protocol_remove_sent_reliable_command(ENetPeer *peer, enet_uint16 reliableSequenceNumber, enet_uint8 channelID) { ENetOutgoingCommand *outgoingCommand = NULL; ENetListIterator currentCommand; ENetProtocolCommand commandNumber; int wasSent = 1; for (currentCommand = enet_list_begin(&peer->sentReliableCommands); currentCommand != enet_list_end(&peer->sentReliableCommands); currentCommand = enet_list_next(currentCommand) ) { outgoingCommand = (ENetOutgoingCommand *) currentCommand; if (outgoingCommand->reliableSequenceNumber == reliableSequenceNumber && outgoingCommand->command.header.channelID == channelID) { break; } } if (currentCommand == enet_list_end(&peer->sentReliableCommands)) { for (currentCommand = enet_list_begin(&peer->outgoingReliableCommands); currentCommand != enet_list_end(&peer->outgoingReliableCommands); currentCommand = enet_list_next(currentCommand) ) { outgoingCommand = (ENetOutgoingCommand *) currentCommand; if (outgoingCommand->sendAttempts < 1) { return ENET_PROTOCOL_COMMAND_NONE; } if (outgoingCommand->reliableSequenceNumber == reliableSequenceNumber && outgoingCommand->command.header.channelID == channelID) { break; } } if (currentCommand == enet_list_end(&peer->outgoingReliableCommands)) { return ENET_PROTOCOL_COMMAND_NONE; } wasSent = 0; } if (outgoingCommand == NULL) { return ENET_PROTOCOL_COMMAND_NONE; } if (channelID < peer->channelCount) { ENetChannel *channel = &peer->channels[channelID]; enet_uint16 reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (channel->reliableWindows[reliableWindow] > 0) { --channel->reliableWindows[reliableWindow]; if (!channel->reliableWindows[reliableWindow]) { channel->usedReliableWindows &= ~(1 << reliableWindow); } } } commandNumber = (ENetProtocolCommand) (outgoingCommand->command.header.command & ENET_PROTOCOL_COMMAND_MASK); enet_list_remove(&outgoingCommand->outgoingCommandList); if (outgoingCommand->packet != NULL) { if (wasSent) { peer->reliableDataInTransit -= outgoingCommand->fragmentLength; } --outgoingCommand->packet->referenceCount; if (outgoingCommand->packet->referenceCount == 0) { outgoingCommand->packet->flags |= ENET_PACKET_FLAG_SENT; callbacks.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; } /* enet_protocol_remove_sent_reliable_command */ static ENetPeer * enet_protocol_handle_connect(ENetHost *host, ENetProtocolHeader *header, ENetProtocol *command) { ENET_UNUSED(header) enet_uint8 incomingSessionID, outgoingSessionID; enet_uint32 mtu, windowSize; ENetChannel *channel; size_t channelCount, duplicatePeers = 0; ENetPeer *currentPeer, *peer = NULL; ENetProtocol verifyCommand; channelCount = ENET_NET_TO_HOST_32(command->connect.channelCount); if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) { return NULL; } for (currentPeer = host->peers; currentPeer < &host->peers[host->peerCount]; ++currentPeer) { if (currentPeer->state == ENET_PEER_STATE_DISCONNECTED) { if (peer == NULL) { peer = currentPeer; } } else if (currentPeer->state != ENET_PEER_STATE_CONNECTING && in6_equal(currentPeer->address.host, host->receivedAddress.host)) { if (currentPeer->address.port == host->receivedAddress.port && currentPeer->connectID == command->connect.connectID) { return NULL; } ++duplicatePeers; } } if (peer == NULL || duplicatePeers >= host->duplicatePeers) { return NULL; } if (channelCount > host->channelLimit) { channelCount = host->channelLimit; } peer->channels = (ENetChannel *) enet_malloc(channelCount * sizeof(ENetChannel)); if (peer->channels == NULL) { return NULL; } peer->channelCount = channelCount; peer->state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT; peer->connectID = command->connect.connectID; peer->address = host->receivedAddress; peer->outgoingPeerID = ENET_NET_TO_HOST_16(command->connect.outgoingPeerID); peer->incomingBandwidth = ENET_NET_TO_HOST_32(command->connect.incomingBandwidth); peer->outgoingBandwidth = ENET_NET_TO_HOST_32(command->connect.outgoingBandwidth); peer->packetThrottleInterval = ENET_NET_TO_HOST_32(command->connect.packetThrottleInterval); peer->packetThrottleAcceleration = ENET_NET_TO_HOST_32(command->connect.packetThrottleAcceleration); peer->packetThrottleDeceleration = ENET_NET_TO_HOST_32(command->connect.packetThrottleDeceleration); peer->eventData = ENET_NET_TO_HOST_32(command->connect.data); incomingSessionID = command->connect.incomingSessionID == 0xFF ? peer->outgoingSessionID : command->connect.incomingSessionID; incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); if (incomingSessionID == peer->outgoingSessionID) { incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); } peer->outgoingSessionID = incomingSessionID; outgoingSessionID = command->connect.outgoingSessionID == 0xFF ? peer->incomingSessionID : command->connect.outgoingSessionID; outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); if (outgoingSessionID == peer->incomingSessionID) { outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); } peer->incomingSessionID = outgoingSessionID; for (channel = peer->channels; channel < &peer->channels[channelCount]; ++channel) { channel->outgoingReliableSequenceNumber = 0; channel->outgoingUnreliableSequenceNumber = 0; channel->incomingReliableSequenceNumber = 0; channel->incomingUnreliableSequenceNumber = 0; enet_list_clear(&channel->incomingReliableCommands); enet_list_clear(&channel->incomingUnreliableCommands); channel->usedReliableWindows = 0; memset(channel->reliableWindows, 0, sizeof(channel->reliableWindows)); } mtu = ENET_NET_TO_HOST_32(command->connect.mtu); if (mtu < ENET_PROTOCOL_MINIMUM_MTU) { mtu = ENET_PROTOCOL_MINIMUM_MTU; } else if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) { mtu = ENET_PROTOCOL_MAXIMUM_MTU; } peer->mtu = mtu; if (host->outgoingBandwidth == 0 && peer->incomingBandwidth == 0) { peer->windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } else if (host->outgoingBandwidth == 0 || peer->incomingBandwidth == 0) { peer->windowSize = (ENET_MAX(host->outgoingBandwidth, peer->incomingBandwidth) / ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } else { peer->windowSize = (ENET_MIN(host->outgoingBandwidth, peer->incomingBandwidth) / 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; } if (host->incomingBandwidth == 0) { windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } else { windowSize = (host->incomingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } if (windowSize > ENET_NET_TO_HOST_32(command->connect.windowSize)) { windowSize = ENET_NET_TO_HOST_32(command->connect.windowSize); } if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) { windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } else if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) { windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } verifyCommand.header.command = ENET_PROTOCOL_COMMAND_VERIFY_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; verifyCommand.header.channelID = 0xFF; verifyCommand.verifyConnect.outgoingPeerID = ENET_HOST_TO_NET_16(peer->incomingPeerID); verifyCommand.verifyConnect.incomingSessionID = incomingSessionID; verifyCommand.verifyConnect.outgoingSessionID = outgoingSessionID; verifyCommand.verifyConnect.mtu = ENET_HOST_TO_NET_32(peer->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(peer->packetThrottleInterval); verifyCommand.verifyConnect.packetThrottleAcceleration = ENET_HOST_TO_NET_32(peer->packetThrottleAcceleration); verifyCommand.verifyConnect.packetThrottleDeceleration = ENET_HOST_TO_NET_32(peer->packetThrottleDeceleration); verifyCommand.verifyConnect.connectID = peer->connectID; enet_peer_queue_outgoing_command(peer, &verifyCommand, NULL, 0, 0); return peer; } /* enet_protocol_handle_connect */ static int enet_protocol_handle_send_reliable(ENetHost *host, ENetPeer *peer, const ENetProtocol *command, enet_uint8 **currentData) { size_t dataLength; if (command->header.channelID >= peer->channelCount || (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER)) { return -1; } dataLength = ENET_NET_TO_HOST_16(command->sendReliable.dataLength); *currentData += dataLength; if (dataLength > host->maximumPacketSize || *currentData < host->receivedData || *currentData > &host->receivedData[host->receivedDataLength]) { return -1; } if (enet_peer_queue_incoming_command(peer, command, (const enet_uint8 *) command + sizeof(ENetProtocolSendReliable), dataLength, ENET_PACKET_FLAG_RELIABLE, 0) == NULL) { return -1; } return 0; } static int enet_protocol_handle_send_unsequenced(ENetHost *host, ENetPeer *peer, const ENetProtocol *command, enet_uint8 **currentData) { enet_uint32 unsequencedGroup, index; size_t dataLength; if (command->header.channelID >= peer->channelCount || (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER)) { return -1; } dataLength = ENET_NET_TO_HOST_16(command->sendUnsequenced.dataLength); *currentData += dataLength; if (dataLength > host->maximumPacketSize || *currentData < host->receivedData || *currentData > &host->receivedData[host->receivedDataLength]) { return -1; } unsequencedGroup = ENET_NET_TO_HOST_16(command->sendUnsequenced.unsequencedGroup); index = unsequencedGroup % ENET_PEER_UNSEQUENCED_WINDOW_SIZE; if (unsequencedGroup < peer->incomingUnsequencedGroup) { unsequencedGroup += 0x10000; } if (unsequencedGroup >= (enet_uint32) peer->incomingUnsequencedGroup + ENET_PEER_FREE_UNSEQUENCED_WINDOWS * ENET_PEER_UNSEQUENCED_WINDOW_SIZE) { return 0; } unsequencedGroup &= 0xFFFF; if (unsequencedGroup - index != peer->incomingUnsequencedGroup) { peer->incomingUnsequencedGroup = unsequencedGroup - index; memset(peer->unsequencedWindow, 0, sizeof(peer->unsequencedWindow)); } else if (peer->unsequencedWindow[index / 32] & (1 << (index % 32))) { return 0; } if (enet_peer_queue_incoming_command(peer, command, (const enet_uint8 *) command + sizeof(ENetProtocolSendUnsequenced), dataLength, ENET_PACKET_FLAG_UNSEQUENCED,0) == NULL) { return -1; } peer->unsequencedWindow[index / 32] |= 1 << (index % 32); return 0; } /* enet_protocol_handle_send_unsequenced */ static int enet_protocol_handle_send_unreliable(ENetHost *host, ENetPeer *peer, const ENetProtocol *command, enet_uint8 **currentData) { size_t dataLength; if (command->header.channelID >= peer->channelCount || (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER)) { return -1; } dataLength = ENET_NET_TO_HOST_16(command->sendUnreliable.dataLength); *currentData += dataLength; if (dataLength > host->maximumPacketSize || *currentData < host->receivedData || *currentData > &host->receivedData[host->receivedDataLength]) { return -1; } if (enet_peer_queue_incoming_command(peer, command, (const enet_uint8 *) command + sizeof(ENetProtocolSendUnreliable), dataLength, 0, 0) == NULL) { return -1; } return 0; } static int enet_protocol_handle_send_fragment(ENetHost *host, ENetPeer *peer, const ENetProtocol *command, enet_uint8 **currentData) { enet_uint32 fragmentNumber, fragmentCount, fragmentOffset, fragmentLength, startSequenceNumber, totalLength; ENetChannel *channel; enet_uint16 startWindow, currentWindow; ENetListIterator currentCommand; ENetIncomingCommand *startCommand = NULL; if (command->header.channelID >= peer->channelCount || (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER)) { return -1; } fragmentLength = ENET_NET_TO_HOST_16(command->sendFragment.dataLength); *currentData += fragmentLength; if (fragmentLength > host->maximumPacketSize || *currentData < host->receivedData || *currentData > &host->receivedData[host->receivedDataLength]) { return -1; } channel = &peer->channels[command->header.channelID]; startSequenceNumber = ENET_NET_TO_HOST_16(command->sendFragment.startSequenceNumber); startWindow = startSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; currentWindow = channel->incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (startSequenceNumber < channel->incomingReliableSequenceNumber) { startWindow += ENET_PEER_RELIABLE_WINDOWS; } if (startWindow < currentWindow || startWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) { return 0; } fragmentNumber = ENET_NET_TO_HOST_32(command->sendFragment.fragmentNumber); fragmentCount = ENET_NET_TO_HOST_32(command->sendFragment.fragmentCount); fragmentOffset = ENET_NET_TO_HOST_32(command->sendFragment.fragmentOffset); totalLength = ENET_NET_TO_HOST_32(command->sendFragment.totalLength); if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT || fragmentNumber >= fragmentCount || totalLength > host->maximumPacketSize || fragmentOffset >= totalLength || fragmentLength > totalLength - fragmentOffset ) { return -1; } for (currentCommand = enet_list_previous(enet_list_end(&channel->incomingReliableCommands)); currentCommand != enet_list_end(&channel->incomingReliableCommands); currentCommand = enet_list_previous(currentCommand) ) { ENetIncomingCommand *incomingCommand = (ENetIncomingCommand *) currentCommand; if (startSequenceNumber >= channel->incomingReliableSequenceNumber) { if (incomingCommand->reliableSequenceNumber < channel->incomingReliableSequenceNumber) { continue; } } else if (incomingCommand->reliableSequenceNumber >= channel->incomingReliableSequenceNumber) { break; } if (incomingCommand->reliableSequenceNumber <= startSequenceNumber) { if (incomingCommand->reliableSequenceNumber < startSequenceNumber) { break; } if ((incomingCommand->command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_FRAGMENT || totalLength != incomingCommand->packet->dataLength || fragmentCount != incomingCommand->fragmentCount ) { return -1; } startCommand = incomingCommand; break; } } if (startCommand == NULL) { ENetProtocol hostCommand = *command; hostCommand.header.reliableSequenceNumber = startSequenceNumber; startCommand = enet_peer_queue_incoming_command(peer, &hostCommand, NULL, totalLength, ENET_PACKET_FLAG_RELIABLE, fragmentCount); if (startCommand == NULL) { return -1; } } if ((startCommand->fragments[fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0) { --startCommand->fragmentsRemaining; startCommand->fragments[fragmentNumber / 32] |= (1 << (fragmentNumber % 32)); if (fragmentOffset + fragmentLength > startCommand->packet->dataLength) { fragmentLength = startCommand->packet->dataLength - fragmentOffset; } memcpy(startCommand->packet->data + fragmentOffset, (enet_uint8 *) command + sizeof(ENetProtocolSendFragment), fragmentLength); if (startCommand->fragmentsRemaining <= 0) { enet_peer_dispatch_incoming_reliable_commands(peer, channel); } } return 0; } /* enet_protocol_handle_send_fragment */ static int enet_protocol_handle_send_unreliable_fragment(ENetHost *host, ENetPeer *peer, const ENetProtocol *command, enet_uint8 **currentData) { enet_uint32 fragmentNumber, fragmentCount, fragmentOffset, fragmentLength, reliableSequenceNumber, startSequenceNumber, totalLength; enet_uint16 reliableWindow, currentWindow; ENetChannel *channel; ENetListIterator currentCommand; ENetIncomingCommand *startCommand = NULL; if (command->header.channelID >= peer->channelCount || (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER)) { return -1; } fragmentLength = ENET_NET_TO_HOST_16(command->sendFragment.dataLength); *currentData += fragmentLength; if (fragmentLength > host->maximumPacketSize || *currentData < host->receivedData || *currentData > &host->receivedData[host->receivedDataLength]) { return -1; } channel = &peer->channels[command->header.channelID]; reliableSequenceNumber = command->header.reliableSequenceNumber; startSequenceNumber = ENET_NET_TO_HOST_16(command->sendFragment.startSequenceNumber); reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; currentWindow = channel->incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (reliableSequenceNumber < channel->incomingReliableSequenceNumber) { reliableWindow += ENET_PEER_RELIABLE_WINDOWS; } if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) { return 0; } if (reliableSequenceNumber == channel->incomingReliableSequenceNumber && startSequenceNumber <= channel->incomingUnreliableSequenceNumber) { return 0; } fragmentNumber = ENET_NET_TO_HOST_32(command->sendFragment.fragmentNumber); fragmentCount = ENET_NET_TO_HOST_32(command->sendFragment.fragmentCount); fragmentOffset = ENET_NET_TO_HOST_32(command->sendFragment.fragmentOffset); totalLength = ENET_NET_TO_HOST_32(command->sendFragment.totalLength); if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT || fragmentNumber >= fragmentCount || totalLength > host->maximumPacketSize || fragmentOffset >= totalLength || fragmentLength > totalLength - fragmentOffset ) { return -1; } for (currentCommand = enet_list_previous(enet_list_end(&channel->incomingUnreliableCommands)); currentCommand != enet_list_end(&channel->incomingUnreliableCommands); currentCommand = enet_list_previous(currentCommand) ) { ENetIncomingCommand *incomingCommand = (ENetIncomingCommand *) currentCommand; if (reliableSequenceNumber >= channel->incomingReliableSequenceNumber) { if (incomingCommand->reliableSequenceNumber < channel->incomingReliableSequenceNumber) { continue; } } else if (incomingCommand->reliableSequenceNumber >= channel->incomingReliableSequenceNumber) { break; } if (incomingCommand->reliableSequenceNumber < reliableSequenceNumber) { break; } if (incomingCommand->reliableSequenceNumber > reliableSequenceNumber) { continue; } if (incomingCommand->unreliableSequenceNumber <= startSequenceNumber) { if (incomingCommand->unreliableSequenceNumber < startSequenceNumber) { break; } if ((incomingCommand->command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT || totalLength != incomingCommand->packet->dataLength || fragmentCount != incomingCommand->fragmentCount ) { return -1; } startCommand = incomingCommand; break; } } if (startCommand == NULL) { startCommand = enet_peer_queue_incoming_command(peer, command, NULL, totalLength, ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT, fragmentCount); if (startCommand == NULL) { return -1; } } if ((startCommand->fragments[fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0) { --startCommand->fragmentsRemaining; startCommand->fragments[fragmentNumber / 32] |= (1 << (fragmentNumber % 32)); if (fragmentOffset + fragmentLength > startCommand->packet->dataLength) { fragmentLength = startCommand->packet->dataLength - fragmentOffset; } memcpy(startCommand->packet->data + fragmentOffset, (enet_uint8 *) command + sizeof(ENetProtocolSendFragment), fragmentLength); if (startCommand->fragmentsRemaining <= 0) { enet_peer_dispatch_incoming_unreliable_commands(peer, channel); } } return 0; } /* enet_protocol_handle_send_unreliable_fragment */ static int enet_protocol_handle_ping(ENetHost *host, ENetPeer *peer, const ENetProtocol *command) { ENET_UNUSED(host) ENET_UNUSED(command) if (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) { return -1; } return 0; } static int enet_protocol_handle_bandwidth_limit(ENetHost *host, ENetPeer *peer, const ENetProtocol *command) { if (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) { return -1; } if (peer->incomingBandwidth != 0) { --host->bandwidthLimitedPeers; } peer->incomingBandwidth = ENET_NET_TO_HOST_32(command->bandwidthLimit.incomingBandwidth); if (peer->incomingBandwidth != 0) { ++host->bandwidthLimitedPeers; } 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 if (peer->incomingBandwidth == 0 || host->outgoingBandwidth == 0) { peer->windowSize = (ENET_MAX(peer->incomingBandwidth, host->outgoingBandwidth) / ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } else { peer->windowSize = (ENET_MIN(peer->incomingBandwidth, host->outgoingBandwidth) / ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } if (peer->windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) { peer->windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } else if (peer->windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) { peer->windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } return 0; } /* enet_protocol_handle_bandwidth_limit */ static int enet_protocol_handle_throttle_configure(ENetHost *host, ENetPeer *peer, const ENetProtocol *command) { ENET_UNUSED(host) if (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) { return -1; } peer->packetThrottleInterval = ENET_NET_TO_HOST_32(command->throttleConfigure.packetThrottleInterval); peer->packetThrottleAcceleration = ENET_NET_TO_HOST_32(command->throttleConfigure.packetThrottleAcceleration); peer->packetThrottleDeceleration = ENET_NET_TO_HOST_32(command->throttleConfigure.packetThrottleDeceleration); return 0; } static int enet_protocol_handle_disconnect(ENetHost *host, ENetPeer *peer, const ENetProtocol *command) { if (peer->state == ENET_PEER_STATE_DISCONNECTED || peer->state == ENET_PEER_STATE_ZOMBIE || peer->state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT ) { return 0; } enet_peer_reset_queues(peer); if (peer->state == ENET_PEER_STATE_CONNECTION_SUCCEEDED || peer->state == ENET_PEER_STATE_DISCONNECTING || peer->state == ENET_PEER_STATE_CONNECTING) { enet_protocol_dispatch_state(host, peer, ENET_PEER_STATE_ZOMBIE); } else if (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) { if (peer->state == ENET_PEER_STATE_CONNECTION_PENDING) { host->recalculateBandwidthLimits = 1; } enet_peer_reset(peer); } else if (command->header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) { enet_protocol_change_state(host, peer, ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT); } else { enet_protocol_dispatch_state(host, peer, ENET_PEER_STATE_ZOMBIE); } if (peer->state != ENET_PEER_STATE_DISCONNECTED) { peer->eventData = ENET_NET_TO_HOST_32(command->disconnect.data); } return 0; } static int enet_protocol_handle_acknowledge(ENetHost *host, ENetEvent *event, ENetPeer *peer, const ENetProtocol *command) { enet_uint32 roundTripTime, receivedSentTime, receivedReliableSequenceNumber; ENetProtocolCommand commandNumber; if (peer->state == ENET_PEER_STATE_DISCONNECTED || peer->state == ENET_PEER_STATE_ZOMBIE) { return 0; } receivedSentTime = ENET_NET_TO_HOST_16(command->acknowledge.receivedSentTime); receivedSentTime |= host->serviceTime & 0xFFFF0000; if ((receivedSentTime & 0x8000) > (host->serviceTime & 0x8000)) { receivedSentTime -= 0x10000; } if (ENET_TIME_LESS(host->serviceTime, receivedSentTime)) { return 0; } peer->lastReceiveTime = host->serviceTime; peer->earliestTimeout = 0; roundTripTime = ENET_TIME_DIFFERENCE(host->serviceTime, receivedSentTime); enet_peer_throttle(peer, roundTripTime); peer->roundTripTimeVariance -= peer->roundTripTimeVariance / 4; if (roundTripTime >= peer->roundTripTime) { peer->roundTripTime += (roundTripTime - peer->roundTripTime) / 8; peer->roundTripTimeVariance += (roundTripTime - peer->roundTripTime) / 4; } else { peer->roundTripTime -= (peer->roundTripTime - roundTripTime) / 8; peer->roundTripTimeVariance += (peer->roundTripTime - roundTripTime) / 4; } if (peer->roundTripTime < peer->lowestRoundTripTime) { peer->lowestRoundTripTime = peer->roundTripTime; } if (peer->roundTripTimeVariance > peer->highestRoundTripTimeVariance) { peer->highestRoundTripTimeVariance = peer->roundTripTimeVariance; } if (peer->packetThrottleEpoch == 0 || ENET_TIME_DIFFERENCE(host->serviceTime, peer->packetThrottleEpoch) >= peer->packetThrottleInterval ) { peer->lastRoundTripTime = peer->lowestRoundTripTime; peer->lastRoundTripTimeVariance = peer->highestRoundTripTimeVariance; peer->lowestRoundTripTime = peer->roundTripTime; peer->highestRoundTripTimeVariance = peer->roundTripTimeVariance; peer->packetThrottleEpoch = host->serviceTime; } receivedReliableSequenceNumber = ENET_NET_TO_HOST_16(command->acknowledge.receivedReliableSequenceNumber); commandNumber = enet_protocol_remove_sent_reliable_command(peer, receivedReliableSequenceNumber, command->header.channelID); switch (peer->state) { case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT: if (commandNumber != ENET_PROTOCOL_COMMAND_VERIFY_CONNECT) { return -1; } enet_protocol_notify_connect(host, peer, event); break; case ENET_PEER_STATE_DISCONNECTING: if (commandNumber != ENET_PROTOCOL_COMMAND_DISCONNECT) { return -1; } enet_protocol_notify_disconnect(host, peer, event); break; case ENET_PEER_STATE_DISCONNECT_LATER: if (enet_list_empty(&peer->outgoingReliableCommands) && enet_list_empty(&peer->outgoingUnreliableCommands) && enet_list_empty(&peer->sentReliableCommands)) { enet_peer_disconnect(peer, peer->eventData); } break; default: break; } return 0; } /* enet_protocol_handle_acknowledge */ static int enet_protocol_handle_verify_connect(ENetHost *host, ENetEvent *event, ENetPeer *peer, const ENetProtocol *command) { enet_uint32 mtu, windowSize; size_t channelCount; if (peer->state != ENET_PEER_STATE_CONNECTING) { return 0; } channelCount = ENET_NET_TO_HOST_32(command->verifyConnect.channelCount); if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT || ENET_NET_TO_HOST_32(command->verifyConnect.packetThrottleInterval) != peer->packetThrottleInterval || ENET_NET_TO_HOST_32(command->verifyConnect.packetThrottleAcceleration) != peer->packetThrottleAcceleration || ENET_NET_TO_HOST_32(command->verifyConnect.packetThrottleDeceleration) != peer->packetThrottleDeceleration || command->verifyConnect.connectID != peer->connectID ) { peer->eventData = 0; enet_protocol_dispatch_state(host, peer, ENET_PEER_STATE_ZOMBIE); return -1; } enet_protocol_remove_sent_reliable_command(peer, 1, 0xFF); if (channelCount < peer->channelCount) { peer->channelCount = channelCount; } peer->outgoingPeerID = ENET_NET_TO_HOST_16(command->verifyConnect.outgoingPeerID); peer->incomingSessionID = command->verifyConnect.incomingSessionID; peer->outgoingSessionID = command->verifyConnect.outgoingSessionID; mtu = ENET_NET_TO_HOST_32(command->verifyConnect.mtu); if (mtu < ENET_PROTOCOL_MINIMUM_MTU) { mtu = ENET_PROTOCOL_MINIMUM_MTU; } else if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) { mtu = ENET_PROTOCOL_MAXIMUM_MTU; } if (mtu < peer->mtu) { peer->mtu = mtu; } windowSize = ENET_NET_TO_HOST_32(command->verifyConnect.windowSize); if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) { windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) { windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } if (windowSize < peer->windowSize) { peer->windowSize = windowSize; } peer->incomingBandwidth = ENET_NET_TO_HOST_32(command->verifyConnect.incomingBandwidth); peer->outgoingBandwidth = ENET_NET_TO_HOST_32(command->verifyConnect.outgoingBandwidth); enet_protocol_notify_connect(host, peer, event); return 0; } /* enet_protocol_handle_verify_connect */ static int enet_protocol_handle_incoming_commands(ENetHost *host, ENetEvent *event) { ENetProtocolHeader *header; ENetProtocol *command; ENetPeer *peer; enet_uint8 *currentData; size_t headerSize; enet_uint16 peerID, flags; enet_uint8 sessionID; if (host->receivedDataLength < (size_t) &((ENetProtocolHeader *) 0)->sentTime) { return 0; } header = (ENetProtocolHeader *) host->receivedData; peerID = ENET_NET_TO_HOST_16(header->peerID); sessionID = (peerID & ENET_PROTOCOL_HEADER_SESSION_MASK) >> ENET_PROTOCOL_HEADER_SESSION_SHIFT; flags = peerID & ENET_PROTOCOL_HEADER_FLAG_MASK; peerID &= ~(ENET_PROTOCOL_HEADER_FLAG_MASK | ENET_PROTOCOL_HEADER_SESSION_MASK); headerSize = (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME ? sizeof(ENetProtocolHeader) : (size_t) &((ENetProtocolHeader *) 0)->sentTime); if (host->checksum != NULL) { headerSize += sizeof(enet_uint32); } if (peerID == ENET_PROTOCOL_MAXIMUM_PEER_ID) { peer = NULL; } else if (peerID >= host->peerCount) { return 0; } else { peer = &host->peers[peerID]; if (peer->state == ENET_PEER_STATE_DISCONNECTED || peer->state == ENET_PEER_STATE_ZOMBIE || ((!in6_equal(host->receivedAddress.host , peer->address.host) || host->receivedAddress.port != peer->address.port) && 1 /* no broadcast in ipv6 !in6_equal(peer->address.host , ENET_HOST_BROADCAST)*/) || (peer->outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID && sessionID != peer->incomingSessionID) ) { return 0; } } if (flags & ENET_PROTOCOL_HEADER_FLAG_COMPRESSED) { size_t originalSize; if (host->compressor.context == NULL || host->compressor.decompress == NULL) { return 0; } originalSize = host->compressor.decompress(host->compressor.context, host->receivedData + headerSize, host->receivedDataLength - headerSize, host->packetData[1] + headerSize, sizeof(host->packetData[1]) - headerSize ); if (originalSize <= 0 || originalSize > sizeof(host->packetData[1]) - headerSize) { return 0; } memcpy(host->packetData[1], header, headerSize); host->receivedData = host->packetData[1]; host->receivedDataLength = headerSize + originalSize; } if (host->checksum != NULL) { enet_uint32 *checksum = (enet_uint32 *) &host->receivedData[headerSize - sizeof(enet_uint32)]; enet_uint32 desiredChecksum = *checksum; ENetBuffer buffer; *checksum = peer != NULL ? peer->connectID : 0; buffer.data = host->receivedData; buffer.dataLength = host->receivedDataLength; if (host->checksum(&buffer, 1) != desiredChecksum) { return 0; } } if (peer != NULL) { peer->address.host = host->receivedAddress.host; peer->address.port = host->receivedAddress.port; peer->incomingDataTotal += host->receivedDataLength; peer->totalDataReceived += host->receivedDataLength; } currentData = host->receivedData + headerSize; while (currentData < &host->receivedData[host->receivedDataLength]) { enet_uint8 commandNumber; size_t commandSize; command = (ENetProtocol *) currentData; if (currentData + sizeof(ENetProtocolCommandHeader) > &host->receivedData[host->receivedDataLength]) { break; } commandNumber = command->header.command & ENET_PROTOCOL_COMMAND_MASK; if (commandNumber >= ENET_PROTOCOL_COMMAND_COUNT) { break; } commandSize = commandSizes[commandNumber]; if (commandSize == 0 || currentData + commandSize > &host->receivedData[host->receivedDataLength]) { break; } currentData += commandSize; if (peer == NULL && (commandNumber != ENET_PROTOCOL_COMMAND_CONNECT || currentData < &host->receivedData[host->receivedDataLength])) { break; } command->header.reliableSequenceNumber = ENET_NET_TO_HOST_16(command->header.reliableSequenceNumber); switch (commandNumber) { case ENET_PROTOCOL_COMMAND_ACKNOWLEDGE: if (enet_protocol_handle_acknowledge(host, event, peer, command)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_CONNECT: if (peer != NULL) { goto commandError; } peer = enet_protocol_handle_connect(host, header, command); if (peer == NULL) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_VERIFY_CONNECT: if (enet_protocol_handle_verify_connect(host, event, peer, command)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_DISCONNECT: if (enet_protocol_handle_disconnect(host, peer, command)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_PING: if (enet_protocol_handle_ping(host, peer, command)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: if (enet_protocol_handle_send_reliable(host, peer, command, ¤tData)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: if (enet_protocol_handle_send_unreliable(host, peer, command, ¤tData)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: if (enet_protocol_handle_send_unsequenced(host, peer, command, ¤tData)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: if (enet_protocol_handle_send_fragment(host, peer, command, ¤tData)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT: if (enet_protocol_handle_bandwidth_limit(host, peer, command)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE: if (enet_protocol_handle_throttle_configure(host, peer, command)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT: if (enet_protocol_handle_send_unreliable_fragment(host, peer, command, ¤tData)) { goto commandError; } break; default: goto commandError; } assert(peer); if ((command->header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) != 0) { enet_uint16 sentTime; if (!(flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME)) { break; } sentTime = ENET_NET_TO_HOST_16(header->sentTime); switch (peer->state) { case ENET_PEER_STATE_DISCONNECTING: case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT: case ENET_PEER_STATE_DISCONNECTED: case ENET_PEER_STATE_ZOMBIE: break; case ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT: if ((command->header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT) { enet_peer_queue_acknowledgement(peer, command, sentTime); } break; default: enet_peer_queue_acknowledgement(peer, command, sentTime); break; } } } commandError: if (event != NULL && event->type != ENET_EVENT_TYPE_NONE) { return 1; } return 0; } /* enet_protocol_handle_incoming_commands */ static int enet_protocol_receive_incoming_commands(ENetHost *host, ENetEvent *event) { int packets; for (packets = 0; packets < 256; ++packets) { int receivedLength; ENetBuffer buffer; buffer.data = host->packetData[0]; // buffer.dataLength = sizeof (host->packetData[0]); buffer.dataLength = host->mtu; receivedLength = enet_socket_receive(host->socket, &host->receivedAddress, &buffer, 1); if (receivedLength == -2) continue; if (receivedLength < 0) { return -1; } if (receivedLength == 0) { return 0; } host->receivedData = host->packetData[0]; host->receivedDataLength = receivedLength; host->totalReceivedData += receivedLength; host->totalReceivedPackets++; if (host->intercept != NULL) { switch (host->intercept(host, (void *)event)) { case 1: if (event != NULL && event->type != ENET_EVENT_TYPE_NONE) { return 1; } continue; case -1: return -1; default: break; } } switch (enet_protocol_handle_incoming_commands(host, event)) { case 1: return 1; case -1: return -1; default: break; } } return -1; } /* enet_protocol_receive_incoming_commands */ 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; enet_uint16 reliableSequenceNumber; currentAcknowledgement = enet_list_begin(&peer->acknowledgements); while (currentAcknowledgement != enet_list_end(&peer->acknowledgements)) { if (command >= &host->commands[sizeof(host->commands) / sizeof(ENetProtocol)] || buffer >= &host->buffers[sizeof(host->buffers) / sizeof(ENetBuffer)] || peer->mtu - host->packetSize < sizeof(ENetProtocolAcknowledge) ) { host->continueSending = 1; break; } acknowledgement = (ENetAcknowledgement *) currentAcknowledgement; currentAcknowledgement = enet_list_next(currentAcknowledgement); buffer->data = command; buffer->dataLength = sizeof(ENetProtocolAcknowledge); host->packetSize += buffer->dataLength; reliableSequenceNumber = ENET_HOST_TO_NET_16(acknowledgement->command.header.reliableSequenceNumber); command->header.command = ENET_PROTOCOL_COMMAND_ACKNOWLEDGE; command->header.channelID = acknowledgement->command.header.channelID; command->header.reliableSequenceNumber = reliableSequenceNumber; command->acknowledge.receivedReliableSequenceNumber = reliableSequenceNumber; command->acknowledge.receivedSentTime = ENET_HOST_TO_NET_16(acknowledgement->sentTime); if ((acknowledgement->command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT) { enet_protocol_dispatch_state(host, peer, ENET_PEER_STATE_ZOMBIE); } enet_list_remove(&acknowledgement->acknowledgementList); enet_free(acknowledgement); ++command; ++buffer; } host->commandCount = command - host->commands; host->bufferCount = buffer - host->buffers; } /* enet_protocol_send_acknowledgements */ static void enet_protocol_send_unreliable_outgoing_commands(ENetHost *host, ENetPeer *peer) { ENetProtocol *command = &host->commands[host->commandCount]; ENetBuffer *buffer = &host->buffers[host->bufferCount]; ENetOutgoingCommand *outgoingCommand; ENetListIterator currentCommand; currentCommand = enet_list_begin(&peer->outgoingUnreliableCommands); while (currentCommand != enet_list_end(&peer->outgoingUnreliableCommands)) { size_t commandSize; outgoingCommand = (ENetOutgoingCommand *) currentCommand; commandSize = commandSizes[outgoingCommand->command.header.command & ENET_PROTOCOL_COMMAND_MASK]; if (command >= &host->commands[sizeof(host->commands) / sizeof(ENetProtocol)] || buffer + 1 >= &host->buffers[sizeof(host->buffers) / sizeof(ENetBuffer)] || peer->mtu - host->packetSize < commandSize || (outgoingCommand->packet != NULL && peer->mtu - host->packetSize < commandSize + outgoingCommand->fragmentLength) ) { host->continueSending = 1; break; } currentCommand = enet_list_next(currentCommand); if (outgoingCommand->packet != NULL && outgoingCommand->fragmentOffset == 0) { peer->packetThrottleCounter += ENET_PEER_PACKET_THROTTLE_COUNTER; peer->packetThrottleCounter %= ENET_PEER_PACKET_THROTTLE_SCALE; if (peer->packetThrottleCounter > peer->packetThrottle) { enet_uint16 reliableSequenceNumber = outgoingCommand->reliableSequenceNumber; enet_uint16 unreliableSequenceNumber = outgoingCommand->unreliableSequenceNumber; for (;;) { --outgoingCommand->packet->referenceCount; if (outgoingCommand->packet->referenceCount == 0) { callbacks.packet_destroy(outgoingCommand->packet); } enet_list_remove(&outgoingCommand->outgoingCommandList); enet_free(outgoingCommand); if (currentCommand == enet_list_end(&peer->outgoingUnreliableCommands)) { break; } outgoingCommand = (ENetOutgoingCommand *) currentCommand; if (outgoingCommand->reliableSequenceNumber != reliableSequenceNumber || outgoingCommand->unreliableSequenceNumber != unreliableSequenceNumber) { break; } currentCommand = enet_list_next(currentCommand); } continue; } } buffer->data = command; buffer->dataLength = commandSize; host->packetSize += buffer->dataLength; *command = outgoingCommand->command; enet_list_remove(&outgoingCommand->outgoingCommandList); if (outgoingCommand->packet != NULL) { ++buffer; buffer->data = outgoingCommand->packet->data + outgoingCommand->fragmentOffset; buffer->dataLength = outgoingCommand->fragmentLength; host->packetSize += buffer->dataLength; enet_list_insert(enet_list_end(&peer->sentUnreliableCommands), outgoingCommand); } else { enet_free(outgoingCommand); } ++command; ++buffer; } host->commandCount = command - host->commands; host->bufferCount = buffer - host->buffers; if (peer->state == ENET_PEER_STATE_DISCONNECT_LATER && enet_list_empty(&peer->outgoingReliableCommands) && enet_list_empty(&peer->outgoingUnreliableCommands) && enet_list_empty(&peer->sentReliableCommands)) { enet_peer_disconnect(peer, peer->eventData); } } /* enet_protocol_send_unreliable_outgoing_commands */ static int enet_protocol_check_timeouts(ENetHost *host, ENetPeer *peer, ENetEvent *event) { ENetOutgoingCommand *outgoingCommand; ENetListIterator currentCommand, insertPosition; currentCommand = enet_list_begin(&peer->sentReliableCommands); insertPosition = enet_list_begin(&peer->outgoingReliableCommands); while (currentCommand != enet_list_end(&peer->sentReliableCommands)) { outgoingCommand = (ENetOutgoingCommand *) currentCommand; currentCommand = enet_list_next(currentCommand); if (ENET_TIME_DIFFERENCE(host->serviceTime, outgoingCommand->sentTime) < outgoingCommand->roundTripTimeout) { continue; } if (peer->earliestTimeout == 0 || ENET_TIME_LESS(outgoingCommand->sentTime, peer->earliestTimeout)) { peer->earliestTimeout = outgoingCommand->sentTime; } if (peer->earliestTimeout != 0 && (ENET_TIME_DIFFERENCE(host->serviceTime, peer->earliestTimeout) >= peer->timeoutMaximum || (outgoingCommand->roundTripTimeout >= outgoingCommand->roundTripTimeoutLimit && ENET_TIME_DIFFERENCE(host->serviceTime, peer->earliestTimeout) >= peer->timeoutMinimum)) ) { enet_protocol_notify_disconnect_timeout(host, peer, event); return 1; } if (outgoingCommand->packet != NULL) { peer->reliableDataInTransit -= outgoingCommand->fragmentLength; } ++peer->packetsLost; ++peer->totalPacketsLost; /* Replaced exponential backoff time with something more linear */ /* Source: http://lists.cubik.org/pipermail/enet-discuss/2014-May/002308.html */ outgoingCommand->roundTripTimeout = peer->roundTripTime + 4 * peer->roundTripTimeVariance; outgoingCommand->roundTripTimeoutLimit = peer->timeoutLimit * outgoingCommand->roundTripTimeout; enet_list_insert(insertPosition, enet_list_remove(&outgoingCommand->outgoingCommandList)); if (currentCommand == enet_list_begin(&peer->sentReliableCommands) && !enet_list_empty(&peer->sentReliableCommands)) { outgoingCommand = (ENetOutgoingCommand *) currentCommand; peer->nextTimeout = outgoingCommand->sentTime + outgoingCommand->roundTripTimeout; } } return 0; } /* enet_protocol_check_timeouts */ static int enet_protocol_send_reliable_outgoing_commands(ENetHost *host, ENetPeer *peer) { ENetProtocol *command = &host->commands[host->commandCount]; ENetBuffer *buffer = &host->buffers[host->bufferCount]; ENetOutgoingCommand *outgoingCommand; ENetListIterator currentCommand; ENetChannel *channel; enet_uint16 reliableWindow; size_t commandSize; int windowExceeded = 0, windowWrap = 0, canPing = 1; currentCommand = enet_list_begin(&peer->outgoingReliableCommands); while (currentCommand != enet_list_end(&peer->outgoingReliableCommands)) { outgoingCommand = (ENetOutgoingCommand *) currentCommand; channel = outgoingCommand->command.header.channelID < peer->channelCount ? &peer->channels[outgoingCommand->command.header.channelID] : NULL; reliableWindow = outgoingCommand->reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (channel != NULL) { if (!windowWrap && outgoingCommand->sendAttempts < 1 && !(outgoingCommand->reliableSequenceNumber % ENET_PEER_RELIABLE_WINDOW_SIZE) && (channel->reliableWindows[(reliableWindow + ENET_PEER_RELIABLE_WINDOWS - 1) % ENET_PEER_RELIABLE_WINDOWS] >= ENET_PEER_RELIABLE_WINDOW_SIZE || channel->usedReliableWindows & ((((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) << reliableWindow) | (((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) >> (ENET_PEER_RELIABLE_WINDOWS - reliableWindow)))) ) { windowWrap = 1; } if (windowWrap) { currentCommand = enet_list_next(currentCommand); continue; } } if (outgoingCommand->packet != NULL) { if (!windowExceeded) { enet_uint32 windowSize = (peer->packetThrottle * peer->windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE; if (peer->reliableDataInTransit + outgoingCommand->fragmentLength > ENET_MAX(windowSize, peer->mtu)) { windowExceeded = 1; } } if (windowExceeded) { currentCommand = enet_list_next(currentCommand); continue; } } canPing = 0; commandSize = commandSizes[outgoingCommand->command.header.command & ENET_PROTOCOL_COMMAND_MASK]; if (command >= &host->commands[sizeof(host->commands) / sizeof(ENetProtocol)] || buffer + 1 >= &host->buffers[sizeof(host->buffers) / sizeof(ENetBuffer)] || peer->mtu - host->packetSize < commandSize || (outgoingCommand->packet != NULL && (enet_uint16) (peer->mtu - host->packetSize) < (enet_uint16) (commandSize + outgoingCommand->fragmentLength)) ) { host->continueSending = 1; break; } currentCommand = enet_list_next(currentCommand); if (channel != NULL && outgoingCommand->sendAttempts < 1) { channel->usedReliableWindows |= 1 << reliableWindow; ++channel->reliableWindows[reliableWindow]; } ++outgoingCommand->sendAttempts; if (outgoingCommand->roundTripTimeout == 0) { outgoingCommand->roundTripTimeout = peer->roundTripTime + 4 * peer->roundTripTimeVariance; outgoingCommand->roundTripTimeoutLimit = peer->timeoutLimit * outgoingCommand->roundTripTimeout; } if (enet_list_empty(&peer->sentReliableCommands)) { peer->nextTimeout = host->serviceTime + outgoingCommand->roundTripTimeout; } enet_list_insert(enet_list_end(&peer->sentReliableCommands), enet_list_remove(&outgoingCommand->outgoingCommandList)); outgoingCommand->sentTime = host->serviceTime; buffer->data = command; buffer->dataLength = commandSize; host->packetSize += buffer->dataLength; host->headerFlags |= ENET_PROTOCOL_HEADER_FLAG_SENT_TIME; *command = outgoingCommand->command; if (outgoingCommand->packet != NULL) { ++buffer; buffer->data = outgoingCommand->packet->data + outgoingCommand->fragmentOffset; buffer->dataLength = outgoingCommand->fragmentLength; host->packetSize += outgoingCommand->fragmentLength; peer->reliableDataInTransit += outgoingCommand->fragmentLength; } ++peer->packetsSent; ++peer->totalPacketsSent; ++command; ++buffer; } host->commandCount = command - host->commands; host->bufferCount = buffer - host->buffers; return canPing; } /* enet_protocol_send_reliable_outgoing_commands */ static int enet_protocol_send_outgoing_commands(ENetHost *host, ENetEvent *event, int checkForTimeouts) { enet_uint8 headerData[sizeof(ENetProtocolHeader) + sizeof(enet_uint32)]; ENetProtocolHeader *header = (ENetProtocolHeader *) headerData; ENetPeer *currentPeer; int sentLength; size_t shouldCompress = 0; host->continueSending = 1; while (host->continueSending) for (host->continueSending = 0, currentPeer = host->peers; currentPeer < &host->peers[host->peerCount]; ++currentPeer) { if (currentPeer->state == ENET_PEER_STATE_DISCONNECTED || currentPeer->state == ENET_PEER_STATE_ZOMBIE) { continue; } host->headerFlags = 0; host->commandCount = 0; host->bufferCount = 1; host->packetSize = sizeof(ENetProtocolHeader); if (!enet_list_empty(¤tPeer->acknowledgements)) { enet_protocol_send_acknowledgements(host, currentPeer); } if (checkForTimeouts != 0 && !enet_list_empty(¤tPeer->sentReliableCommands) && ENET_TIME_GREATER_EQUAL(host->serviceTime, currentPeer->nextTimeout) && enet_protocol_check_timeouts(host, currentPeer, event) == 1 ) { if (event != NULL && event->type != ENET_EVENT_TYPE_NONE) { return 1; } else { continue; } } if ((enet_list_empty(¤tPeer->outgoingReliableCommands) || enet_protocol_send_reliable_outgoing_commands(host, currentPeer)) && enet_list_empty(¤tPeer->sentReliableCommands) && ENET_TIME_DIFFERENCE(host->serviceTime, currentPeer->lastReceiveTime) >= currentPeer->pingInterval && currentPeer->mtu - host->packetSize >= sizeof(ENetProtocolPing) ) { enet_peer_ping(currentPeer); enet_protocol_send_reliable_outgoing_commands(host, currentPeer); } if (!enet_list_empty(¤tPeer->outgoingUnreliableCommands)) { enet_protocol_send_unreliable_outgoing_commands(host, currentPeer); } if (host->commandCount == 0) { continue; } if (currentPeer->packetLossEpoch == 0) { currentPeer->packetLossEpoch = host->serviceTime; } else if (ENET_TIME_DIFFERENCE(host->serviceTime, currentPeer->packetLossEpoch) >= ENET_PEER_PACKET_LOSS_INTERVAL && currentPeer->packetsSent > 0) { enet_uint32 packetLoss = currentPeer->packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer->packetsSent; #ifdef ENET_DEBUG 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(¤tPeer->outgoingReliableCommands), enet_list_size(¤tPeer->outgoingUnreliableCommands), currentPeer->channels != NULL ? enet_list_size( ¤tPeer->channels->incomingReliableCommands) : 0, currentPeer->channels != NULL ? enet_list_size(¤tPeer->channels->incomingUnreliableCommands) : 0 ); #endif currentPeer->packetLossVariance -= currentPeer->packetLossVariance / 4; if (packetLoss >= currentPeer->packetLoss) { currentPeer->packetLoss += (packetLoss - currentPeer->packetLoss) / 8; currentPeer->packetLossVariance += (packetLoss - currentPeer->packetLoss) / 4; } else { currentPeer->packetLoss -= (currentPeer->packetLoss - packetLoss) / 8; currentPeer->packetLossVariance += (currentPeer->packetLoss - packetLoss) / 4; } currentPeer->packetLossEpoch = host->serviceTime; currentPeer->packetsSent = 0; currentPeer->packetsLost = 0; } host->buffers[0].data = headerData; if (host->headerFlags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME) { header->sentTime = ENET_HOST_TO_NET_16(host->serviceTime & 0xFFFF); host->buffers[0].dataLength = sizeof(ENetProtocolHeader); } else { host->buffers[0].dataLength = (size_t) &((ENetProtocolHeader *) 0)->sentTime; } shouldCompress = 0; if (host->compressor.context != NULL && host->compressor.compress != NULL) { size_t originalSize = host->packetSize - sizeof(ENetProtocolHeader), compressedSize = host->compressor.compress(host->compressor.context, &host->buffers[1], host->bufferCount - 1, originalSize, host->packetData[1], originalSize); if (compressedSize > 0 && compressedSize < originalSize) { host->headerFlags |= ENET_PROTOCOL_HEADER_FLAG_COMPRESSED; shouldCompress = compressedSize; #ifdef ENET_DEBUG_COMPRESS printf("peer %u: compressed %u->%u (%u%%)\n", currentPeer->incomingPeerID, originalSize, compressedSize, (compressedSize * 100) / originalSize); #endif } } if (currentPeer->outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID) { host->headerFlags |= currentPeer->outgoingSessionID << ENET_PROTOCOL_HEADER_SESSION_SHIFT; } header->peerID = ENET_HOST_TO_NET_16(currentPeer->outgoingPeerID | host->headerFlags); if (host->checksum != NULL) { enet_uint32 *checksum = (enet_uint32 *) &headerData[host->buffers[0].dataLength]; *checksum = currentPeer->outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID ? currentPeer->connectID : 0; host->buffers[0].dataLength += sizeof(enet_uint32); *checksum = host->checksum(host->buffers, host->bufferCount); } if (shouldCompress > 0) { host->buffers[1].data = host->packetData[1]; host->buffers[1].dataLength = shouldCompress; host->bufferCount = 2; } currentPeer->lastSendTime = host->serviceTime; sentLength = enet_socket_send(host->socket, ¤tPeer->address, host->buffers, host->bufferCount); enet_protocol_remove_sent_unreliable_commands(currentPeer); if (sentLength < 0) { // The local 'headerData' array (to which 'data' is assigned) goes out // of scope on return from this function, so ensure we no longer point to it. host->buffers[0].data = NULL; return -1; } host->totalSentData += sentLength; currentPeer->totalDataSent += sentLength; host->totalSentPackets++; } // The local 'headerData' array (to which 'data' is assigned) goes out // of scope on return from this function, so ensure we no longer point to it. host->buffers[0].data = NULL; return 0; } /* enet_protocol_send_outgoing_commands */ /** Sends any queued packets on the host specified to its designated peers. * * @param host host to flush * @remarks this function need only be used in circumstances where one wishes to send queued packets earlier than in a call to enet_host_service(). * @ingroup host */ void enet_host_flush(ENetHost *host) { host->serviceTime = enet_time_get(); enet_protocol_send_outgoing_commands(host, NULL, 0); } /** Checks for any queued events on the host and dispatches one if available. * * @param host host to check for events * @param event an event structure where event details will be placed if available * @retval > 0 if an event was dispatched * @retval 0 if no events are available * @retval < 0 on failure * @ingroup host */ int enet_host_check_events(ENetHost *host, ENetEvent *event) { if (event == NULL) { return -1; } event->type = ENET_EVENT_TYPE_NONE; event->peer = NULL; event->packet = NULL; return enet_protocol_dispatch_incoming_commands(host, event); } /** Waits for events on the host specified and shuttles packets between * the host and its peers. * * @param host host to service * @param event an event structure where event details will be placed if one occurs * if event == NULL then no events will be delivered * @param timeout number of milliseconds that ENet should wait for events * @retval > 0 if an event occurred within the specified time limit * @retval 0 if no event occurred * @retval < 0 on failure * @remarks enet_host_service should be called fairly regularly for adequate performance * @ingroup host */ int enet_host_service(ENetHost *host, ENetEvent *event, enet_uint32 timeout) { enet_uint32 waitCondition; if (event != NULL) { event->type = ENET_EVENT_TYPE_NONE; event->peer = NULL; event->packet = NULL; switch (enet_protocol_dispatch_incoming_commands(host, event)) { case 1: return 1; case -1: #ifdef ENET_DEBUG perror("Error dispatching incoming packets"); #endif return -1; default: break; } } host->serviceTime = enet_time_get(); timeout += host->serviceTime; do { if (ENET_TIME_DIFFERENCE(host->serviceTime, host->bandwidthThrottleEpoch) >= ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL) { enet_host_bandwidth_throttle(host); } switch (enet_protocol_send_outgoing_commands(host, event, 1)) { case 1: return 1; case -1: #ifdef ENET_DEBUG perror("Error sending outgoing packets"); #endif return -1; default: break; } switch (enet_protocol_receive_incoming_commands(host, event)) { case 1: return 1; case -1: #ifdef ENET_DEBUG perror("Error receiving incoming packets"); #endif return -1; default: break; } switch (enet_protocol_send_outgoing_commands(host, event, 1)) { case 1: return 1; case -1: #ifdef ENET_DEBUG perror("Error sending outgoing packets"); #endif return -1; default: break; } if (event != NULL) { switch (enet_protocol_dispatch_incoming_commands(host, event)) { case 1: return 1; case -1: #ifdef ENET_DEBUG perror("Error dispatching incoming packets"); #endif return -1; default: break; } } if (ENET_TIME_GREATER_EQUAL(host->serviceTime, timeout)) { return 0; } do { host->serviceTime = enet_time_get(); if (ENET_TIME_GREATER_EQUAL(host->serviceTime, timeout)) { return 0; } waitCondition = ENET_SOCKET_WAIT_RECEIVE | ENET_SOCKET_WAIT_INTERRUPT; if (enet_socket_wait(host->socket, &waitCondition, ENET_TIME_DIFFERENCE(timeout, host->serviceTime)) != 0) { return -1; } } while (waitCondition & ENET_SOCKET_WAIT_INTERRUPT); host->serviceTime = enet_time_get(); } while (waitCondition & ENET_SOCKET_WAIT_RECEIVE); return 0; } /* enet_host_service */ // =======================================================================// // ! // ! Peer // ! // =======================================================================// /** Configures throttle parameter for a peer. * * Unreliable packets are dropped by ENet in response to the varying conditions * of the Internet connection to the peer. The throttle represents a probability * that an unreliable packet should not be dropped and thus sent by ENet to the peer. * The lowest mean round trip time from the sending of a reliable packet to the * receipt of its acknowledgement is measured over an amount of time specified by * the interval parameter in milliseconds. If a measured round trip time happens to * be significantly less than the mean round trip time measured over the interval, * then the throttle probability is increased to allow more traffic by an amount * specified in the acceleration parameter, which is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE * constant. If a measured round trip time happens to be significantly greater than * the mean round trip time measured over the interval, then the throttle probability * is decreased to limit traffic by an amount specified in the deceleration parameter, which * is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE constant. When the throttle has * a value of ENET_PEER_PACKET_THROTTLE_SCALE, no unreliable packets are dropped by * ENet, and so 100% of all unreliable packets will be sent. When the throttle has a * value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable * packets will be sent. Intermediate values for the throttle represent intermediate * probabilities between 0% and 100% of unreliable packets being sent. The bandwidth * limits of the local and foreign hosts are taken into account to determine a * sensible limit for the throttle probability above which it should not raise even in * the best of conditions. * * @param peer peer to configure * @param interval interval, in milliseconds, over which to measure lowest mean RTT; the default value is ENET_PEER_PACKET_THROTTLE_INTERVAL. * @param acceleration rate at which to increase the throttle probability as mean RTT declines * @param deceleration rate at which to decrease the throttle probability as mean RTT increases */ void enet_peer_throttle_configure(ENetPeer *peer, enet_uint32 interval, enet_uint32 acceleration, enet_uint32 deceleration) { ENetProtocol command; peer->packetThrottleInterval = interval; peer->packetThrottleAcceleration = acceleration; peer->packetThrottleDeceleration = deceleration; command.header.command = ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; command.header.channelID = 0xFF; command.throttleConfigure.packetThrottleInterval = ENET_HOST_TO_NET_32(interval); command.throttleConfigure.packetThrottleAcceleration = ENET_HOST_TO_NET_32(acceleration); command.throttleConfigure.packetThrottleDeceleration = ENET_HOST_TO_NET_32(deceleration); enet_peer_queue_outgoing_command(peer, &command, NULL, 0, 0); } int enet_peer_throttle(ENetPeer *peer, enet_uint32 rtt) { if (peer->lastRoundTripTime <= peer->lastRoundTripTimeVariance) { peer->packetThrottle = peer->packetThrottleLimit; } else if (rtt < peer->lastRoundTripTime) { peer->packetThrottle += peer->packetThrottleAcceleration; if (peer->packetThrottle > peer->packetThrottleLimit) { peer->packetThrottle = peer->packetThrottleLimit; } return 1; } else if (rtt > peer->lastRoundTripTime + 2 * peer->lastRoundTripTimeVariance) { if (peer->packetThrottle > peer->packetThrottleDeceleration) { peer->packetThrottle -= peer->packetThrottleDeceleration; } else { peer->packetThrottle = 0; } return -1; } return 0; } /* Extended functionality for easier binding in other programming languages */ enet_uint32 enet_host_get_peers_count(ENetHost *host) { return host->connectedPeers; } enet_uint32 enet_host_get_packets_sent(ENetHost *host) { return host->totalSentPackets; } enet_uint32 enet_host_get_packets_received(ENetHost *host) { return host->totalReceivedPackets; } enet_uint32 enet_host_get_bytes_sent(ENetHost *host) { return host->totalSentData; } enet_uint32 enet_host_get_bytes_received(ENetHost *host) { return host->totalReceivedData; } /** Gets received data buffer. Returns buffer length. * @param host host to access recevie buffer * @param data ouput parameter for recevied data * @retval buffer length */ enet_uint32 enet_host_get_received_data(ENetHost *host, /*out*/ enet_uint8** data) { *data = host->receivedData; return host->receivedDataLength; } enet_uint32 enet_host_get_mtu(ENetHost *host) { return host->mtu; } enet_uint32 enet_peer_get_id(ENetPeer *peer) { return peer->connectID; } enet_uint32 enet_peer_get_ip(ENetPeer *peer, char *ip, size_t ipLength) { return enet_address_get_host_ip(&peer->address, ip, ipLength); } enet_uint16 enet_peer_get_port(ENetPeer *peer) { return peer->address.port; } ENetPeerState enet_peer_get_state(ENetPeer *peer) { return peer->state; } enet_uint32 enet_peer_get_rtt(ENetPeer *peer) { return peer->roundTripTime; } enet_uint64 enet_peer_get_packets_sent(ENetPeer *peer) { return peer->totalPacketsSent; } enet_uint32 enet_peer_get_packets_lost(ENetPeer *peer) { return peer->totalPacketsLost; } enet_uint64 enet_peer_get_bytes_sent(ENetPeer *peer) { return peer->totalDataSent; } enet_uint64 enet_peer_get_bytes_received(ENetPeer *peer) { return peer->totalDataReceived; } void * enet_peer_get_data(ENetPeer *peer) { return (void *) peer->data; } void enet_peer_set_data(ENetPeer *peer, const void *data) { peer->data = (enet_uint32 *) data; } void * enet_packet_get_data(ENetPacket *packet) { return (void *) packet->data; } enet_uint32 enet_packet_get_length(ENetPacket *packet) { return packet->dataLength; } void enet_packet_set_free_callback(ENetPacket *packet, void *callback) { packet->freeCallback = (ENetPacketFreeCallback)callback; } /** Queues a packet to be sent. * @param peer destination for the packet * @param channelID channel on which to send * @param packet packet to send * @retval 0 on success * @retval < 0 on failure */ int enet_peer_send(ENetPeer *peer, enet_uint8 channelID, ENetPacket *packet) { ENetChannel *channel = &peer->channels[channelID]; ENetProtocol command; size_t fragmentLength; if (peer->state != ENET_PEER_STATE_CONNECTED || channelID >= peer->channelCount || packet->dataLength > peer->host->maximumPacketSize) { return -1; } fragmentLength = peer->mtu - sizeof(ENetProtocolHeader) - sizeof(ENetProtocolSendFragment); if (peer->host->checksum != NULL) { fragmentLength -= sizeof(enet_uint32); } if (packet->dataLength > fragmentLength) { enet_uint32 fragmentCount = (packet->dataLength + fragmentLength - 1) / fragmentLength, fragmentNumber, fragmentOffset; enet_uint8 commandNumber; enet_uint16 startSequenceNumber; ENetList fragments; ENetOutgoingCommand *fragment; if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT) { return -1; } if ((packet->flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT)) == ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT && channel->outgoingUnreliableSequenceNumber < 0xFFFF) { commandNumber = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT; startSequenceNumber = ENET_HOST_TO_NET_16(channel->outgoingUnreliableSequenceNumber + 1); } else { commandNumber = ENET_PROTOCOL_COMMAND_SEND_FRAGMENT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; startSequenceNumber = ENET_HOST_TO_NET_16(channel->outgoingReliableSequenceNumber + 1); } enet_list_clear(&fragments); for (fragmentNumber = 0, fragmentOffset = 0; fragmentOffset < packet->dataLength; ++fragmentNumber, fragmentOffset += fragmentLength) { if (packet->dataLength - fragmentOffset < fragmentLength) { fragmentLength = packet->dataLength - fragmentOffset; } fragment = (ENetOutgoingCommand *) enet_malloc(sizeof(ENetOutgoingCommand)); if (fragment == NULL) { while (!enet_list_empty(&fragments)) { fragment = (ENetOutgoingCommand *) enet_list_remove(enet_list_begin(&fragments)); enet_free(fragment); } return -1; } fragment->fragmentOffset = fragmentOffset; fragment->fragmentLength = fragmentLength; fragment->packet = packet; fragment->command.header.command = commandNumber; fragment->command.header.channelID = channelID; fragment->command.sendFragment.startSequenceNumber = startSequenceNumber; fragment->command.sendFragment.dataLength = ENET_HOST_TO_NET_16(fragmentLength); fragment->command.sendFragment.fragmentCount = ENET_HOST_TO_NET_32(fragmentCount); fragment->command.sendFragment.fragmentNumber = ENET_HOST_TO_NET_32(fragmentNumber); fragment->command.sendFragment.totalLength = ENET_HOST_TO_NET_32(packet->dataLength); fragment->command.sendFragment.fragmentOffset = ENET_NET_TO_HOST_32(fragmentOffset); enet_list_insert(enet_list_end(&fragments), fragment); } packet->referenceCount += fragmentNumber; while (!enet_list_empty(&fragments)) { fragment = (ENetOutgoingCommand *) enet_list_remove(enet_list_begin(&fragments)); enet_peer_setup_outgoing_command(peer, fragment); } return 0; } command.header.channelID = channelID; if ((packet->flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNSEQUENCED)) == ENET_PACKET_FLAG_UNSEQUENCED) { command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; command.sendUnsequenced.dataLength = ENET_HOST_TO_NET_16(packet->dataLength); } else if (packet->flags & ENET_PACKET_FLAG_RELIABLE || channel->outgoingUnreliableSequenceNumber >= 0xFFFF) { command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; command.sendReliable.dataLength = ENET_HOST_TO_NET_16(packet->dataLength); } else { command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE; command.sendUnreliable.dataLength = ENET_HOST_TO_NET_16(packet->dataLength); } if (enet_peer_queue_outgoing_command(peer, &command, packet, 0, packet->dataLength) == NULL) { return -1; } return 0; } // enet_peer_send /** Attempts to dequeue any incoming queued packet. * @param peer peer to dequeue packets from * @param channelID holds the channel ID of the channel the packet was received on success * @returns a pointer to the packet, or NULL if there are no available incoming queued packets */ ENetPacket * enet_peer_receive(ENetPeer *peer, enet_uint8 *channelID) { ENetIncomingCommand *incomingCommand; ENetPacket *packet; if (enet_list_empty(&peer->dispatchedCommands)) { return NULL; } incomingCommand = (ENetIncomingCommand *) enet_list_remove(enet_list_begin(&peer->dispatchedCommands)); if (channelID != NULL) { *channelID = incomingCommand->command.header.channelID; } packet = incomingCommand->packet; --packet->referenceCount; if (incomingCommand->fragments != NULL) { enet_free(incomingCommand->fragments); } enet_free(incomingCommand); peer->totalWaitingData -= packet->dataLength; return packet; } static void enet_peer_reset_outgoing_commands(ENetList *queue) { ENetOutgoingCommand *outgoingCommand; while (!enet_list_empty(queue)) { outgoingCommand = (ENetOutgoingCommand *) enet_list_remove(enet_list_begin(queue)); if (outgoingCommand->packet != NULL) { --outgoingCommand->packet->referenceCount; if (outgoingCommand->packet->referenceCount == 0) { callbacks.packet_destroy(outgoingCommand->packet); } } enet_free(outgoingCommand); } } static void enet_peer_remove_incoming_commands(ENetList *queue, ENetListIterator startCommand, ENetListIterator endCommand) { ENET_UNUSED(queue) ENetListIterator currentCommand; for (currentCommand = startCommand; currentCommand != endCommand;) { ENetIncomingCommand *incomingCommand = (ENetIncomingCommand *) currentCommand; currentCommand = enet_list_next(currentCommand); enet_list_remove(&incomingCommand->incomingCommandList); if (incomingCommand->packet != NULL) { --incomingCommand->packet->referenceCount; if (incomingCommand->packet->referenceCount == 0) { callbacks.packet_destroy(incomingCommand->packet); } } if (incomingCommand->fragments != NULL) { enet_free(incomingCommand->fragments); } enet_free(incomingCommand); } } static void enet_peer_reset_incoming_commands(ENetList *queue) { enet_peer_remove_incoming_commands(queue, enet_list_begin(queue), enet_list_end(queue)); } void enet_peer_reset_queues(ENetPeer *peer) { ENetChannel *channel; if (peer->needsDispatch) { enet_list_remove(&peer->dispatchList); peer->needsDispatch = 0; } while (!enet_list_empty(&peer->acknowledgements)) { enet_free(enet_list_remove(enet_list_begin(&peer->acknowledgements))); } enet_peer_reset_outgoing_commands(&peer->sentReliableCommands); enet_peer_reset_outgoing_commands(&peer->sentUnreliableCommands); enet_peer_reset_outgoing_commands(&peer->outgoingReliableCommands); enet_peer_reset_outgoing_commands(&peer->outgoingUnreliableCommands); enet_peer_reset_incoming_commands(&peer->dispatchedCommands); if (peer->channels != NULL && peer->channelCount > 0) { for (channel = peer->channels; channel < &peer->channels[peer->channelCount]; ++channel) { enet_peer_reset_incoming_commands(&channel->incomingReliableCommands); enet_peer_reset_incoming_commands(&channel->incomingUnreliableCommands); } enet_free(peer->channels); } peer->channels = NULL; peer->channelCount = 0; } void enet_peer_on_connect(ENetPeer *peer) { if (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) { if (peer->incomingBandwidth != 0) { ++peer->host->bandwidthLimitedPeers; } ++peer->host->connectedPeers; } } void enet_peer_on_disconnect(ENetPeer *peer) { if (peer->state == ENET_PEER_STATE_CONNECTED || peer->state == ENET_PEER_STATE_DISCONNECT_LATER) { if (peer->incomingBandwidth != 0) { --peer->host->bandwidthLimitedPeers; } --peer->host->connectedPeers; } } /** 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) { enet_peer_on_disconnect(peer); // We don't want to reset connectID here, otherwise, we can't get it in the Disconnect event // peer->connectID = 0; peer->outgoingPeerID = ENET_PROTOCOL_MAXIMUM_PEER_ID; peer->state = ENET_PEER_STATE_DISCONNECTED; peer->incomingBandwidth = 0; peer->outgoingBandwidth = 0; peer->incomingBandwidthThrottleEpoch = 0; peer->outgoingBandwidthThrottleEpoch = 0; peer->incomingDataTotal = 0; peer->totalDataReceived = 0; peer->outgoingDataTotal = 0; peer->totalDataSent = 0; peer->lastSendTime = 0; peer->lastReceiveTime = 0; peer->nextTimeout = 0; peer->earliestTimeout = 0; peer->packetLossEpoch = 0; peer->packetsSent = 0; peer->totalPacketsSent = 0; peer->packetsLost = 0; peer->totalPacketsLost = 0; peer->packetLoss = 0; peer->packetLossVariance = 0; peer->packetThrottle = ENET_PEER_DEFAULT_PACKET_THROTTLE; peer->packetThrottleLimit = ENET_PEER_PACKET_THROTTLE_SCALE; peer->packetThrottleCounter = 0; peer->packetThrottleEpoch = 0; peer->packetThrottleAcceleration = ENET_PEER_PACKET_THROTTLE_ACCELERATION; peer->packetThrottleDeceleration = ENET_PEER_PACKET_THROTTLE_DECELERATION; peer->packetThrottleInterval = ENET_PEER_PACKET_THROTTLE_INTERVAL; peer->pingInterval = ENET_PEER_PING_INTERVAL; peer->timeoutLimit = ENET_PEER_TIMEOUT_LIMIT; peer->timeoutMinimum = ENET_PEER_TIMEOUT_MINIMUM; peer->timeoutMaximum = ENET_PEER_TIMEOUT_MAXIMUM; peer->lastRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; peer->lowestRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; peer->lastRoundTripTimeVariance = 0; peer->highestRoundTripTimeVariance = 0; peer->roundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; peer->roundTripTimeVariance = 0; peer->mtu = peer->host->mtu; peer->reliableDataInTransit = 0; peer->outgoingReliableSequenceNumber = 0; peer->windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; peer->incomingUnsequencedGroup = 0; peer->outgoingUnsequencedGroup = 0; peer->eventData = 0; peer->totalWaitingData = 0; memset(peer->unsequencedWindow, 0, sizeof(peer->unsequencedWindow)); enet_peer_reset_queues(peer); } /** Sends a ping request to a peer. * @param peer destination for the ping request * @remarks ping requests factor into the mean round trip time as designated by the * roundTripTime field in the ENetPeer structure. ENet automatically pings all connected * peers at regular intervals, however, this function may be called to ensure more * frequent ping requests. */ void enet_peer_ping(ENetPeer *peer) { ENetProtocol command; if (peer->state != ENET_PEER_STATE_CONNECTED) { return; } command.header.command = ENET_PROTOCOL_COMMAND_PING | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; command.header.channelID = 0xFF; enet_peer_queue_outgoing_command(peer, &command, NULL, 0, 0); } /** Sets the interval at which pings will be sent to a peer. * * Pings are used both to monitor the liveness of the connection and also to dynamically * adjust the throttle during periods of low traffic so that the throttle has reasonable * responsiveness during traffic spikes. * * @param peer the peer to adjust * @param pingInterval the interval at which to send pings; defaults to ENET_PEER_PING_INTERVAL if 0 */ void enet_peer_ping_interval(ENetPeer *peer, enet_uint32 pingInterval) { peer->pingInterval = pingInterval ? pingInterval : (enet_uint32)ENET_PEER_PING_INTERVAL; } /** Sets the timeout parameters for a peer. * * The timeout parameter control how and when a peer will timeout from a failure to acknowledge * reliable traffic. Timeout values use an exponential backoff mechanism, where if a reliable * packet is not acknowledge within some multiple of the average RTT plus a variance tolerance, * the timeout will be doubled until it reaches a set limit. If the timeout is thus at this * limit and reliable packets have been sent but not acknowledged within a certain minimum time * period, the peer will be disconnected. Alternatively, if reliable packets have been sent * but not acknowledged for a certain maximum time period, the peer will be disconnected regardless * of the current timeout limit value. * * @param peer the peer to adjust * @param timeoutLimit the timeout limit; defaults to ENET_PEER_TIMEOUT_LIMIT if 0 * @param timeoutMinimum the timeout minimum; defaults to ENET_PEER_TIMEOUT_MINIMUM if 0 * @param timeoutMaximum the timeout maximum; defaults to ENET_PEER_TIMEOUT_MAXIMUM if 0 */ void enet_peer_timeout(ENetPeer *peer, enet_uint32 timeoutLimit, enet_uint32 timeoutMinimum, enet_uint32 timeoutMaximum) { peer->timeoutLimit = timeoutLimit ? timeoutLimit : (enet_uint32)ENET_PEER_TIMEOUT_LIMIT; peer->timeoutMinimum = timeoutMinimum ? timeoutMinimum : (enet_uint32)ENET_PEER_TIMEOUT_MINIMUM; peer->timeoutMaximum = timeoutMaximum ? timeoutMaximum : (enet_uint32)ENET_PEER_TIMEOUT_MAXIMUM; } /** Force an immediate disconnection from a peer. * @param peer peer to disconnect * @param data data describing the disconnection * @remarks No ENET_EVENT_DISCONNECT event will be generated. The foreign peer is not * guaranteed to receive the disconnect notification, and is reset immediately upon * return from this function. */ void enet_peer_disconnect_now(ENetPeer *peer, enet_uint32 data) { ENetProtocol command; if (peer->state == ENET_PEER_STATE_DISCONNECTED) { return; } if (peer->state != ENET_PEER_STATE_ZOMBIE && peer->state != ENET_PEER_STATE_DISCONNECTING) { enet_peer_reset_queues(peer); command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; command.header.channelID = 0xFF; command.disconnect.data = ENET_HOST_TO_NET_32(data); enet_peer_queue_outgoing_command(peer, &command, NULL, 0, 0); enet_host_flush(peer->host); } enet_peer_reset(peer); } /** Request a disconnection from a peer. * @param peer peer to request a disconnection * @param data data describing the disconnection * @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service() * once the disconnection is complete. */ void enet_peer_disconnect(ENetPeer *peer, enet_uint32 data) { ENetProtocol command; if (peer->state == ENET_PEER_STATE_DISCONNECTING || peer->state == ENET_PEER_STATE_DISCONNECTED || peer->state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT || peer->state == ENET_PEER_STATE_ZOMBIE ) { return; } enet_peer_reset_queues(peer); command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT; command.header.channelID = 0xFF; command.disconnect.data = ENET_HOST_TO_NET_32(data); if (peer->state == ENET_PEER_STATE_CONNECTED || peer->state == ENET_PEER_STATE_DISCONNECT_LATER) { command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; } else { command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; } enet_peer_queue_outgoing_command(peer, &command, NULL, 0, 0); if (peer->state == ENET_PEER_STATE_CONNECTED || peer->state == ENET_PEER_STATE_DISCONNECT_LATER) { enet_peer_on_disconnect(peer); peer->state = ENET_PEER_STATE_DISCONNECTING; } else { enet_host_flush(peer->host); enet_peer_reset(peer); } } /** Request a disconnection from a peer, but only after all queued outgoing packets are sent. * @param peer peer to request a disconnection * @param data data describing the disconnection * @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service() * once the disconnection is complete. */ void enet_peer_disconnect_later(ENetPeer *peer, enet_uint32 data) { if ((peer->state == ENET_PEER_STATE_CONNECTED || peer->state == ENET_PEER_STATE_DISCONNECT_LATER) && !(enet_list_empty(&peer->outgoingReliableCommands) && enet_list_empty(&peer->outgoingUnreliableCommands) && enet_list_empty(&peer->sentReliableCommands)) ) { peer->state = ENET_PEER_STATE_DISCONNECT_LATER; peer->eventData = data; } else { enet_peer_disconnect(peer, data); } } ENetAcknowledgement *enet_peer_queue_acknowledgement(ENetPeer *peer, const ENetProtocol *command, enet_uint16 sentTime) { ENetAcknowledgement *acknowledgement; if (command->header.channelID < peer->channelCount) { ENetChannel *channel = &peer->channels[command->header.channelID]; enet_uint16 reliableWindow = command->header.reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; enet_uint16 currentWindow = channel->incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (command->header.reliableSequenceNumber < channel->incomingReliableSequenceNumber) { reliableWindow += ENET_PEER_RELIABLE_WINDOWS; } if (reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1 && reliableWindow <= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS) { return NULL; } } acknowledgement = (ENetAcknowledgement *) enet_malloc(sizeof(ENetAcknowledgement)); if (acknowledgement == NULL) { return NULL; } peer->outgoingDataTotal += sizeof(ENetProtocolAcknowledge); acknowledgement->sentTime = sentTime; acknowledgement->command = *command; enet_list_insert(enet_list_end(&peer->acknowledgements), acknowledgement); return acknowledgement; } void enet_peer_setup_outgoing_command(ENetPeer *peer, ENetOutgoingCommand *outgoingCommand) { ENetChannel *channel = &peer->channels[outgoingCommand->command.header.channelID]; peer->outgoingDataTotal += enet_protocol_command_size(outgoingCommand->command.header.command) + outgoingCommand->fragmentLength; if (outgoingCommand->command.header.channelID == 0xFF) { ++peer->outgoingReliableSequenceNumber; outgoingCommand->reliableSequenceNumber = peer->outgoingReliableSequenceNumber; outgoingCommand->unreliableSequenceNumber = 0; } else if (outgoingCommand->command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) { ++channel->outgoingReliableSequenceNumber; channel->outgoingUnreliableSequenceNumber = 0; outgoingCommand->reliableSequenceNumber = channel->outgoingReliableSequenceNumber; outgoingCommand->unreliableSequenceNumber = 0; } else if (outgoingCommand->command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED) { ++peer->outgoingUnsequencedGroup; outgoingCommand->reliableSequenceNumber = 0; outgoingCommand->unreliableSequenceNumber = 0; } else { if (outgoingCommand->fragmentOffset == 0) { ++channel->outgoingUnreliableSequenceNumber; } outgoingCommand->reliableSequenceNumber = channel->outgoingReliableSequenceNumber; outgoingCommand->unreliableSequenceNumber = channel->outgoingUnreliableSequenceNumber; } outgoingCommand->sendAttempts = 0; outgoingCommand->sentTime = 0; outgoingCommand->roundTripTimeout = 0; outgoingCommand->roundTripTimeoutLimit = 0; outgoingCommand->command.header.reliableSequenceNumber = ENET_HOST_TO_NET_16(outgoingCommand->reliableSequenceNumber); switch (outgoingCommand->command.header.command & ENET_PROTOCOL_COMMAND_MASK) { case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: outgoingCommand->command.sendUnreliable.unreliableSequenceNumber = ENET_HOST_TO_NET_16(outgoingCommand->unreliableSequenceNumber); break; case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: outgoingCommand->command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16(peer->outgoingUnsequencedGroup); break; default: break; } if (outgoingCommand->command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) { enet_list_insert(enet_list_end(&peer->outgoingReliableCommands), outgoingCommand); } else { enet_list_insert(enet_list_end(&peer->outgoingUnreliableCommands), outgoingCommand); } } ENetOutgoingCommand * enet_peer_queue_outgoing_command(ENetPeer *peer, const ENetProtocol *command, ENetPacket *packet, enet_uint32 offset, enet_uint16 length) { ENetOutgoingCommand *outgoingCommand = (ENetOutgoingCommand *) enet_malloc(sizeof(ENetOutgoingCommand)); if (outgoingCommand == NULL) { return NULL; } outgoingCommand->command = *command; outgoingCommand->fragmentOffset = offset; outgoingCommand->fragmentLength = length; outgoingCommand->packet = packet; if (packet != NULL) { ++packet->referenceCount; } enet_peer_setup_outgoing_command(peer, outgoingCommand); return outgoingCommand; } void enet_peer_dispatch_incoming_unreliable_commands(ENetPeer *peer, ENetChannel *channel) { ENetListIterator droppedCommand, startCommand, currentCommand; for (droppedCommand = startCommand = currentCommand = enet_list_begin(&channel->incomingUnreliableCommands); currentCommand != enet_list_end(&channel->incomingUnreliableCommands); currentCommand = enet_list_next(currentCommand) ) { ENetIncomingCommand *incomingCommand = (ENetIncomingCommand *) currentCommand; if ((incomingCommand->command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) { continue; } if (incomingCommand->reliableSequenceNumber == channel->incomingReliableSequenceNumber) { if (incomingCommand->fragmentsRemaining <= 0) { channel->incomingUnreliableSequenceNumber = incomingCommand->unreliableSequenceNumber; continue; } if (startCommand != currentCommand) { enet_list_move(enet_list_end(&peer->dispatchedCommands), startCommand, enet_list_previous(currentCommand)); if (!peer->needsDispatch) { enet_list_insert(enet_list_end(&peer->host->dispatchQueue), &peer->dispatchList); peer->needsDispatch = 1; } droppedCommand = currentCommand; } else if (droppedCommand != currentCommand) { droppedCommand = enet_list_previous(currentCommand); } } else { enet_uint16 reliableWindow = incomingCommand->reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; enet_uint16 currentWindow = channel->incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (incomingCommand->reliableSequenceNumber < channel->incomingReliableSequenceNumber) { reliableWindow += ENET_PEER_RELIABLE_WINDOWS; } if (reliableWindow >= currentWindow && reliableWindow < currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) { break; } droppedCommand = enet_list_next(currentCommand); if (startCommand != currentCommand) { enet_list_move(enet_list_end(&peer->dispatchedCommands), startCommand, enet_list_previous(currentCommand)); if (!peer->needsDispatch) { enet_list_insert(enet_list_end(&peer->host->dispatchQueue), &peer->dispatchList); peer->needsDispatch = 1; } } } startCommand = enet_list_next(currentCommand); } if (startCommand != currentCommand) { enet_list_move(enet_list_end(&peer->dispatchedCommands), startCommand, enet_list_previous(currentCommand)); if (!peer->needsDispatch) { enet_list_insert(enet_list_end(&peer->host->dispatchQueue), &peer->dispatchList); peer->needsDispatch = 1; } droppedCommand = currentCommand; } enet_peer_remove_incoming_commands(&channel->incomingUnreliableCommands,enet_list_begin(&channel->incomingUnreliableCommands), droppedCommand); } void enet_peer_dispatch_incoming_reliable_commands(ENetPeer *peer, ENetChannel *channel) { ENetListIterator currentCommand; for (currentCommand = enet_list_begin(&channel->incomingReliableCommands); currentCommand != enet_list_end(&channel->incomingReliableCommands); currentCommand = enet_list_next(currentCommand) ) { ENetIncomingCommand *incomingCommand = (ENetIncomingCommand *) currentCommand; if (incomingCommand->fragmentsRemaining > 0 || incomingCommand->reliableSequenceNumber != (enet_uint16) (channel->incomingReliableSequenceNumber + 1)) { break; } channel->incomingReliableSequenceNumber = incomingCommand->reliableSequenceNumber; if (incomingCommand->fragmentCount > 0) { channel->incomingReliableSequenceNumber += incomingCommand->fragmentCount - 1; } } if (currentCommand == enet_list_begin(&channel->incomingReliableCommands)) { return; } channel->incomingUnreliableSequenceNumber = 0; enet_list_move(enet_list_end(&peer->dispatchedCommands), enet_list_begin(&channel->incomingReliableCommands), enet_list_previous(currentCommand)); if (!peer->needsDispatch) { enet_list_insert(enet_list_end(&peer->host->dispatchQueue), &peer->dispatchList); peer->needsDispatch = 1; } if (!enet_list_empty(&channel->incomingUnreliableCommands)) { enet_peer_dispatch_incoming_unreliable_commands(peer, channel); } } ENetIncomingCommand * enet_peer_queue_incoming_command(ENetPeer *peer, const ENetProtocol *command, const void *data, size_t dataLength, enet_uint32 flags, enet_uint32 fragmentCount) { static ENetIncomingCommand dummyCommand; ENetChannel *channel = &peer->channels[command->header.channelID]; enet_uint32 unreliableSequenceNumber = 0, reliableSequenceNumber = 0; enet_uint16 reliableWindow, currentWindow; ENetIncomingCommand *incomingCommand; ENetListIterator currentCommand; ENetPacket *packet = NULL; if (peer->state == ENET_PEER_STATE_DISCONNECT_LATER) { goto discardCommand; } if ((command->header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) { reliableSequenceNumber = command->header.reliableSequenceNumber; reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; currentWindow = channel->incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (reliableSequenceNumber < channel->incomingReliableSequenceNumber) { reliableWindow += ENET_PEER_RELIABLE_WINDOWS; } if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) { goto discardCommand; } } switch (command->header.command & ENET_PROTOCOL_COMMAND_MASK) { case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: if (reliableSequenceNumber == channel->incomingReliableSequenceNumber) { goto discardCommand; } for (currentCommand = enet_list_previous(enet_list_end(&channel->incomingReliableCommands)); currentCommand != enet_list_end(&channel->incomingReliableCommands); currentCommand = enet_list_previous(currentCommand) ) { incomingCommand = (ENetIncomingCommand *) currentCommand; if (reliableSequenceNumber >= channel->incomingReliableSequenceNumber) { if (incomingCommand->reliableSequenceNumber < channel->incomingReliableSequenceNumber) { continue; } } else if (incomingCommand->reliableSequenceNumber >= channel->incomingReliableSequenceNumber) { break; } if (incomingCommand->reliableSequenceNumber <= reliableSequenceNumber) { if (incomingCommand->reliableSequenceNumber < reliableSequenceNumber) { break; } goto discardCommand; } } break; case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT: unreliableSequenceNumber = ENET_NET_TO_HOST_16(command->sendUnreliable.unreliableSequenceNumber); if (reliableSequenceNumber == channel->incomingReliableSequenceNumber && unreliableSequenceNumber <= channel->incomingUnreliableSequenceNumber) { goto discardCommand; } for (currentCommand = enet_list_previous(enet_list_end(&channel->incomingUnreliableCommands)); currentCommand != enet_list_end(&channel->incomingUnreliableCommands); currentCommand = enet_list_previous(currentCommand) ) { incomingCommand = (ENetIncomingCommand *) currentCommand; if ((command->header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) { continue; } if (reliableSequenceNumber >= channel->incomingReliableSequenceNumber) { if (incomingCommand->reliableSequenceNumber < channel->incomingReliableSequenceNumber) { continue; } } else if (incomingCommand->reliableSequenceNumber >= channel->incomingReliableSequenceNumber) { break; } if (incomingCommand->reliableSequenceNumber < reliableSequenceNumber) { break; } if (incomingCommand->reliableSequenceNumber > reliableSequenceNumber) { continue; } if (incomingCommand->unreliableSequenceNumber <= unreliableSequenceNumber) { if (incomingCommand->unreliableSequenceNumber < unreliableSequenceNumber) { break; } goto discardCommand; } } break; case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: currentCommand = enet_list_end(&channel->incomingUnreliableCommands); break; default: goto discardCommand; } if (peer->totalWaitingData >= peer->host->maximumWaitingData) { goto notifyError; } packet = callbacks.packet_create(data, dataLength, flags); if (packet == NULL) { goto notifyError; } incomingCommand = (ENetIncomingCommand *) enet_malloc(sizeof(ENetIncomingCommand)); if (incomingCommand == NULL) { goto notifyError; } incomingCommand->reliableSequenceNumber = command->header.reliableSequenceNumber; incomingCommand->unreliableSequenceNumber = unreliableSequenceNumber & 0xFFFF; incomingCommand->command = *command; incomingCommand->fragmentCount = fragmentCount; incomingCommand->fragmentsRemaining = fragmentCount; incomingCommand->packet = packet; incomingCommand->fragments = NULL; if (fragmentCount > 0) { if (fragmentCount <= ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT) { incomingCommand->fragments = (enet_uint32 *) enet_malloc((fragmentCount + 31) / 32 * sizeof(enet_uint32)); } if (incomingCommand->fragments == NULL) { enet_free(incomingCommand); goto notifyError; } memset(incomingCommand->fragments, 0, (fragmentCount + 31) / 32 * sizeof(enet_uint32)); } assert(packet != NULL); ++packet->referenceCount; peer->totalWaitingData += packet->dataLength; enet_list_insert(enet_list_next(currentCommand), incomingCommand); switch (command->header.command & ENET_PROTOCOL_COMMAND_MASK) { case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: enet_peer_dispatch_incoming_reliable_commands(peer, channel); break; default: enet_peer_dispatch_incoming_unreliable_commands(peer, channel); break; } return incomingCommand; discardCommand: if (fragmentCount > 0) { goto notifyError; } if (packet != NULL && packet->referenceCount == 0) { callbacks.packet_destroy(packet); } return &dummyCommand; notifyError: if (packet != NULL && packet->referenceCount == 0) { callbacks.packet_destroy(packet); } return NULL; } /* enet_peer_queue_incoming_command */ // =======================================================================// // ! // ! Host // ! // =======================================================================// /** Creates a host for communicating to peers. * * @param address the address at which other peers may connect to this host. If NULL, then no peers may connect to the host. * @param peerCount the maximum number of peers that should be allocated for the host. * @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT * @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. * @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. * * @returns the host on success and NULL on failure * * @remarks ENet will strategically drop packets on specific sides of a connection between hosts * to ensure the host's bandwidth is not overwhelmed. The bandwidth parameters also determine * the window size of a connection which limits the amount of reliable packets that may be in transit * at any given time. */ ENetHost * enet_host_create(const ENetAddress *address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) { ENetHost *host; ENetPeer *currentPeer; if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID) { return NULL; } host = (ENetHost *) enet_malloc(sizeof(ENetHost)); if (host == NULL) { return NULL; } memset(host, 0, sizeof(ENetHost)); host->peers = (ENetPeer *) enet_malloc(peerCount * sizeof(ENetPeer)); if (host->peers == NULL) { enet_free(host); return NULL; } memset(host->peers, 0, peerCount * sizeof(ENetPeer)); host->socket = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM); if (host->socket != ENET_SOCKET_NULL) { enet_socket_set_option (host->socket, ENET_SOCKOPT_IPV6_V6ONLY, 0); } if (host->socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind(host->socket, address) < 0)) { if (host->socket != ENET_SOCKET_NULL) { enet_socket_destroy(host->socket); } enet_free(host->peers); enet_free(host); return NULL; } enet_socket_set_option(host->socket, ENET_SOCKOPT_NONBLOCK, 1); enet_socket_set_option(host->socket, ENET_SOCKOPT_BROADCAST, 1); enet_socket_set_option(host->socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE); enet_socket_set_option(host->socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE); enet_socket_set_option(host->socket, ENET_SOCKOPT_IPV6_V6ONLY, 0); if (address != NULL && enet_socket_get_address(host->socket, &host->address) < 0) { host->address = *address; } if (!channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) { channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; } host->randomSeed = (enet_uint32) ((uintptr_t) host % UINT32_MAX); host->randomSeed += enet_host_random_seed(); host->randomSeed = (host->randomSeed << 16) | (host->randomSeed >> 16); host->channelLimit = channelLimit; host->incomingBandwidth = incomingBandwidth; host->outgoingBandwidth = outgoingBandwidth; host->bandwidthThrottleEpoch = 0; host->recalculateBandwidthLimits = 0; host->mtu = ENET_HOST_DEFAULT_MTU; host->peerCount = peerCount; host->commandCount = 0; host->bufferCount = 0; host->checksum = NULL; host->receivedAddress.host = ENET_HOST_ANY; host->receivedAddress.port = 0; host->receivedData = NULL; host->receivedDataLength = 0; host->totalSentData = 0; host->totalSentPackets = 0; host->totalReceivedData = 0; host->totalReceivedPackets = 0; host->connectedPeers = 0; host->bandwidthLimitedPeers = 0; host->duplicatePeers = ENET_PROTOCOL_MAXIMUM_PEER_ID; host->maximumPacketSize = ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE; host->maximumWaitingData = ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA; host->compressor.context = NULL; host->compressor.compress = NULL; host->compressor.decompress = NULL; host->compressor.destroy = NULL; host->intercept = NULL; enet_list_clear(&host->dispatchQueue); for (currentPeer = host->peers; currentPeer < &host->peers[host->peerCount]; ++currentPeer) { currentPeer->host = host; currentPeer->incomingPeerID = currentPeer - host->peers; currentPeer->outgoingSessionID = currentPeer->incomingSessionID = 0xFF; currentPeer->data = NULL; enet_list_clear(¤tPeer->acknowledgements); enet_list_clear(¤tPeer->sentReliableCommands); enet_list_clear(¤tPeer->sentUnreliableCommands); enet_list_clear(¤tPeer->outgoingReliableCommands); enet_list_clear(¤tPeer->outgoingUnreliableCommands); enet_list_clear(¤tPeer->dispatchedCommands); enet_peer_reset(currentPeer); } return host; } /* enet_host_create */ /** Destroys the host and all resources associated with it. * @param host pointer to the host to destroy */ void enet_host_destroy(ENetHost *host) { ENetPeer *currentPeer; if (host == NULL) { return; } enet_socket_destroy(host->socket); for (currentPeer = host->peers; currentPeer < &host->peers[host->peerCount]; ++currentPeer) { enet_peer_reset(currentPeer); } if (host->compressor.context != NULL && host->compressor.destroy) { (*host->compressor.destroy)(host->compressor.context); } enet_free(host->peers); enet_free(host); } /** Initiates a connection to a foreign host. * @param host host seeking the connection * @param address destination for the connection * @param channelCount number of channels to allocate * @param data user data supplied to the receiving host * @returns a peer representing the foreign host on success, NULL on failure * @remarks The peer returned will have not completed the connection until enet_host_service() * notifies of an ENET_EVENT_TYPE_CONNECT event for the peer. */ ENetPeer * enet_host_connect(ENetHost *host, const ENetAddress *address, size_t channelCount, enet_uint32 data) { ENetPeer *currentPeer; ENetChannel *channel; ENetProtocol command; if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) { channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; } else if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) { channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; } for (currentPeer = host->peers; currentPeer < &host->peers[host->peerCount]; ++currentPeer) { if (currentPeer->state == ENET_PEER_STATE_DISCONNECTED) { break; } } if (currentPeer >= &host->peers[host->peerCount]) { return NULL; } currentPeer->channels = (ENetChannel *) enet_malloc(channelCount * sizeof(ENetChannel)); if (currentPeer->channels == NULL) { return NULL; } currentPeer->channelCount = channelCount; currentPeer->state = ENET_PEER_STATE_CONNECTING; currentPeer->address = *address; currentPeer->connectID = ++host->randomSeed; if (host->outgoingBandwidth == 0) { currentPeer->windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } else { currentPeer->windowSize = (host->outgoingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } if (currentPeer->windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) { currentPeer->windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } else if (currentPeer->windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) { currentPeer->windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } for (channel = currentPeer->channels; channel < ¤tPeer->channels[channelCount]; ++channel) { channel->outgoingReliableSequenceNumber = 0; channel->outgoingUnreliableSequenceNumber = 0; channel->incomingReliableSequenceNumber = 0; channel->incomingUnreliableSequenceNumber = 0; enet_list_clear(&channel->incomingReliableCommands); enet_list_clear(&channel->incomingUnreliableCommands); channel->usedReliableWindows = 0; memset(channel->reliableWindows, 0, sizeof(channel->reliableWindows)); } command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; command.header.channelID = 0xFF; command.connect.outgoingPeerID = ENET_HOST_TO_NET_16(currentPeer->incomingPeerID); command.connect.incomingSessionID = currentPeer->incomingSessionID; command.connect.outgoingSessionID = currentPeer->outgoingSessionID; command.connect.mtu = ENET_HOST_TO_NET_32(currentPeer->mtu); command.connect.windowSize = ENET_HOST_TO_NET_32(currentPeer->windowSize); command.connect.channelCount = ENET_HOST_TO_NET_32(channelCount); command.connect.incomingBandwidth = ENET_HOST_TO_NET_32(host->incomingBandwidth); command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32(host->outgoingBandwidth); command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32(currentPeer->packetThrottleInterval); command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32(currentPeer->packetThrottleAcceleration); command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32(currentPeer->packetThrottleDeceleration); command.connect.connectID = currentPeer->connectID; command.connect.data = ENET_HOST_TO_NET_32(data); enet_peer_queue_outgoing_command(currentPeer, &command, NULL, 0, 0); return currentPeer; } /* enet_host_connect */ /** 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) { callbacks.packet_destroy(packet); } } /** Sends raw data to specified address. Useful when you want to send unconnected data using host's socket. * @param host host sending data * @param address destination address * @param data data pointer * @param dataLength length of data to send * @retval >=0 bytes sent * @retval <0 error * @sa enet_socket_send */ int enet_host_send_raw(ENetHost *host, const ENetAddress* address, enet_uint8* data, size_t dataLength) { ENetBuffer buffer; buffer.data = data; buffer.dataLength = dataLength; return enet_socket_send(host->socket, address, &buffer, 1); } /** Sends raw data to specified address with extended arguments. Allows to send only part of data, handy for other programming languages. * I.e. if you have data =- { 0, 1, 2, 3 } and call function as enet_host_send_raw_ex(data, 1, 2) then it will skip 1 byte and send 2 bytes { 1, 2 }. * @param host host sending data * @param address destination address * @param data data pointer * @param skipBytes number of bytes to skip from start of data * @param bytesToSend number of bytes to send * @retval >=0 bytes sent * @retval <0 error * @sa enet_socket_send */ int enet_host_send_raw_ex(ENetHost *host, const ENetAddress* address, enet_uint8* data, size_t skipBytes, size_t bytesToSend) { ENetBuffer buffer; buffer.data = data + skipBytes; buffer.dataLength = bytesToSend; return enet_socket_send(host->socket, address, &buffer, 1); } /** Sets intercept callback for the host. * @param host host to set a callback * @param callback intercept callback */ void enet_host_set_intercept(ENetHost *host, const ENetInterceptCallback callback) { host->intercept = callback; } /** Sets the packet compressor the host should use to compress and decompress packets. * @param host host to enable or disable compression for * @param compressor callbacks for for the packet compressor; if NULL, then compression is disabled */ void enet_host_compress(ENetHost *host, const ENetCompressor *compressor) { if (host->compressor.context != NULL && host->compressor.destroy) { (*host->compressor.destroy)(host->compressor.context); } if (compressor) { host->compressor = *compressor; } else { host->compressor.context = NULL; } } /** Limits the maximum allowed channels of future incoming connections. * @param host host to limit * @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT */ void enet_host_channel_limit(ENetHost *host, size_t channelLimit) { if (!channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) { channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; } host->channelLimit = channelLimit; } /** Adjusts the bandwidth limits of a host. * @param host host to adjust * @param incomingBandwidth new incoming bandwidth * @param outgoingBandwidth new outgoing bandwidth * @remarks the incoming and outgoing bandwidth parameters are identical in function to those * specified in enet_host_create(). */ void enet_host_bandwidth_limit(ENetHost *host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) { host->incomingBandwidth = incomingBandwidth; host->outgoingBandwidth = outgoingBandwidth; host->recalculateBandwidthLimits = 1; } void enet_host_bandwidth_throttle(ENetHost *host) { enet_uint32 timeCurrent = enet_time_get(); enet_uint32 elapsedTime = timeCurrent - host->bandwidthThrottleEpoch; enet_uint32 peersRemaining = (enet_uint32) host->connectedPeers; enet_uint32 dataTotal = ~0; enet_uint32 bandwidth = ~0; enet_uint32 throttle = 0; enet_uint32 bandwidthLimit = 0; int needsAdjustment = host->bandwidthLimitedPeers > 0 ? 1 : 0; ENetPeer *peer; ENetProtocol command; if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL) { return; } if (host->outgoingBandwidth == 0 && host->incomingBandwidth == 0) { return; } host->bandwidthThrottleEpoch = timeCurrent; if (peersRemaining == 0) { return; } if (host->outgoingBandwidth != 0) { dataTotal = 0; bandwidth = (host->outgoingBandwidth * elapsedTime) / 1000; for (peer = host->peers; peer < &host->peers[host->peerCount]; ++peer) { if (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) { continue; } dataTotal += peer->outgoingDataTotal; } } while (peersRemaining > 0 && needsAdjustment != 0) { needsAdjustment = 0; if (dataTotal <= bandwidth) { throttle = ENET_PEER_PACKET_THROTTLE_SCALE; } else { throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal; } for (peer = host->peers; peer < &host->peers[host->peerCount]; ++peer) { enet_uint32 peerBandwidth; if ((peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) || peer->incomingBandwidth == 0 || peer->outgoingBandwidthThrottleEpoch == timeCurrent ) { continue; } peerBandwidth = (peer->incomingBandwidth * elapsedTime) / 1000; if ((throttle * peer->outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth) { continue; } peer->packetThrottleLimit = (peerBandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / peer->outgoingDataTotal; if (peer->packetThrottleLimit == 0) { peer->packetThrottleLimit = 1; } if (peer->packetThrottle > peer->packetThrottleLimit) { peer->packetThrottle = peer->packetThrottleLimit; } peer->outgoingBandwidthThrottleEpoch = timeCurrent; peer->incomingDataTotal = 0; peer->outgoingDataTotal = 0; needsAdjustment = 1; --peersRemaining; bandwidth -= peerBandwidth; dataTotal -= peerBandwidth; } } if (peersRemaining > 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) { if ((peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) || peer->outgoingBandwidthThrottleEpoch == timeCurrent) { continue; } peer->packetThrottleLimit = throttle; if (peer->packetThrottle > peer->packetThrottleLimit) { peer->packetThrottle = peer->packetThrottleLimit; } peer->incomingDataTotal = 0; peer->outgoingDataTotal = 0; } } if (host->recalculateBandwidthLimits) { host->recalculateBandwidthLimits = 0; peersRemaining = (enet_uint32) host->connectedPeers; bandwidth = host->incomingBandwidth; needsAdjustment = 1; if (bandwidth == 0) { bandwidthLimit = 0; } else { while (peersRemaining > 0 && needsAdjustment != 0) { needsAdjustment = 0; bandwidthLimit = bandwidth / peersRemaining; for (peer = host->peers; peer < &host->peers[host->peerCount]; ++peer) { if ((peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) || peer->incomingBandwidthThrottleEpoch == timeCurrent ) { continue; } if (peer->outgoingBandwidth > 0 && peer->outgoingBandwidth >= bandwidthLimit) { continue; } peer->incomingBandwidthThrottleEpoch = timeCurrent; needsAdjustment = 1; --peersRemaining; bandwidth -= peer->outgoingBandwidth; } } } for (peer = host->peers; peer < &host->peers[host->peerCount]; ++peer) { if (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) { continue; } command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; command.header.channelID = 0xFF; command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32(host->outgoingBandwidth); if (peer->incomingBandwidthThrottleEpoch == timeCurrent) { command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32(peer->outgoingBandwidth); } else { command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32(bandwidthLimit); } enet_peer_queue_outgoing_command(peer, &command, NULL, 0, 0); } } } /* enet_host_bandwidth_throttle */ // =======================================================================// // ! // ! Time // ! // =======================================================================// #ifdef _WIN32 static LARGE_INTEGER getFILETIMEoffset() { SYSTEMTIME s; FILETIME f; LARGE_INTEGER t; s.wYear = 1970; s.wMonth = 1; s.wDay = 1; s.wHour = 0; s.wMinute = 0; s.wSecond = 0; s.wMilliseconds = 0; SystemTimeToFileTime(&s, &f); t.QuadPart = f.dwHighDateTime; t.QuadPart <<= 32; t.QuadPart |= f.dwLowDateTime; return (t); } int clock_gettime(int X, struct timespec *tv) { (void)X; LARGE_INTEGER t; FILETIME f; double microseconds; static LARGE_INTEGER offset; static double frequencyToMicroseconds; static int initialized = 0; static BOOL usePerformanceCounter = 0; if (!initialized) { LARGE_INTEGER performanceFrequency; initialized = 1; usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency); if (usePerformanceCounter) { QueryPerformanceCounter(&offset); frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.; } else { offset = getFILETIMEoffset(); frequencyToMicroseconds = 10.; } } if (usePerformanceCounter) { QueryPerformanceCounter(&t); } else { GetSystemTimeAsFileTime(&f); t.QuadPart = f.dwHighDateTime; t.QuadPart <<= 32; t.QuadPart |= f.dwLowDateTime; } t.QuadPart -= offset.QuadPart; microseconds = (double)t.QuadPart / frequencyToMicroseconds; t.QuadPart = (LONGLONG)microseconds; tv->tv_sec = (long)(t.QuadPart / 1000000); tv->tv_nsec = t.QuadPart % 1000000 * 1000; return (0); } #elif __APPLE__ && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 #define CLOCK_MONOTONIC 0 int clock_gettime(int X, struct timespec *ts) { clock_serv_t cclock; mach_timespec_t mts; host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); ts->tv_sec = mts.tv_sec; ts->tv_nsec = mts.tv_nsec; return 0; } #endif enet_uint32 enet_time_get() { // TODO enet uses 32 bit timestamps. We should modify it to use // 64 bit timestamps, but this is not trivial since we'd end up // changing half the structs in enet. For now, retain 32 bits, but // use an offset so we don't run out of bits. Basically, the first // call of enet_time_get() will always return 1, and follow-up calls // indicate elapsed time since the first call. // // Note that we don't want to return 0 from the first call, in case // some part of enet uses 0 as a special value (meaning time not set // for example). static uint64_t start_time_ns = 0; struct timespec ts; #if defined(CLOCK_MONOTONIC_RAW) clock_gettime(CLOCK_MONOTONIC_RAW, &ts); #else clock_gettime(CLOCK_MONOTONIC, &ts); #endif static const uint64_t ns_in_s = 1000 * 1000 * 1000; static const uint64_t ns_in_ms = 1000 * 1000; uint64_t current_time_ns = ts.tv_nsec + (uint64_t)ts.tv_sec * ns_in_s; // Most of the time we just want to atomically read the start time. We // could just use a single CAS instruction instead of this if, but it // would be slower in the average case. // // Note that statics are auto-initialized to zero, and starting a thread // implies a memory barrier. So we know that whatever thread calls this, // it correctly sees the start_time_ns as 0 initially. uint64_t offset_ns = ENET_ATOMIC_READ(&start_time_ns); if (offset_ns == 0) { // We still need to CAS, since two different threads can get here // at the same time. // // We assume that current_time_ns is > 1ms. // // Set the value of the start_time_ns, such that the first timestamp // is at 1ms. This ensures 0 remains a special value. uint64_t want_value = current_time_ns - 1 * ns_in_ms; uint64_t old_value = ENET_ATOMIC_CAS(&start_time_ns, 0, want_value); offset_ns = old_value == 0 ? want_value : old_value; } uint64_t result_in_ns = current_time_ns - offset_ns; return (enet_uint32)(result_in_ns / ns_in_ms); } void enet_inaddr_map4to6(struct in_addr in, struct in6_addr *out) { if (in.s_addr == 0x00000000) { /* 0.0.0.0 */ *out = enet_v6_anyaddr; } else if (in.s_addr == 0xFFFFFFFF) { /* 255.255.255.255 */ *out = enet_v6_noaddr; } else { *out = enet_v4_anyaddr; out->s6_addr[10] = 0xFF; out->s6_addr[11] = 0xFF; out->s6_addr[12] = ((uint8_t *)&in.s_addr)[0]; out->s6_addr[13] = ((uint8_t *)&in.s_addr)[1]; out->s6_addr[14] = ((uint8_t *)&in.s_addr)[2]; out->s6_addr[15] = ((uint8_t *)&in.s_addr)[3]; } } void enet_inaddr_map6to4(const struct in6_addr *in, struct in_addr *out) { memset(out, 0, sizeof(struct in_addr)); ((uint8_t *)&out->s_addr)[0] = in->s6_addr[12]; ((uint8_t *)&out->s_addr)[1] = in->s6_addr[13]; ((uint8_t *)&out->s_addr)[2] = in->s6_addr[14]; ((uint8_t *)&out->s_addr)[3] = in->s6_addr[15]; } int enet_in6addr_lookup_host(const char *name, bool nodns, ENetAddress *out) { struct addrinfo hints, *resultList = NULL, *result = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; if (nodns) { hints.ai_flags = AI_NUMERICHOST; /* prevent actual DNS lookups! */ } if (getaddrinfo(name, NULL, &hints, &resultList) != 0) { freeaddrinfo(resultList); return -1; } for (result = resultList; result != NULL; result = result->ai_next) { if (result->ai_addr != NULL) { if (result->ai_family == AF_INET || (result->ai_family == AF_UNSPEC && result->ai_addrlen == sizeof(struct sockaddr_in))) { enet_inaddr_map4to6(((struct sockaddr_in*)result->ai_addr)->sin_addr, &out->host); out->sin6_scope_id = 0; freeaddrinfo(resultList); return 0; } else if (result->ai_family == AF_INET6 || (result->ai_family == AF_UNSPEC && result->ai_addrlen == sizeof(struct sockaddr_in6))) { memcpy(&out->host, &((struct sockaddr_in6*)result->ai_addr)->sin6_addr, sizeof(struct in6_addr)); out->sin6_scope_id = (enet_uint16) ((struct sockaddr_in6*)result->ai_addr)->sin6_scope_id; freeaddrinfo(resultList); return 0; } } } freeaddrinfo(resultList); return -1; } int enet_address_set_host_ip_new(ENetAddress *address, const char *name) { return enet_in6addr_lookup_host(name, true, address); } int enet_address_set_host_new(ENetAddress *address, const char *name) { return enet_in6addr_lookup_host(name, false, address); } int enet_address_get_host_ip_new(const ENetAddress *address, char *name, size_t nameLength) { if (IN6_IS_ADDR_V4MAPPED(&address->host)) { struct in_addr buf; enet_inaddr_map6to4(&address->host, &buf); if (inet_ntop(AF_INET, &buf, name, nameLength) == NULL) { return -1; } } else { if (inet_ntop(AF_INET6, (void*)&address->host, name, nameLength) == NULL) { return -1; } } return 0; } /* enet_address_get_host_ip_new */ int enet_address_get_host_new(const ENetAddress *address, char *name, size_t nameLength) { struct sockaddr_in6 sin; memset(&sin, 0, sizeof(struct sockaddr_in6)); int err; sin.sin6_family = AF_INET6; sin.sin6_port = ENET_HOST_TO_NET_16 (address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; err = getnameinfo((struct sockaddr *) &sin, sizeof(sin), name, nameLength, NULL, 0, NI_NAMEREQD); if (!err) { if (name != NULL && nameLength > 0 && !memchr(name, '\0', nameLength)) { return -1; } return 0; } if (err != EAI_NONAME) { return -1; } return enet_address_get_host_ip_new(address, name, nameLength); } /* enet_address_get_host_new */ // =======================================================================// // ! // ! Platform Specific (Unix) // ! // =======================================================================// #ifndef _WIN32 #if defined(__MINGW32__) && defined(ENET_MINGW_COMPAT) // inet_ntop/inet_pton for MinGW from http://mingw-users.1079350.n2.nabble.com/IPv6-getaddrinfo-amp-inet-ntop-td5891996.html const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) { if (af == AF_INET) { struct sockaddr_in in; memset(&in, 0, sizeof(in)); in.sin_family = AF_INET; memcpy(&in.sin_addr, src, sizeof(struct in_addr)); getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } else if (af == AF_INET6) { struct sockaddr_in6 in; memset(&in, 0, sizeof(in)); in.sin6_family = AF_INET6; memcpy(&in.sin6_addr, src, sizeof(struct in_addr6)); getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } return NULL; } #define NS_INADDRSZ 4 #define NS_IN6ADDRSZ 16 #define NS_INT16SZ 2 int inet_pton4(const char *src, char *dst) { uint8_t tmp[NS_INADDRSZ], *tp; int saw_digit = 0; int octets = 0; *(tp = tmp) = 0; int ch; while ((ch = *src++) != '\0') { if (ch >= '0' && ch <= '9') { uint32_t n = *tp * 10 + (ch - '0'); if (saw_digit && *tp == 0) return 0; if (n > 255) return 0; *tp = n; if (!saw_digit) { if (++octets > 4) return 0; saw_digit = 1; } } else if (ch == '.' && saw_digit) { if (octets == 4) return 0; *++tp = 0; saw_digit = 0; } else return 0; } if (octets < 4) return 0; memcpy(dst, tmp, NS_INADDRSZ); return 1; } int inet_pton6(const char *src, char *dst) { static const char xdigits[] = "0123456789abcdef"; uint8_t tmp[NS_IN6ADDRSZ]; uint8_t *tp = (uint8_t*) memset(tmp, '\0', NS_IN6ADDRSZ); uint8_t *endp = tp + NS_IN6ADDRSZ; uint8_t *colonp = NULL; /* Leading :: requires some special handling. */ if (*src == ':') { if (*++src != ':') return 0; } const char *curtok = src; int saw_xdigit = 0; uint32_t val = 0; int ch; while ((ch = tolower(*src++)) != '\0') { const char *pch = strchr(xdigits, ch); if (pch != NULL) { val <<= 4; val |= (pch - xdigits); if (val > 0xffff) return 0; saw_xdigit = 1; continue; } if (ch == ':') { curtok = src; if (!saw_xdigit) { if (colonp) return 0; colonp = tp; continue; } else if (*src == '\0') { return 0; } if (tp + NS_INT16SZ > endp) return 0; *tp++ = (uint8_t) (val >> 8) & 0xff; *tp++ = (uint8_t) val & 0xff; saw_xdigit = 0; val = 0; continue; } if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && inet_pton4(curtok, (char*) tp) > 0) { tp += NS_INADDRSZ; saw_xdigit = 0; break; /* '\0' was seen by inet_pton4(). */ } return 0; } if (saw_xdigit) { if (tp + NS_INT16SZ > endp) return 0; *tp++ = (uint8_t) (val >> 8) & 0xff; *tp++ = (uint8_t) val & 0xff; } if (colonp != NULL) { /* * Since some memmove()'s erroneously fail to handle * overlapping regions, we'll do the shift by hand. */ const int n = tp - colonp; if (tp == endp) return 0; for (int i = 1; i <= n; i++) { endp[-i] = colonp[n - i]; colonp[n - i] = 0; } tp = endp; } if (tp != endp) return 0; memcpy(dst, tmp, NS_IN6ADDRSZ); return 1; } int inet_pton(int af, const char *src, struct in6_addr *dst) { switch (af) { case AF_INET: return inet_pton4(src, (char *)dst); case AF_INET6: return inet_pton6(src, (char *)dst); default: return -1; } } #endif // __MINGW__ int enet_initialize(void) { return 0; } void enet_deinitialize(void) {} enet_uint64 enet_host_random_seed(void) { return (enet_uint64) time(NULL); } int enet_address_set_host_ip_old(ENetAddress *address, const char *name) { if (!inet_pton(AF_INET6, name, &address->host)) { return -1; } return 0; } int enet_address_set_host_old(ENetAddress *address, const char *name) { struct addrinfo hints, *resultList = NULL, *result = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; if (getaddrinfo(name, NULL, &hints, &resultList) != 0) { return -1; } for (result = resultList; result != NULL; result = result->ai_next) { if (result->ai_addr != NULL && result->ai_addrlen >= sizeof(struct sockaddr_in)) { if (result->ai_family == AF_INET) { struct sockaddr_in * sin = (struct sockaddr_in *) result->ai_addr; ((uint32_t *)&address->host.s6_addr)[0] = 0; ((uint32_t *)&address->host.s6_addr)[1] = 0; ((uint32_t *)&address->host.s6_addr)[2] = htonl(0xffff); ((uint32_t *)&address->host.s6_addr)[3] = sin->sin_addr.s_addr; freeaddrinfo(resultList); return 0; } else if(result->ai_family == AF_INET6) { struct sockaddr_in6 * sin = (struct sockaddr_in6 *)result->ai_addr; address->host = sin->sin6_addr; address->sin6_scope_id = sin->sin6_scope_id; freeaddrinfo(resultList); return 0; } } } freeaddrinfo(resultList); return enet_address_set_host_ip(address, name); } /* enet_address_set_host_old */ int enet_address_get_host_ip_old(const ENetAddress *address, char *name, size_t nameLength) { if (inet_ntop(AF_INET6, &address->host, name, nameLength) == NULL) { return -1; } return 0; } int enet_address_get_host_old(const ENetAddress *address, char *name, size_t nameLength) { struct sockaddr_in6 sin; int err; memset(&sin, 0, sizeof(struct sockaddr_in6)); sin.sin6_family = AF_INET6; sin.sin6_port = ENET_HOST_TO_NET_16 (address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; err = getnameinfo((struct sockaddr *) &sin, sizeof(sin), name, nameLength, NULL, 0, NI_NAMEREQD); if (!err) { if (name != NULL && nameLength > 0 && !memchr(name, '\0', nameLength)) { return -1; } return 0; } if (err != EAI_NONAME) { return -1; } return enet_address_get_host_ip(address, name, nameLength); } /* enet_address_get_host_old */ int enet_socket_bind(ENetSocket socket, const ENetAddress *address) { struct sockaddr_in6 sin; memset(&sin, 0, sizeof(struct sockaddr_in6)); sin.sin6_family = AF_INET6; if (address != NULL) { sin.sin6_port = ENET_HOST_TO_NET_16(address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; } else { sin.sin6_port = 0; sin.sin6_addr = ENET_HOST_ANY; sin.sin6_scope_id = 0; } return bind(socket, (struct sockaddr *)&sin, sizeof(struct sockaddr_in6)); } int enet_socket_get_address(ENetSocket socket, ENetAddress *address) { struct sockaddr_in6 sin; socklen_t sinLength = sizeof(struct sockaddr_in6); if (getsockname(socket, (struct sockaddr *) &sin, &sinLength) == -1) { return -1; } address->host = sin.sin6_addr; address->port = ENET_NET_TO_HOST_16(sin.sin6_port); address->sin6_scope_id = sin.sin6_scope_id; return 0; } int enet_socket_listen(ENetSocket socket, int backlog) { return listen(socket, backlog < 0 ? SOMAXCONN : backlog); } ENetSocket enet_socket_create(ENetSocketType type) { return socket(PF_INET6, (int)type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); } int enet_socket_set_option(ENetSocket socket, ENetSocketOption option, int value) { int result = -1; switch (option) { case ENET_SOCKOPT_NONBLOCK: result = fcntl(socket, F_SETFL, (value ? O_NONBLOCK : 0) | (fcntl(socket, F_GETFL) & ~O_NONBLOCK)); break; case ENET_SOCKOPT_BROADCAST: result = setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_REUSEADDR: result = setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_RCVBUF: result = setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_SNDBUF: result = setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_RCVTIMEO: { struct timeval timeVal; timeVal.tv_sec = value / 1000; timeVal.tv_usec = (value % 1000) * 1000; result = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeVal, sizeof(struct timeval)); break; } case ENET_SOCKOPT_SNDTIMEO: { struct timeval timeVal; timeVal.tv_sec = value / 1000; timeVal.tv_usec = (value % 1000) * 1000; result = setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeVal, sizeof(struct timeval)); break; } case ENET_SOCKOPT_NODELAY: result = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_IPV6_V6ONLY: result = setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&value, sizeof(int)); break; default: break; } return result == -1 ? -1 : 0; } /* enet_socket_set_option */ int enet_socket_get_option(ENetSocket socket, ENetSocketOption option, int *value) { int result = -1; socklen_t len; switch (option) { case ENET_SOCKOPT_ERROR: len = sizeof(int); result = getsockopt(socket, SOL_SOCKET, SO_ERROR, value, &len); break; default: break; } return result == -1 ? -1 : 0; } int enet_socket_connect(ENetSocket socket, const ENetAddress *address) { struct sockaddr_in6 sin; int result; memset(&sin, 0, sizeof(struct sockaddr_in6)); sin.sin6_family = AF_INET6; sin.sin6_port = ENET_HOST_TO_NET_16(address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; result = connect(socket, (struct sockaddr *)&sin, sizeof(struct sockaddr_in6)); if (result == -1 && errno == EINPROGRESS) { return 0; } return result; } ENetSocket enet_socket_accept(ENetSocket socket, ENetAddress *address) { int result; struct sockaddr_in6 sin; socklen_t sinLength = sizeof(struct sockaddr_in6); 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 = sin.sin6_addr; address->port = ENET_NET_TO_HOST_16 (sin.sin6_port); address->sin6_scope_id = sin.sin6_scope_id; } return result; } int enet_socket_shutdown(ENetSocket socket, ENetSocketShutdown how) { return shutdown(socket, (int) how); } void enet_socket_destroy(ENetSocket socket) { if (socket != -1) { close(socket); } } int enet_socket_send(ENetSocket socket, const ENetAddress *address, const ENetBuffer *buffers, size_t bufferCount) { struct msghdr msgHdr; struct sockaddr_in6 sin; int sentLength; memset(&msgHdr, 0, sizeof(struct msghdr)); if (address != NULL) { memset(&sin, 0, sizeof(struct sockaddr_in6)); sin.sin6_family = AF_INET6; sin.sin6_port = ENET_HOST_TO_NET_16(address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; msgHdr.msg_name = &sin; msgHdr.msg_namelen = sizeof(struct sockaddr_in6); } 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; } /* enet_socket_send */ int enet_socket_receive(ENetSocket socket, ENetAddress *address, ENetBuffer *buffers, size_t bufferCount) { struct msghdr msgHdr; struct sockaddr_in6 sin; int recvLength; memset(&msgHdr, 0, sizeof(struct msghdr)); if (address != NULL) { msgHdr.msg_name = &sin; msgHdr.msg_namelen = sizeof(struct sockaddr_in6); } 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; } if (msgHdr.msg_flags & MSG_TRUNC) { return -1; } if (address != NULL) { address->host = sin.sin6_addr; address->port = ENET_NET_TO_HOST_16(sin.sin6_port); address->sin6_scope_id = sin.sin6_scope_id; } return recvLength; } /* enet_socket_receive */ int enet_socketset_select(ENetSocket maxSocket, ENetSocketSet *readSet, ENetSocketSet *writeSet, enet_uint32 timeout) { struct timeval timeVal; timeVal.tv_sec = timeout / 1000; timeVal.tv_usec = (timeout % 1000) * 1000; return select(maxSocket + 1, readSet, writeSet, NULL, &timeVal); } int enet_socket_wait(ENetSocket socket, enet_uint32 *condition, enet_uint64 timeout) { 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) { if (errno == EINTR && *condition & ENET_SOCKET_WAIT_INTERRUPT) { *condition = ENET_SOCKET_WAIT_INTERRUPT; return 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; } /* enet_socket_wait */ #endif // !_WIN32 // =======================================================================// // ! // ! Platform Specific (Win) // ! // =======================================================================// #ifdef _WIN32 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_uint64 enet_host_random_seed(void) { return (enet_uint64) timeGetTime(); } int enet_address_set_host_ip_old(ENetAddress *address, const char *name) { enet_uint8 vals[4] = { 0, 0, 0, 0 }; int i; for (i = 0; i < 4; ++i) { const char *next = name + 1; if (*name != '0') { long val = strtol(name, (char **) &next, 10); if (val < 0 || val > 255 || next == name || next - name > 3) { return -1; } vals[i] = (enet_uint8) val; } if (*next != (i < 3 ? '.' : '\0')) { return -1; } name = next + 1; } memcpy(&address->host, vals, sizeof(enet_uint32)); return 0; } int enet_address_set_host_old(ENetAddress *address, const char *name) { struct hostent *hostEntry = NULL; hostEntry = gethostbyname(name); if (hostEntry == NULL || hostEntry->h_addrtype != AF_INET) { if (!inet_pton(AF_INET6, name, &address->host)) { return -1; } return 0; } ((enet_uint32 *)&address->host.s6_addr)[0] = 0; ((enet_uint32 *)&address->host.s6_addr)[1] = 0; ((enet_uint32 *)&address->host.s6_addr)[2] = htonl(0xffff); ((enet_uint32 *)&address->host.s6_addr)[3] = *(enet_uint32 *)hostEntry->h_addr_list[0]; return 0; } int enet_address_get_host_ip_old(const ENetAddress *address, char *name, size_t nameLength) { if (inet_ntop(AF_INET6, (PVOID)&address->host, name, nameLength) == NULL) { return -1; } return 0; } int enet_address_get_host_old(const ENetAddress *address, char *name, size_t nameLength) { struct in6_addr in; struct hostent *hostEntry = NULL; in = address->host; hostEntry = gethostbyaddr((char *)&in, sizeof(struct in6_addr), AF_INET6); if (hostEntry == NULL) { return enet_address_get_host_ip(address, name, nameLength); } else { size_t hostLen = strlen(hostEntry->h_name); if (hostLen >= nameLength) { return -1; } memcpy(name, hostEntry->h_name, hostLen + 1); } return 0; } int enet_socket_bind(ENetSocket socket, const ENetAddress *address) { struct sockaddr_in6 sin; memset(&sin, 0, sizeof(struct sockaddr_in6)); sin.sin6_family = AF_INET6; if (address != NULL) { sin.sin6_port = ENET_HOST_TO_NET_16 (address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; } else { sin.sin6_port = 0; sin.sin6_addr = in6addr_any; sin.sin6_scope_id = 0; } return bind(socket, (struct sockaddr *) &sin, sizeof(struct sockaddr_in6)) == SOCKET_ERROR ? -1 : 0; } int enet_socket_get_address(ENetSocket socket, ENetAddress *address) { struct sockaddr_in6 sin; int sinLength = sizeof(struct sockaddr_in6); if (getsockname(socket, (struct sockaddr *) &sin, &sinLength) == -1) { return -1; } address->host = sin.sin6_addr; address->port = ENET_NET_TO_HOST_16(sin.sin6_port); address->sin6_scope_id = sin.sin6_scope_id; return 0; } int enet_socket_listen(ENetSocket socket, int backlog) { return listen(socket, backlog < 0 ? SOMAXCONN : backlog) == SOCKET_ERROR ? -1 : 0; } ENetSocket enet_socket_create(ENetSocketType type) { return socket(PF_INET6, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); } int enet_socket_set_option(ENetSocket socket, ENetSocketOption option, int value) { int result = SOCKET_ERROR; switch (option) { case ENET_SOCKOPT_NONBLOCK: { u_long nonBlocking = (u_long) value; result = ioctlsocket(socket, FIONBIO, &nonBlocking); break; } case ENET_SOCKOPT_BROADCAST: result = setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_REUSEADDR: result = setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_RCVBUF: result = setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_SNDBUF: result = setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_RCVTIMEO: result = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_SNDTIMEO: result = setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_NODELAY: result = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_IPV6_V6ONLY: result = setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&value, sizeof(int)); break; default: break; } return result == SOCKET_ERROR ? -1 : 0; } /* enet_socket_set_option */ int enet_socket_get_option(ENetSocket socket, ENetSocketOption option, int *value) { int result = SOCKET_ERROR, len; switch (option) { case ENET_SOCKOPT_ERROR: len = sizeof(int); result = getsockopt(socket, SOL_SOCKET, SO_ERROR, (char *)value, &len); break; default: break; } return result == SOCKET_ERROR ? -1 : 0; } int enet_socket_connect(ENetSocket socket, const ENetAddress *address) { struct sockaddr_in6 sin; int result; memset(&sin, 0, sizeof(struct sockaddr_in6)); sin.sin6_family = AF_INET6; sin.sin6_port = ENET_HOST_TO_NET_16(address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; result = connect(socket, (struct sockaddr *) &sin, sizeof(struct sockaddr_in6)); if (result == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) { return -1; } return 0; } ENetSocket enet_socket_accept(ENetSocket socket, ENetAddress *address) { SOCKET result; struct sockaddr_in6 sin; int sinLength = sizeof(struct sockaddr_in6); result = accept(socket, address != NULL ? (struct sockaddr *)&sin : NULL, address != NULL ? &sinLength : NULL); if (result == INVALID_SOCKET) { return ENET_SOCKET_NULL; } if (address != NULL) { address->host = sin.sin6_addr; address->port = ENET_NET_TO_HOST_16(sin.sin6_port); address->sin6_scope_id = sin.sin6_scope_id; } return result; } int enet_socket_shutdown(ENetSocket socket, ENetSocketShutdown how) { return shutdown(socket, (int) how) == SOCKET_ERROR ? -1 : 0; } void enet_socket_destroy(ENetSocket socket) { if (socket != INVALID_SOCKET) { closesocket(socket); } } int enet_socket_send(ENetSocket socket, const ENetAddress *address, const ENetBuffer *buffers, size_t bufferCount) { struct sockaddr_in6 sin; DWORD sentLength; if (address != NULL) { memset(&sin, 0, sizeof(struct sockaddr_in6)); sin.sin6_family = AF_INET6; sin.sin6_port = ENET_HOST_TO_NET_16(address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; } if (WSASendTo(socket, (LPWSABUF) buffers, (DWORD) bufferCount, &sentLength, 0, address != NULL ? (struct sockaddr *) &sin : NULL, address != NULL ? sizeof(struct sockaddr_in6) : 0, NULL, NULL) == SOCKET_ERROR ) { return (WSAGetLastError() == WSAEWOULDBLOCK) ? 0 : -1; } return (int) sentLength; } int enet_socket_receive(ENetSocket socket, ENetAddress *address, ENetBuffer *buffers, size_t bufferCount) { INT sinLength = sizeof(struct sockaddr_in6); DWORD flags = 0, recvLength; struct sockaddr_in6 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 = sin.sin6_addr; address->port = ENET_NET_TO_HOST_16(sin.sin6_port); address->sin6_scope_id = sin.sin6_scope_id; } return (int) recvLength; } /* enet_socket_receive */ int enet_socketset_select(ENetSocket maxSocket, ENetSocketSet *readSet, ENetSocketSet *writeSet, enet_uint32 timeout) { struct timeval timeVal; timeVal.tv_sec = timeout / 1000; timeVal.tv_usec = (timeout % 1000) * 1000; return select(maxSocket + 1, readSet, writeSet, NULL, &timeVal); } int enet_socket_wait(ENetSocket socket, enet_uint32 *condition, enet_uint64 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; } /* enet_socket_wait */ #endif // _WIN32 #ifdef __cplusplus } #endif #endif // ENET_IMPLEMENTATION #endif // ENET_INCLUDE_H