diff --git a/CMakeLists.txt b/CMakeLists.txt index d3d4aa8d..f4548cfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,7 @@ set(INCLUDE_FILES ${INCLUDE_FILES_PREFIX}/callbacks.h ${INCLUDE_FILES_PREFIX}/enet.h ${INCLUDE_FILES_PREFIX}/list.h + ${INCLUDE_FILES_PREFIX}/map.h ${INCLUDE_FILES_PREFIX}/protocol.h ${INCLUDE_FILES_PREFIX}/time.h ${INCLUDE_FILES_PREFIX}/types.h @@ -75,6 +76,7 @@ set(SOURCE_FILES compress.c host.c list.c + map.c packet.c peer.c protocol.c diff --git a/host.c b/host.c index 3b2180f7..e31d86da 100644 --- a/host.c +++ b/host.c @@ -130,6 +130,7 @@ enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelL enet_peer_reset (currentPeer); } + enet_map_init (& host -> peerIDMapping, host -> peerCount); return host; } @@ -158,6 +159,7 @@ enet_host_destroy (ENetHost * host) enet_free (host -> peers); enet_free (host); + enet_map_free (& host -> peerIDMapping); } /** Initiates a connection to a foreign host. @@ -182,12 +184,14 @@ enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelC if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; + int peerIndex = 0; for (currentPeer = host -> peers; currentPeer < & host -> peers [host -> peerCount]; ++ currentPeer) { if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED) break; + peerIndex++; } if (currentPeer >= & host -> peers [host -> peerCount]) @@ -230,9 +234,16 @@ enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelC memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows)); } + // Generate a random peer ID and save it to the host's mapping so we can look it up later. + enet_uint16 randomValue = (enet_uint16) enet_host_random_seed (); + randomValue &= ~ (ENET_PROTOCOL_HEADER_FLAG_MASK | ENET_PROTOCOL_HEADER_SESSION_MASK); + if(enet_map_insert (& host -> peerIDMapping, randomValue, peerIndex) == 0) + return NULL; + 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); + currentPeer -> incomingPeerID = randomValue; + command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (randomValue); command.connect.incomingSessionID = currentPeer -> incomingSessionID; command.connect.outgoingSessionID = currentPeer -> outgoingSessionID; command.connect.mtu = ENET_HOST_TO_NET_32 (currentPeer -> mtu); diff --git a/include/enet/enet.h b/include/enet/enet.h index fc45cbd0..51f52f63 100644 --- a/include/enet/enet.h +++ b/include/enet/enet.h @@ -21,6 +21,7 @@ extern "C" #include "enet/types.h" #include "enet/protocol.h" #include "enet/list.h" +#include "enet/map.h" #include "enet/callbacks.h" #define ENET_VERSION_MAJOR 1 @@ -371,6 +372,7 @@ typedef struct _ENetHost enet_uint32 randomSeed; int recalculateBandwidthLimits; ENetPeer * peers; /**< array of peers allocated for this host */ + ENetMap peerIDMapping; /**< keep track of what peer ID belongs to what peer */ size_t peerCount; /**< number of peers allocated for this host */ size_t channelLimit; /**< maximum number of channels allowed for connected peers */ enet_uint32 serviceTime; diff --git a/include/enet/map.h b/include/enet/map.h new file mode 100644 index 00000000..f2db9a69 --- /dev/null +++ b/include/enet/map.h @@ -0,0 +1,30 @@ +/** + @file map.h + @brief ENet integer keyed hashmap implementation +*/ +#ifndef __ENET_MAP_H__ +#define __ENET_MAP_H__ + +#include + +typedef struct _ENetMapDataItem { + enet_uint16 value; + enet_uint16 key; +} ENetMapDataItem; + +typedef struct _ENetMap +{ + ENetMapDataItem * hashArray; + enet_uint16 size; +} ENetMap; + +// Init and delete map structs +extern void enet_map_init(ENetMap *map, enet_uint16 size); +extern void enet_map_free(ENetMap *map); + +// Get, Insert, and Delete items into a map +extern enet_uint16 enet_map_get(ENetMap *map, enet_uint16 key); +extern int enet_map_insert(ENetMap *map, enet_uint16 key, enet_uint16 value); +extern void enet_map_delete(ENetMap *map, enet_uint16 key); + +#endif /* __ENET_MAP_H__ */ diff --git a/map.c b/map.c new file mode 100644 index 00000000..66504dcd --- /dev/null +++ b/map.c @@ -0,0 +1,77 @@ +/** + @file map.c + @brief ENet integer keyed hashmap implementation +*/ +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +enet_uint16 hashCode(ENetMap *map, enet_uint16 key) { + return key % map -> size; +} + +void +enet_map_init(ENetMap *map, enet_uint16 size) { + map -> size = size; + map -> hashArray = (ENetMapDataItem *) enet_malloc( size * (sizeof(ENetMapDataItem))); + for (enet_uint16 i = 0; i < size; i++) { + map -> hashArray[i].key = -1; + map -> hashArray[i].value = -1; + } +} + +void +enet_map_free(ENetMap *map) { + enet_free(map -> hashArray); +} + + +enet_uint16 +enet_map_get(ENetMap *map, enet_uint16 key) { + enet_uint16 hashIndex = hashCode(map, key); + + // Worst case scenario O(n) lookup. But the vast majority of lookups will + // hit on the first key in practice. + for (enet_uint16 i = 0; i < map -> size; i++) { + if (map -> hashArray[hashIndex].key == key) { + return map -> hashArray[hashIndex].value; + } + ++hashIndex; + hashIndex %= map -> size; + } + return -1; +} + +int +enet_map_insert(ENetMap *map, enet_uint16 key, enet_uint16 value) { + int hashIndex = hashCode(map, key); + + // Start at hashIndex, and loop in array until we find an empty cell + for (enet_uint16 i = 0; i < map -> size; i++) { + if (map -> hashArray[hashIndex].key == (enet_uint16)(-1)) { + map -> hashArray[hashIndex].key = key; + map -> hashArray[hashIndex].value = value; + return 1; + } + + ++hashIndex; + hashIndex %= map -> size; + } + return 0; +} + +void +enet_map_delete(ENetMap *map, enet_uint16 key) { + enet_uint16 hashIndex = hashCode(map, key); + + // Worst case scenario O(n) lookup. But the vast majority of lookups will + // hit on the first key in practice. + for (enet_uint16 i = 0; i < map -> size; i++) { + if (map -> hashArray[hashIndex].key != key) { + map -> hashArray[hashIndex].key = -1; + map -> hashArray[hashIndex].value = -1; + return; + } + ++hashIndex; + hashIndex %= map -> size; + } +} diff --git a/peer.c b/peer.c index 9370ef4b..7bff192f 100644 --- a/peer.c +++ b/peer.c @@ -362,6 +362,7 @@ enet_peer_on_disconnect (ENetPeer * peer) -- peer -> host -> bandwidthLimitedPeers; -- peer -> host -> connectedPeers; + enet_map_delete (& peer -> host -> peerIDMapping, peer -> outgoingPeerID); } } @@ -514,6 +515,7 @@ enet_peer_disconnect_now (ENetPeer * peer, enet_uint32 data) enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); + enet_map_delete (& peer -> host -> peerIDMapping, peer -> outgoingPeerID); enet_host_flush (peer -> host); } diff --git a/protocol.c b/protocol.c index 9d654f1d..747628e1 100644 --- a/protocol.c +++ b/protocol.c @@ -291,6 +291,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet size_t channelCount, duplicatePeers = 0; ENetPeer * currentPeer, * peer = NULL; ENetProtocol verifyCommand; + int peerIndex, i = 0; channelCount = ENET_NET_TO_HOST_32 (command -> connect.channelCount); @@ -305,7 +306,10 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED) { if (peer == NULL) + { peer = currentPeer; + peerIndex = i; + } } else if (currentPeer -> state != ENET_PEER_STATE_CONNECTING && @@ -317,6 +321,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet ++ duplicatePeers; } + ++ i; } if (peer == NULL || duplicatePeers >= host -> duplicatePeers) @@ -412,9 +417,16 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + // Generate a random peer ID and save it to the host's mapping so we can look it up later. + enet_uint16 randomValue = (enet_uint16) enet_host_random_seed (); + randomValue &= ~ (ENET_PROTOCOL_HEADER_FLAG_MASK | ENET_PROTOCOL_HEADER_SESSION_MASK); + if(enet_map_insert (& host -> peerIDMapping, randomValue, peerIndex) == 0) + return NULL; + 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); + peer -> incomingPeerID = randomValue; + verifyCommand.verifyConnect.outgoingPeerID = ENET_HOST_TO_NET_16 (randomValue); verifyCommand.verifyConnect.incomingSessionID = incomingSessionID; verifyCommand.verifyConnect.outgoingSessionID = outgoingSessionID; verifyCommand.verifyConnect.mtu = ENET_HOST_TO_NET_32 (peer -> mtu); @@ -1020,14 +1032,15 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) if (host -> checksum != NULL) headerSize += sizeof (enet_uint32); - if (peerID == ENET_PROTOCOL_MAXIMUM_PEER_ID) - peer = NULL; - else - if (peerID >= host -> peerCount) - return 0; + // Do a lookup of the peer ID. If we have that peer, then use it + enet_uint16 peerIndex = enet_map_get (& host -> peerIDMapping, peerID); + if (peerIndex == (enet_uint16)(-1)) + { + peer = NULL; + } else { - peer = & host -> peers [peerID]; + peer = & host -> peers [peerIndex]; if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE || @@ -1572,7 +1585,8 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch ENetPeer * currentPeer; int sentLength; size_t shouldCompress = 0; - + int peerIndex = 0; + host -> continueSending = 1; while (host -> continueSending) @@ -1668,7 +1682,7 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch 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); + header -> peerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID | host -> headerFlags); if (host -> checksum != NULL) { enet_uint32 * checksum = (enet_uint32 *) & headerData [host -> buffers -> dataLength]; @@ -1695,6 +1709,7 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch host -> totalSentData += sentLength; host -> totalSentPackets ++; + ++ peerIndex; } return 0;