diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0a26685..8098a1e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -41,6 +41,8 @@ jobs: run: cmake --build build - name: Run unit tests run: cd build ; .\Debug\enet_test.exe + - name: Run unit tests with extra peers + run: cd build ; .\Debug\enet_test_extra_peers.exe build-lin: name: Test Linux @@ -54,6 +56,8 @@ jobs: run: cmake --build build - name: Test build on Linux run: build/enet_test + - name: Test build on Linux with extra peers + run: build/enet_test_extra_peers build-mac: name: Test macOS @@ -67,6 +71,8 @@ jobs: run: cmake --build build - name: Test build on macOS run: build/enet_test + - name: Test build on macOS with extra peers + run: build/enet_test_extra_peers done: name: Notify about status diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b7cac7..aef6467 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,11 @@ project(enet C) option(ENET_STATIC "Build enet as a static library" ON) option(ENET_SHARED "Build enet as a shared library" OFF) option(ENET_TEST "Build enet tests" ON) +option(ENET_USE_MORE_PEERS "Build enet with up to 65k peers support" OFF) + +if (ENET_USE_MORE_PEERS) + add_definitions(-DENET_USE_MORE_PEERS) +endif() # ----------------------------- # 2) Static library @@ -59,18 +64,31 @@ endif() # 4) Tests (optional) # ----------------------------- if(ENET_TEST) - add_executable(enet_test test/build.c) - target_include_directories(enet_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) + add_library(enet_test_interface INTERFACE) - if(WIN32) - target_link_libraries(enet_test PUBLIC winmm ws2_32) - endif() + target_include_directories(enet_test_interface INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) if(TARGET enet_static) - target_link_libraries(enet_test PRIVATE enet_static) + target_link_libraries(enet_test_interface INTERFACE enet_static) elseif(TARGET enet_shared) - target_link_libraries(enet_test PRIVATE enet_shared) + target_link_libraries(enet_test_interface INTERFACE enet_shared) endif() + + if(WIN32) + target_link_libraries(enet_test_interface INTERFACE winmm ws2_32) + endif() + + # Default test + add_executable(enet_test test/build.c) + target_link_libraries(enet_test PRIVATE enet_test_interface) + + # Test with more peers + add_executable(enet_test_extra_peers test/build.c) + target_link_libraries(enet_test_extra_peers PRIVATE enet_test_interface) + target_compile_definitions(enet_test_extra_peers PRIVATE ENET_USE_MORE_PEERS) + endif() # ----------------------------- diff --git a/include/enet.h b/include/enet.h index 7a9afc7..0ae0ce9 100644 --- a/include/enet.h +++ b/include/enet.h @@ -305,7 +305,11 @@ extern "C" { ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE = 65536, ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1, ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255, +#ifdef ENET_USE_MORE_PEERS + ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xFFFF, +#else ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xFFF, +#endif ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT = 1024 * 1024 }; @@ -332,12 +336,22 @@ extern "C" { ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 << 7), ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 << 6), +#ifdef ENET_USE_MORE_PEERS + ENET_PROTOCOL_HEADER_FLAG_PEER_EXTRA = (1 << 13), + ENET_PROTOCOL_HEADER_FLAG_COMPRESSED = (1 << 14), + ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 << 15), + ENET_PROTOCOL_HEADER_FLAG_MASK = ENET_PROTOCOL_HEADER_FLAG_PEER_EXTRA | ENET_PROTOCOL_HEADER_FLAG_COMPRESSED | ENET_PROTOCOL_HEADER_FLAG_SENT_TIME, + + ENET_PROTOCOL_HEADER_SESSION_MASK = (3 << 11), + ENET_PROTOCOL_HEADER_SESSION_SHIFT = 11 +#else 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 +#endif } ENetProtocolFlag; #ifdef _MSC_VER @@ -354,6 +368,10 @@ extern "C" { enet_uint16 sentTime; } ENET_PACKED ENetProtocolHeader; + typedef struct _ENetProtocolHeaderMinimal { + enet_uint16 peerID; + } ENET_PACKED ENetProtocolHeaderMinimal; + typedef struct _ENetProtocolCommandHeader { enet_uint8 command; enet_uint8 channelID; @@ -2483,7 +2501,7 @@ extern "C" { enet_uint16 peerID, flags; enet_uint8 sessionID; - if (host->receivedDataLength < (size_t) &((ENetProtocolHeader *) 0)->sentTime) { + if (host->receivedDataLength < sizeof(ENetProtocolHeaderMinimal)) { return 0; } @@ -2494,8 +2512,27 @@ extern "C" { 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); + headerSize = (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME ? sizeof(ENetProtocolHeader) : sizeof(ENetProtocolHeaderMinimal)); + +#ifdef ENET_USE_MORE_PEERS + if (flags & ENET_PROTOCOL_HEADER_FLAG_PEER_EXTRA) { + if (host->receivedDataLength < headerSize + sizeof(enet_uint8)) { + return 0; + } + + enet_uint8 * headerExtraPeerID = (enet_uint8 *) & host -> receivedData [headerSize]; + enet_uint8 peerIDExtra = *headerExtraPeerID; + peerID = (peerID & 0x07FF) | ((enet_uint16)peerIDExtra << 11); + + headerSize += sizeof (enet_uint8); + } +#endif + if (host->checksum != NULL) { + if (host->receivedDataLength < headerSize + sizeof(enet_uint32)) { + return 0; + } + headerSize += sizeof(enet_uint32); } @@ -3081,7 +3118,13 @@ extern "C" { } /* 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)]; + enet_uint8 headerData[ + sizeof(ENetProtocolHeader) +#ifdef ENET_USE_MORE_PEERS + + sizeof(enet_uint8) // additional peer id byte +#endif + + sizeof(enet_uint32) + ]; ENetProtocolHeader *header = (ENetProtocolHeader *) headerData; ENetPeer *currentPeer; int sentLength; @@ -3171,7 +3214,7 @@ extern "C" { 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; + host->buffers[0].dataLength = sizeof(ENetProtocolHeaderMinimal); } shouldCompress = 0; @@ -3190,7 +3233,32 @@ extern "C" { if (currentPeer->outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID) { host->headerFlags |= currentPeer->outgoingSessionID << ENET_PROTOCOL_HEADER_SESSION_SHIFT; } + +#ifdef ENET_USE_MORE_PEERS + { + enet_uint16 basePeerID = (enet_uint16)(currentPeer->outgoingPeerID & 0x07FF); + enet_uint16 flagsAndSession = (enet_uint16)(host->headerFlags & 0xF800); /* top bits only */ + + if (currentPeer->outgoingPeerID > 0x07FF) + { + flagsAndSession |= ENET_PROTOCOL_HEADER_FLAG_PEER_EXTRA; + header->peerID = ENET_HOST_TO_NET_16(basePeerID | flagsAndSession); + { + enet_uint8 overflowByte = (enet_uint8)((currentPeer->outgoingPeerID >> 11) & 0xFF); + enet_uint8 *extraPeerIDByte = &headerData[host->buffers[0].dataLength]; + *extraPeerIDByte = overflowByte; + host->buffers[0].dataLength += sizeof(enet_uint8); + } + } + else + { + header->peerID = ENET_HOST_TO_NET_16(basePeerID | flagsAndSession); + } + } +#else header->peerID = ENET_HOST_TO_NET_16(currentPeer->outgoingPeerID | host->headerFlags); +#endif + 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; diff --git a/test/build.c b/test/build.c index a7e4b92..c38bcd3 100644 --- a/test/build.c +++ b/test/build.c @@ -9,14 +9,22 @@ typedef struct { ENetPeer *peer; } Client; +#ifdef ENET_USE_MORE_PEERS +#define MAX_CLIENTS 5000 +#else +#define MAX_CLIENTS 32 +#endif + +unsigned long long counter = 0; + void host_server(ENetHost *server) { ENetEvent event; while (enet_host_service(server, &event, 2) > 0) { switch (event.type) { case ENET_EVENT_TYPE_CONNECT: - printf("A new client connected from ::1:%u.\n", event.peer->address.port); + printf("A new peer with ID %u connected from ::1:%u.\n", event.peer->incomingPeerID , event.peer->address.port); /* Store any relevant client information here. */ - event.peer->data = "Client information"; + event.peer->data = (void*)(counter++); break; case ENET_EVENT_TYPE_RECEIVE: printf("A packet of length %zu containing %s was received from %s on channel %u.\n", @@ -30,7 +38,7 @@ void host_server(ENetHost *server) { break; case ENET_EVENT_TYPE_DISCONNECT: - printf ("%s disconnected.\n", (char *)event.peer->data); + printf ("Peer with ID %u disconnected.\n", event.peer->incomingPeerID); /* Reset the peer's client information. */ event.peer->data = NULL; break; @@ -51,8 +59,6 @@ int main() { return 1; } - #define MAX_CLIENTS 32 - int i = 0; ENetHost *server; Client clients[MAX_CLIENTS]; @@ -81,6 +87,8 @@ int main() { } } + printf("running server...\n"); + // program will make N iterations, and then exit static int counter = 1000; @@ -95,9 +103,12 @@ int main() { counter--; } while (counter > 0); + printf("stopping clients...\n"); + for (i = 0; i < MAX_CLIENTS; ++i) { enet_peer_disconnect_now(clients[i].peer, 0); enet_host_destroy(clients[i].host); + host_server(server); } host_server(server);